From 924d18ded4736d0847673be79e84311a01f689f3 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Thu, 4 Jul 2024 18:47:37 +0200 Subject: [PATCH 01/22] Push initial config and genesis --- misc/deployments/test4.gno.land/config.toml | 247 +++++++++++++++++++ misc/deployments/test4.gno.land/genesis.json | 19 ++ 2 files changed, 266 insertions(+) create mode 100644 misc/deployments/test4.gno.land/config.toml create mode 100644 misc/deployments/test4.gno.land/genesis.json diff --git a/misc/deployments/test4.gno.land/config.toml b/misc/deployments/test4.gno.land/config.toml new file mode 100644 index 00000000000..156e92e42ad --- /dev/null +++ b/misc/deployments/test4.gno.land/config.toml @@ -0,0 +1,247 @@ + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# Database backend: goleveldb | boltdb +# * goleveldb (github.com/syndtr/goleveldb - most popular implementation) +# - pure go +# - stable +#* boltdb (uses etcd's fork of bolt - go.etcd.io/bbolt) +# - EXPERIMENTAL +# - may be faster is some use-cases (random reads - indexer) +# - use boltdb build tag (go build -tags boltdb) +db_backend = "goleveldb" + +# Database directory +db_dir = "db" + +# If this node is many blocks behind the tip of the chain, FastSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +fast_sync = true + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter_peers = false +home = "" + +# A custom human readable name for this node +moniker = "artemis.local" + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node_key_file = "secrets/node_key.json" + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +priv_validator_key_file = "secrets/priv_validator_key.json" + +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "" + +# Path to the JSON file containing the last sign state of a validator +priv_validator_state_file = "secrets/priv_validator_state.json" + +# TCP or UNIX socket address for the profiling server to listen on +prof_laddr = "" + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the Tendermint binary +proxy_app = "tcp://127.0.0.1:26658" + +##### consensus configuration options ##### +[consensus] + + # EmptyBlocks mode and possible interval between empty blocks + create_empty_blocks = true + create_empty_blocks_interval = "0s" + home = "" + + # Reactor sleep duration parameters + peer_gossip_sleep_duration = "100ms" + peer_query_maj23_sleep_duration = "2s" + + # Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) + skip_timeout_commit = false + timeout_commit = "2s" + timeout_precommit = "1s" + timeout_precommit_delta = "500ms" + timeout_prevote = "1s" + timeout_prevote_delta = "500ms" + timeout_propose = "3s" + timeout_propose_delta = "500ms" + wal_file = "wal/cs.wal/wal" + +##### mempool configuration options ##### +[mempool] + broadcast = true + + # Size of the cache (used to filter transactions we saw earlier) in transactions + cache_size = 10000 + home = "" + + # Limit the total size of all txs in the mempool. + # This only accounts for raw transactions (e.g. given 1MB transactions and + # max_txs_bytes=5MB, mempool will only accept 5 transactions). + max_pending_txs_bytes = 1073741824 + recheck = true + + # Maximum number of transactions in the mempool + size = 5000 + wal_dir = "" + +##### peer to peer configuration options ##### +[p2p] + + # Toggle to disable guard against peers connecting from the same ip. + allow_duplicate_ip = false + dial_timeout = "3s" + + # Address to advertise to peers for them to dial + # If empty, will use the same port as the laddr, + # and will introspect on the listener or use UPnP + # to figure out the address. + external_address = "" + + # Time to wait before flushing messages out on the connection + flush_throttle_timeout = "100ms" + + # Peer connection configuration. + handshake_timeout = "20s" + home = "" + + # Address to listen for incoming connections + laddr = "tcp://0.0.0.0:26656" + + # Maximum number of inbound peers + max_num_inbound_peers = 40 + + # Maximum number of outbound peers to connect to, excluding persistent peers + max_num_outbound_peers = 10 + + # Maximum size of a message packet payload, in bytes + max_packet_msg_payload_size = 1024 + + # Comma separated list of nodes to keep persistent connections to + persistent_peers = "" + + # Set true to enable the peer-exchange reactor + pex = true + + # Comma separated list of peer IDs to keep private (will not be gossiped to other peers) + private_peer_ids = "" + + # Rate at which packets can be received, in bytes/second + recv_rate = 5120000 + + # Seed mode, in which node constantly crawls the network and looks for + # peers. If another node asks it for addresses, it responds and disconnects. + # + # Does not work if the peer-exchange reactor is disabled. + seed_mode = false + + # Comma separated list of seed nodes to connect to + seeds = "" + + # Rate at which packets can be sent, in bytes/second + send_rate = 5120000 + test_dial_fail = false + test_fuzz = false + + # UPNP port forwarding + upnp = false + + [p2p.test_fuzz_config] + MaxDelay = "3s" + Mode = 0 + ProbDropConn = 0.0 + ProbDropRW = 0.2 + ProbSleep = 0.0 + +##### rpc server configuration options ##### +[rpc] + + # A list of non simple headers the client is allowed to use with cross-domain requests + cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"] + + # A list of methods the client is allowed to use with cross-domain requests + cors_allowed_methods = ["HEAD", "GET", "POST", "OPTIONS"] + + # A list of origins a cross-domain request can be executed from + # Default value '[]' disables cors support + # Use '["*"]' to allow any origin + cors_allowed_origins = ["*"] + + # TCP or UNIX socket address for the gRPC server to listen on + # NOTE: This server only supports /broadcast_tx_commit + grpc_laddr = "" + + # Maximum number of simultaneous connections. + # Does not include RPC (HTTP&WebSocket) connections. See max_open_connections + # If you want to accept a larger number than the default, make sure + # you increase your OS limits. + # 0 - unlimited. + # Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} + # 1024 - 40 - 10 - 50 = 924 = ~900 + grpc_max_open_connections = 900 + home = "" + + # TCP or UNIX socket address for the RPC server to listen on + laddr = "tcp://127.0.0.1:26657" + + # Maximum size of request body, in bytes + max_body_bytes = 1000000 + + # Maximum size of request header, in bytes + max_header_bytes = 1048576 + + # Maximum number of simultaneous connections (including WebSocket). + # Does not include gRPC connections. See grpc_max_open_connections + # If you want to accept a larger number than the default, make sure + # you increase your OS limits. + # 0 - unlimited. + # Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} + # 1024 - 40 - 10 - 50 = 924 = ~900 + max_open_connections = 900 + + # How long to wait for a tx to be committed during /broadcast_tx_commit. + # WARNING: Using a value larger than 10s will result in increasing the + # global HTTP write timeout, which applies to all connections and endpoints. + # See https://github.com/tendermint/classic/issues/3435 + timeout_broadcast_tx_commit = "10s" + + # The path to a file containing certificate that is used to create the HTTPS server. + # Might be either absolute path or path related to tendermint's config directory. + # If the certificate is signed by a certificate authority, + # the certFile should be the concatenation of the server's certificate, any intermediates, + # and the CA's certificate. + # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. + tls_cert_file = "" + + # The path to a file containing matching private key that is used to create the HTTPS server. + # Might be either absolute path or path related to tendermint's config directory. + # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. + tls_key_file = "" + + # Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool + unsafe = false + +##### node telemetry ##### +[telemetry] + enabled = false + + # the endpoint to export metrics to, like a local OpenTelemetry collector + exporter_endpoint = "" + meter_name = "gno.land" + + # the ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service) + service_instance_id = "gno-node-1" + service_name = "gno.land" + +##### event store ##### +[tx_event_store] + + # Type of event store + event_store_type = "none" + + # Event store parameters + [tx_event_store.event_store_params] diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json new file mode 100644 index 00000000000..b91d95bec45 --- /dev/null +++ b/misc/deployments/test4.gno.land/genesis.json @@ -0,0 +1,19 @@ +{ + "genesis_time": "2024-07-10T06:00:00Z", + "chain_id": "test4", + "consensus_params": { + "Block": { + "MaxTxBytes": "1000000", + "MaxDataBytes": "2000000", + "MaxBlockBytes": "0", + "MaxGas": "100000000", + "TimeIotaMS": "100" + }, + "Validator": { + "PubKeyTypeURLs": [ + "/tm.PubKeyEd25519" + ] + } + }, + "app_hash": null +} \ No newline at end of file From 256e9775750156ed57feba3c4ad00c9880532b1f Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Thu, 4 Jul 2024 19:05:19 +0200 Subject: [PATCH 02/22] Add README --- misc/deployments/test4.gno.land/README.md | 35 +++++++++++++++++++++ misc/deployments/test4.gno.land/config.toml | 16 +++++----- 2 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 misc/deployments/test4.gno.land/README.md diff --git a/misc/deployments/test4.gno.land/README.md b/misc/deployments/test4.gno.land/README.md new file mode 100644 index 00000000000..d9552f818c9 --- /dev/null +++ b/misc/deployments/test4.gno.land/README.md @@ -0,0 +1,35 @@ +## Overview + +This deployment folder contains minimal information needed to launch a full test4.gno.land validator node. + +## `genesis.json` + +The initial `genesis.json` validator set is consisted of 3 entities (7 validators in total): + +- Gno Core - the gno core team (**4 validators**) +- Gno DevX - the gno devX team (**2 validators**) +- Onbloc - the [Onbloc](https://onbloc.xyz/) team (**1 validator**) + +Subsequent validators will be added through the governance mechanism in govdao, employing Proof of Contribution. + +The addresses premined belong to different faucet accounts, validator entities and implementation partners. + +## `config.toml` + +The `config.toml` located in this directory is a **_guideline_**, and not a definitive configuration on how +all nodes should be configured in the network. + +### Important params + +Some configuration params are required, while others are advised to be set. + +- `moniker` - the recognizable identifier of the node. +- `consensus.timeout_commit` - the timeout value after the consensus commit phase. ⚠️ **Required to be `2s`** ⚠️. +- `mempool.size` - the maximum number of txs in the mempool. **Advised to be `10000`**. +- `p2p.laddr` - the listen address for P2P traffic, **specific to every node deployment**. +- `p2p.max_num_outbound_peers` - the max number of outbound peer connections. **Advised to be `40`**. +- `p2p.persistent_peers` - the persistent peers. ⚠️ **Required to be `TODO: add p2p addresses`** ⚠️. +- `rpc.laddr` - the JSON-RPC listen address, **specific to every node deployment**. +- `telemetry.enabled` - flag indicating if telemetry should be turned on. **Advised to be `true`**. +- `telemetry.exporter_endpoint` - endpoint for the otel exported. ⚠️ **Required if `telemetry.enabled=true`** ⚠️. +- `telemetry.service_instance_id` - unique ID of the node telemetry instance, **specific to every node deployment**. \ No newline at end of file diff --git a/misc/deployments/test4.gno.land/config.toml b/misc/deployments/test4.gno.land/config.toml index 156e92e42ad..09b50bccc3f 100644 --- a/misc/deployments/test4.gno.land/config.toml +++ b/misc/deployments/test4.gno.land/config.toml @@ -26,7 +26,7 @@ filter_peers = false home = "" # A custom human readable name for this node -moniker = "artemis.local" +moniker = "artemis.local" # Change me! # Path to the JSON file containing the private key to use for node authentication in the p2p protocol node_key_file = "secrets/node_key.json" @@ -62,7 +62,7 @@ proxy_app = "tcp://127.0.0.1:26658" # Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) skip_timeout_commit = false - timeout_commit = "2s" + timeout_commit = "2s" # Do NOT change me, leave me at 2s! timeout_precommit = "1s" timeout_precommit_delta = "500ms" timeout_prevote = "1s" @@ -86,7 +86,7 @@ proxy_app = "tcp://127.0.0.1:26658" recheck = true # Maximum number of transactions in the mempool - size = 5000 + size = 10000 # Advised value is 10000 wal_dir = "" ##### peer to peer configuration options ##### @@ -110,13 +110,13 @@ proxy_app = "tcp://127.0.0.1:26658" home = "" # Address to listen for incoming connections - laddr = "tcp://0.0.0.0:26656" + laddr = "tcp://0.0.0.0:26656" # Change me! # Maximum number of inbound peers max_num_inbound_peers = 40 # Maximum number of outbound peers to connect to, excluding persistent peers - max_num_outbound_peers = 10 + max_num_outbound_peers = 40 # Advised value is 40 # Maximum size of a message packet payload, in bytes max_packet_msg_payload_size = 1024 @@ -186,7 +186,7 @@ proxy_app = "tcp://127.0.0.1:26658" home = "" # TCP or UNIX socket address for the RPC server to listen on - laddr = "tcp://127.0.0.1:26657" + laddr = "tcp://127.0.0.1:26657" # Change me! # Maximum size of request body, in bytes max_body_bytes = 1000000 @@ -227,10 +227,10 @@ proxy_app = "tcp://127.0.0.1:26658" ##### node telemetry ##### [telemetry] - enabled = false + enabled = false # Advised to be `true` # the endpoint to export metrics to, like a local OpenTelemetry collector - exporter_endpoint = "" + exporter_endpoint = "" # Change me! meter_name = "gno.land" # the ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service) From b44ce5380c8890004d3e3453ac24d67d5ddd6dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20=C5=BDivkovi=C4=87?= Date: Fri, 5 Jul 2024 18:00:21 +0200 Subject: [PATCH 03/22] Update misc/deployments/test4.gno.land/README.md Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- misc/deployments/test4.gno.land/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/deployments/test4.gno.land/README.md b/misc/deployments/test4.gno.land/README.md index d9552f818c9..ebc6620803d 100644 --- a/misc/deployments/test4.gno.land/README.md +++ b/misc/deployments/test4.gno.land/README.md @@ -10,7 +10,7 @@ The initial `genesis.json` validator set is consisted of 3 entities (7 validator - Gno DevX - the gno devX team (**2 validators**) - Onbloc - the [Onbloc](https://onbloc.xyz/) team (**1 validator**) -Subsequent validators will be added through the governance mechanism in govdao, employing Proof of Contribution. +Subsequent validators will be added through the governance mechanism in govdao, employing a preliminary simplified version Proof of Contribution. The addresses premined belong to different faucet accounts, validator entities and implementation partners. From cddf3394c0f5e522209ffddafad73232762275a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20=C5=BDivkovi=C4=87?= Date: Fri, 5 Jul 2024 18:00:41 +0200 Subject: [PATCH 04/22] Update misc/deployments/test4.gno.land/config.toml Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- misc/deployments/test4.gno.land/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/deployments/test4.gno.land/config.toml b/misc/deployments/test4.gno.land/config.toml index 09b50bccc3f..591099ae184 100644 --- a/misc/deployments/test4.gno.land/config.toml +++ b/misc/deployments/test4.gno.land/config.toml @@ -231,7 +231,7 @@ proxy_app = "tcp://127.0.0.1:26658" # the endpoint to export metrics to, like a local OpenTelemetry collector exporter_endpoint = "" # Change me! - meter_name = "gno.land" + meter_name = "test4.gno.land" # the ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service) service_instance_id = "gno-node-1" From 867a6a772a3ecad73842c80ec1e687dd8a95e31d Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sat, 6 Jul 2024 14:31:01 +0200 Subject: [PATCH 05/22] Use 0.0.0.0 for rpc laddr --- misc/deployments/test4.gno.land/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/deployments/test4.gno.land/config.toml b/misc/deployments/test4.gno.land/config.toml index 591099ae184..61e4ed09091 100644 --- a/misc/deployments/test4.gno.land/config.toml +++ b/misc/deployments/test4.gno.land/config.toml @@ -186,7 +186,7 @@ proxy_app = "tcp://127.0.0.1:26658" home = "" # TCP or UNIX socket address for the RPC server to listen on - laddr = "tcp://127.0.0.1:26657" # Change me! + laddr = "tcp://0.0.0.0:26657" # Please use a reverse proxy! # Maximum size of request body, in bytes max_body_bytes = 1000000 From 63aa3575265e94e18b9c1729c343aa29e2aee207 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sat, 6 Jul 2024 14:33:35 +0200 Subject: [PATCH 06/22] Add comment for max_pending_txs_bytes --- misc/deployments/test4.gno.land/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/deployments/test4.gno.land/config.toml b/misc/deployments/test4.gno.land/config.toml index 61e4ed09091..13a1ae04031 100644 --- a/misc/deployments/test4.gno.land/config.toml +++ b/misc/deployments/test4.gno.land/config.toml @@ -82,7 +82,7 @@ proxy_app = "tcp://127.0.0.1:26658" # Limit the total size of all txs in the mempool. # This only accounts for raw transactions (e.g. given 1MB transactions and # max_txs_bytes=5MB, mempool will only accept 5 transactions). - max_pending_txs_bytes = 1073741824 + max_pending_txs_bytes = 1073741824 # ~1GB recheck = true # Maximum number of transactions in the mempool From 42781fc0dd7a55dd6484bc071499b7a18b3ffc59 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sat, 6 Jul 2024 14:34:18 +0200 Subject: [PATCH 07/22] Enable telemetry --- misc/deployments/test4.gno.land/config.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/deployments/test4.gno.land/config.toml b/misc/deployments/test4.gno.land/config.toml index 13a1ae04031..0722ba5b435 100644 --- a/misc/deployments/test4.gno.land/config.toml +++ b/misc/deployments/test4.gno.land/config.toml @@ -227,10 +227,10 @@ proxy_app = "tcp://127.0.0.1:26658" ##### node telemetry ##### [telemetry] - enabled = false # Advised to be `true` + enabled = true # Advised to be `true` # the endpoint to export metrics to, like a local OpenTelemetry collector - exporter_endpoint = "" # Change me! + exporter_endpoint = "" # Change me to the OTEL endpoint! meter_name = "test4.gno.land" # the ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service) From 5aeedacf3b6810753464c26f2436121848f6fa77 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sat, 6 Jul 2024 14:35:33 +0200 Subject: [PATCH 08/22] Add sentry comment --- misc/deployments/test4.gno.land/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/deployments/test4.gno.land/config.toml b/misc/deployments/test4.gno.land/config.toml index 0722ba5b435..8509d5415ac 100644 --- a/misc/deployments/test4.gno.land/config.toml +++ b/misc/deployments/test4.gno.land/config.toml @@ -125,7 +125,7 @@ proxy_app = "tcp://127.0.0.1:26658" persistent_peers = "" # Set true to enable the peer-exchange reactor - pex = true + pex = false # Should be `false` if using a sentry node. Otherwise `true`! # Comma separated list of peer IDs to keep private (will not be gossiped to other peers) private_peer_ids = "" From 2f0260619981fb9acba0b11120806844e5efa58d Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sat, 6 Jul 2024 14:41:20 +0200 Subject: [PATCH 09/22] Update README --- misc/deployments/test4.gno.land/README.md | 13 +++++++++---- misc/deployments/test4.gno.land/config.toml | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/misc/deployments/test4.gno.land/README.md b/misc/deployments/test4.gno.land/README.md index ebc6620803d..9b84488c7dc 100644 --- a/misc/deployments/test4.gno.land/README.md +++ b/misc/deployments/test4.gno.land/README.md @@ -10,7 +10,8 @@ The initial `genesis.json` validator set is consisted of 3 entities (7 validator - Gno DevX - the gno devX team (**2 validators**) - Onbloc - the [Onbloc](https://onbloc.xyz/) team (**1 validator**) -Subsequent validators will be added through the governance mechanism in govdao, employing a preliminary simplified version Proof of Contribution. +Subsequent validators will be added through the governance mechanism in govdao, employing a preliminary simplified +version Proof of Contribution. The addresses premined belong to different faucet accounts, validator entities and implementation partners. @@ -26,10 +27,14 @@ Some configuration params are required, while others are advised to be set. - `moniker` - the recognizable identifier of the node. - `consensus.timeout_commit` - the timeout value after the consensus commit phase. ⚠️ **Required to be `2s`** ⚠️. - `mempool.size` - the maximum number of txs in the mempool. **Advised to be `10000`**. -- `p2p.laddr` - the listen address for P2P traffic, **specific to every node deployment**. +- `p2p.laddr` - the listen address for P2P traffic, **specific to every node deployment**. It is advised to use a + reverse-proxy, and keep this value at `tcp://0.0.0.0:`. - `p2p.max_num_outbound_peers` - the max number of outbound peer connections. **Advised to be `40`**. -- `p2p.persistent_peers` - the persistent peers. ⚠️ **Required to be `TODO: add p2p addresses`** ⚠️. +- `p2p.persistent_peers` - the persistent peers. ⚠️ **Required to be `TODO: add p2p addresses`** ⚠️. +- `p2p.pex` - if using a sentry node architecture, should be `false`. **If not, please set to `true`**. +- `p2p.external_address` - the advertised peer dial address. If empty, will use the same port as the `p2p.laddr`. This + value should be **changed to `{{ your_ip_address }}:26656`** - `rpc.laddr` - the JSON-RPC listen address, **specific to every node deployment**. - `telemetry.enabled` - flag indicating if telemetry should be turned on. **Advised to be `true`**. -- `telemetry.exporter_endpoint` - endpoint for the otel exported. ⚠️ **Required if `telemetry.enabled=true`** ⚠️. +- `telemetry.exporter_endpoint` - endpoint for the otel exported. ⚠️ **Required if `telemetry.enabled=true`** ⚠️. - `telemetry.service_instance_id` - unique ID of the node telemetry instance, **specific to every node deployment**. \ No newline at end of file diff --git a/misc/deployments/test4.gno.land/config.toml b/misc/deployments/test4.gno.land/config.toml index 8509d5415ac..c29152a7189 100644 --- a/misc/deployments/test4.gno.land/config.toml +++ b/misc/deployments/test4.gno.land/config.toml @@ -100,7 +100,7 @@ proxy_app = "tcp://127.0.0.1:26658" # If empty, will use the same port as the laddr, # and will introspect on the listener or use UPnP # to figure out the address. - external_address = "" + external_address = "" # Change me! # Time to wait before flushing messages out on the connection flush_throttle_timeout = "100ms" From 41cecf98cc095160889cf017432b1b48817819aa Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sat, 6 Jul 2024 15:08:58 +0200 Subject: [PATCH 10/22] Update genesis.json --- misc/deployments/test4.gno.land/genesis.json | 21 +++++++++++++- .../test4.gno.land/genesis_balances.txt | 29 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 misc/deployments/test4.gno.land/genesis_balances.txt diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index b91d95bec45..30899d490f4 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -15,5 +15,24 @@ ] } }, - "app_hash": null + "app_hash": null, + "app_state": { + "@type": "/gno.GenesisState", + "balances": [ + "g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot", + "g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot", + "g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot", + "g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot", + "g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot", + "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", + "g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot", + "g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot", + "g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot", + "g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot", + "g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot", + "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", + "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot" + ], + "txs": null + } } \ No newline at end of file diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt new file mode 100644 index 00000000000..59d366fd99d --- /dev/null +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -0,0 +1,29 @@ +# Faucet Accounts + +g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot +g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot +g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot +g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot +g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot +g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot +g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot +g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot +g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot +g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot + +# Core Team + +# TODO add + +# DevX Team + +# TODO add + +# Onbloc + +g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot +g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot + +# Berty + +g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot \ No newline at end of file From 0ceb1c038800c1aca4cc22a76f4287523627aa0f Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sat, 6 Jul 2024 15:37:00 +0200 Subject: [PATCH 11/22] Add the teams into the genesis.json --- misc/deployments/test4.gno.land/genesis.json | 34 +++++++++--- .../test4.gno.land/genesis_balances.txt | 52 ++++++++++++++----- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index 30899d490f4..a08a86b283b 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -19,19 +19,39 @@ "app_state": { "@type": "/gno.GenesisState", "balances": [ - "g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot", - "g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot", - "g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot", - "g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot", + "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", + "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", + "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", + "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", + "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", + "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", "g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot", "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", + "g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot", + "g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot", + "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", + "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", + "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", "g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot", + "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", + "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", + "g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot", + "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", + "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", "g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot", + "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", + "g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot", + "g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot", + "g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot", + "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", "g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot", + "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", + "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", + "g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot", + "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", "g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot", - "g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot", - "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", - "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot" + "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", + "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot" ], "txs": null } diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index 59d366fd99d..66fda61ed10 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -1,28 +1,52 @@ # Faucet Accounts -g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot -g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot -g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot -g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot -g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot -g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot -g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot -g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot -g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot -g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot +g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot # Faucet #0 +g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot # Faucet #1 +g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot # Faucet #2 +g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot # Faucet #3 +g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot # Faucet #4 +g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot # Faucet #5 +g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot # Faucet #6 +g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot # Faucet #7 +g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot # Faucet #8 +g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot # Faucet #9 # Core Team -# TODO add +g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot # Jae +g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot # Manfred +g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot # Milos +g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot # Nemanja +g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot # Petar +g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot # Marc +g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot # Antonio +g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot # Guilhem +g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot # Ray +g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot # Maxwell +g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot # Dylan +g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot # Morgan +# XXX=9000000000000000000ugnot # Kristov # TODO add +g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot # Sergio + +# DevRel + +g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot # Michelle +g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot # Leon # DevX Team -# TODO add +# XXX=9000000000000000000ugnot # Ilker # TODO add +g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot # Jerónimo +g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot # Denis +# XXX=9000000000000000000ugnot # Lucio # TODO add +g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot # Danny +g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot # Salvo +g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot # Alexis # Onbloc -g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot -g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot +g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot # GnoSwap +g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot # Adena # Berty From cf39e5a1aacba1a5a619994d55094d14fdf7a109 Mon Sep 17 00:00:00 2001 From: Salvatore Mazzarino Date: Sun, 7 Jul 2024 12:31:26 +0200 Subject: [PATCH 12/22] feat: add devx balances and validators (#2528) This PR adds: - Iker Gno key address - DevX Faucets addresses - DevX Validators
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--- misc/deployments/test4.gno.land/genesis.json | 27 +++++++++++++++++-- .../test4.gno.land/genesis_balances.txt | 6 +++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index a08a86b283b..9b1699834a7 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -15,6 +15,26 @@ ] } }, + "validators": [ + { + "address": "g1mxguhd5zacar64txhfm0v7hhtph5wur5hx86vs", + "pub_key": { + "@type": "/tm.PubKeyEd25519", + "value": "C0l3PwIhnCmIG3YiAa9jf/uuvZj1ob7lolasnEhDgBE=" + }, + "power": "", + "name": "devx-val-1" + }, + { + "address": "g1t9ctfa468hn6czff8kazw08crazehcxaqa2uaa", + "pub_key": { + "@type": "/tm.PubKeyEd25519", + "value": "wBqj3F9RgIJ12UqGq5okzHfVbbd6H5AF5y4as8Ovx00=" + }, + "power": "", + "name": "devx-val-2" + } + ], "app_hash": null, "app_state": { "@type": "/gno.GenesisState", @@ -51,8 +71,11 @@ "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", "g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot", "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", - "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot" + "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", + "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot", + "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", + "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot" ], "txs": null } -} \ No newline at end of file +} diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index 66fda61ed10..06fc353cb65 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -10,6 +10,8 @@ g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot # Faucet #6 g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot # Faucet #7 g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot # Faucet #8 g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot # Faucet #9 +g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot # Faucet #10 +g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot # Faucet #11 # Core Team @@ -35,7 +37,7 @@ g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot # Leon # DevX Team -# XXX=9000000000000000000ugnot # Ilker # TODO add +g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot # Ilker g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot # Jerónimo g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot # Denis # XXX=9000000000000000000ugnot # Lucio # TODO add @@ -50,4 +52,4 @@ g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot # Adena # Berty -g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot \ No newline at end of file +g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot From 26f85c1856ef7d299e73ffbcd32c1da2df5299a3 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Sun, 7 Jul 2024 13:21:51 +0200 Subject: [PATCH 13/22] Add core validators --- misc/deployments/test4.gno.land/genesis.json | 98 +++++++++++++------ .../test4.gno.land/genesis_balances.txt | 5 +- 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index 9b1699834a7..6b11b54e6fc 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -16,13 +16,49 @@ } }, "validators": [ + { + "address": "g19v2h4pn6lrf8pwvhn8h0anek0cpt2tmhye4vkv", + "pub_key": { + "@type": "/tm.PubKeyEd25519", + "value": "b0bw/4GEumi09c6uhTutrjXoV7lUAxGZQqcT+MbaFBQ=" + }, + "power": "1", + "name": "core-val-1" + }, + { + "address": "g13us7swtc9hq550y9v4z6vcarak9vf8nqdvcqj4", + "pub_key": { + "@type": "/tm.PubKeyEd25519", + "value": "6FXDIqlSEl5pbPMMTLSa12XC99lWGQIRVm82bWTQibo=" + }, + "power": "1", + "name": "core-val-2" + }, + { + "address": "g1ectm6algkfw3qnjmjvx7hacmh358t36ggj5lqv", + "pub_key": { + "@type": "/tm.PubKeyEd25519", + "value": "gqFJlETPoCVCnRnCVuRjpY47Yj2kxm5R2ukPFHtlLr4=" + }, + "power": "1", + "name": "core-val-3" + }, + { + "address": "g1tcxls3ylnrwrq95j33xpyuct4l370ra7jca4kj", + "pub_key": { + "@type": "/tm.PubKeyEd25519", + "value": "n+j30mkr4dHWZlrq2MMrbR1YcIDDf3UiyEDF2oSFp3M=" + }, + "power": "1", + "name": "core-val-4" + }, { "address": "g1mxguhd5zacar64txhfm0v7hhtph5wur5hx86vs", "pub_key": { "@type": "/tm.PubKeyEd25519", "value": "C0l3PwIhnCmIG3YiAa9jf/uuvZj1ob7lolasnEhDgBE=" }, - "power": "", + "power": "1", "name": "devx-val-1" }, { @@ -31,7 +67,7 @@ "@type": "/tm.PubKeyEd25519", "value": "wBqj3F9RgIJ12UqGq5okzHfVbbd6H5AF5y4as8Ovx00=" }, - "power": "", + "power": "1", "name": "devx-val-2" } ], @@ -39,43 +75,43 @@ "app_state": { "@type": "/gno.GenesisState", "balances": [ - "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", - "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", - "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", - "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", - "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", - "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", - "g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot", - "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", - "g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot", + "g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot", "g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot", - "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", - "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", + "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", - "g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot", - "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", - "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", + "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", + "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", "g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot", - "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", + "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", + "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", + "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", - "g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot", - "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", + "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", + "g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot", + "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", + "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", + "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", + "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", + "g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot", + "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", + "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", + "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", "g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot", - "g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot", + "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", + "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", "g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot", - "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", - "g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot", + "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", - "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", - "g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot", - "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", + "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", "g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot", - "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", - "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", - "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot", - "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", - "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot" + "g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot", + "g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot", + "g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot", + "g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot", + "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", + "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", + "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot" ], "txs": null } -} +} \ No newline at end of file diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index 06fc353cb65..1476784df5f 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -1,4 +1,4 @@ -# Faucet Accounts +# Faucet Accounts (Core) g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot # Faucet #0 g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot # Faucet #1 @@ -10,6 +10,9 @@ g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot # Faucet #6 g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot # Faucet #7 g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot # Faucet #8 g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot # Faucet #9 + +# Faucet Accounts (DevX) + g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot # Faucet #10 g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot # Faucet #11 From 938cc34a674380327c1ff17292ae3cf44a4b6a51 Mon Sep 17 00:00:00 2001 From: Blake <104744707+r3v4s@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:41:15 +0900 Subject: [PATCH 14/22] feat: add onbloc validator (#2533) Adds onbloc validator into test4 genesis. --- misc/deployments/test4.gno.land/genesis.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index 6b11b54e6fc..e0186b2226d 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -69,6 +69,15 @@ }, "power": "1", "name": "devx-val-2" + }, + { + "address": "g1gav33elude7prcdctpjekze7ft9l8qdjxqaj6d", + "pub_key": { + "@type": "/tm.PubKeyEd25519", + "value": "+DJIo04cO4y+144EdXCw3ED/plkOhWDJ8FP1ScF40m4=" + }, + "power": "1", + "name": "onbloc-val-1" } ], "app_hash": null, From 902acafa194e80120c23a2510804cd50a4ea7c43 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Mon, 8 Jul 2024 12:25:08 +0200 Subject: [PATCH 15/22] Add Dragos, Teritori premines --- misc/deployments/test4.gno.land/genesis.json | 57 ++++++++++--------- .../test4.gno.land/genesis_balances.txt | 9 +++ 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index e0186b2226d..1257f1f926d 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -84,42 +84,45 @@ "app_state": { "@type": "/gno.GenesisState", "balances": [ - "g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot", - "g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot", - "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", - "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", - "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", - "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", - "g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot", "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", - "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", - "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", - "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", - "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", "g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot", - "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", - "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", - "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", - "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", - "g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot", - "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", + "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", + "g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot", + "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", + "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", - "g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot", + "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", + "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", + "g16f5chytu99dmjqtekxf8qzg04vcv7dck6qny6d=9000000000000000000ugnot", + "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", + "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", - "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", + "g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot", + "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", + "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", + "g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot", + "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", + "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", + "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot", "g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot", - "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", - "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", - "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", - "g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot", "g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot", - "g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot", + "g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3=9000000000000000000ugnot", "g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot", + "g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot", + "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", + "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", + "g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot", + "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", + "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", + "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", + "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", + "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", "g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot", - "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", - "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", - "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot" + "g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot", + "g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot", + "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", + "g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot" ], "txs": null } diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index 1476784df5f..dac4caaabf6 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -56,3 +56,12 @@ g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot # Adena # Berty g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot + +# Dragos + +g16f5chytu99dmjqtekxf8qzg04vcv7dck6qny6d=9000000000000000000ugnot # Flippando faucet +g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3=9000000000000000000ugnot # ZenTasktic faucet + +# Teritori + +g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot # Teritori Core Team \ No newline at end of file From 184d24346178694315a1c6b728de4a00a186eb0c Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Mon, 8 Jul 2024 12:27:37 +0200 Subject: [PATCH 16/22] Add comments in balances file --- misc/deployments/test4.gno.land/genesis_balances.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index dac4caaabf6..0ec45e6a59c 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -45,6 +45,7 @@ g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot # Jerónimo g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot # Denis # XXX=9000000000000000000ugnot # Lucio # TODO add g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot # Danny +# XXX=9000000000000000000ugnot # Alan g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot # Salvo g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot # Alexis @@ -55,7 +56,7 @@ g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot # Adena # Berty -g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot +g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot # Berty Core Team # Dragos From 390a21796f3e36ca6267439f16b580976ce7c124 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Mon, 8 Jul 2024 14:54:15 +0200 Subject: [PATCH 17/22] Update core faucet accounts --- misc/deployments/test4.gno.land/genesis.json | 66 +++++++++---------- .../test4.gno.land/genesis_balances.txt | 20 +++--- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index 1257f1f926d..93448e17369 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -84,45 +84,45 @@ "app_state": { "@type": "/gno.GenesisState", "balances": [ - "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", - "g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot", - "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", - "g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot", - "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", - "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", - "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", - "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", - "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", - "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", - "g16f5chytu99dmjqtekxf8qzg04vcv7dck6qny6d=9000000000000000000ugnot", - "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", - "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", - "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", - "g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot", - "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", + "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", + "g1knpjkw7d5ft2830n8rnatllzq9gu85dmn20nrp=9000000000000000000ugnot", "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", - "g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot", - "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", - "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", + "g1xja7p9s3ly3tvkgks9x0n6f6yau2hnzl4x8x3d=9000000000000000000ugnot", + "g1563d3cw0gdv68le6azw2s63xm0jx9xvgpmfatq=9000000000000000000ugnot", "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot", - "g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot", - "g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot", - "g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3=9000000000000000000ugnot", - "g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot", + "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", + "g16f5chytu99dmjqtekxf8qzg04vcv7dck6qny6d=9000000000000000000ugnot", "g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot", + "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", + "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", + "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", + "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", + "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", + "g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3=9000000000000000000ugnot", + "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", - "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", - "g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot", + "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", + "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", + "g1s6ujq0r200a0tfqgnjfflz7ddetsnrwvzxxx74=9000000000000000000ugnot", + "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", + "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", + "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", + "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", + "g1a5kdc6ykfykq9p6088sclnr7e63hj8y4nnr5gn=9000000000000000000ugnot", "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", - "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", - "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", - "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", + "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", + "g13csus64lj8twrn266837l9e77dusk2zwa794qz=9000000000000000000ugnot", + "g1gadp0yq5djelxwv7h8g7wpdf7x5w3vuzwmqrne=9000000000000000000ugnot", + "g1cm7u2fqd7drwfcqtn7zsgrfcyvlkkukhhtrxxj=9000000000000000000ugnot", + "g1njagaeg7e398hze39ygfgvc4gwsh6lkz7dwnuz=9000000000000000000ugnot", + "g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot", "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", - "g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot", - "g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot", - "g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot", - "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", - "g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot" + "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", + "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", + "g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot", + "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", + "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", + "g1d7set9rsdw7ggt9elvzerep4jz80a4hu4crmzg=9000000000000000000ugnot" ], "txs": null } diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index 0ec45e6a59c..5b7c31ca725 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -1,15 +1,15 @@ # Faucet Accounts (Core) -g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5=9000000000000000000ugnot # Faucet #0 -g19qr6kdu7pyvc95jyec7f7xfq2lj2w4zp46u4sy=9000000000000000000ugnot # Faucet #1 -g1xmtdgzwvvclkxxgszyyas0ekz66kder3x8jmzk=9000000000000000000ugnot # Faucet #2 -g1pwx9cvx9cyeza8jnajsr2rxqyzsanjwd0ssx32=9000000000000000000ugnot # Faucet #3 -g1s8zdg7e7jrcjupna78wvyy32py0ghgn3jm9rwu=9000000000000000000ugnot # Faucet #4 -g13qtrg2fnphh4rw697educlcmv0ru2yk63flfad=9000000000000000000ugnot # Faucet #5 -g1dupcckverkpf5vsjk4z4xs0q6l630zjv98pv0a=9000000000000000000ugnot # Faucet #6 -g17a3rlkw58e4gnp92al9fx9039tt5w9mq9530f8=9000000000000000000ugnot # Faucet #7 -g1jv52j08qvyrjmdnv7yag9swpt9gz2rd9zg66ga=9000000000000000000ugnot # Faucet #8 -g1mk2ww330ms9yxr9pxelpg0t0emqvjjepnhmmeh=9000000000000000000ugnot # Faucet #9 +g1njagaeg7e398hze39ygfgvc4gwsh6lkz7dwnuz=9000000000000000000ugnot # Faucet #0 +g1a5kdc6ykfykq9p6088sclnr7e63hj8y4nnr5gn=9000000000000000000ugnot # Faucet #1 +g1gadp0yq5djelxwv7h8g7wpdf7x5w3vuzwmqrne=9000000000000000000ugnot # Faucet #2 +g1s6ujq0r200a0tfqgnjfflz7ddetsnrwvzxxx74=9000000000000000000ugnot # Faucet #3 +g1d7set9rsdw7ggt9elvzerep4jz80a4hu4crmzg=9000000000000000000ugnot # Faucet #4 +g1cm7u2fqd7drwfcqtn7zsgrfcyvlkkukhhtrxxj=9000000000000000000ugnot # Faucet #5 +g13csus64lj8twrn266837l9e77dusk2zwa794qz=9000000000000000000ugnot # Faucet #6 +g1knpjkw7d5ft2830n8rnatllzq9gu85dmn20nrp=9000000000000000000ugnot # Faucet #7 +g1563d3cw0gdv68le6azw2s63xm0jx9xvgpmfatq=9000000000000000000ugnot # Faucet #8 +g1xja7p9s3ly3tvkgks9x0n6f6yau2hnzl4x8x3d=9000000000000000000ugnot # Faucet #9 # Faucet Accounts (DevX) From 8da0ce04f648a2c504e9e9388c40f81497052b18 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Tue, 9 Jul 2024 20:41:05 +0200 Subject: [PATCH 18/22] Predeploy transactions --- misc/deployments/test4.gno.land/genesis.json | 4761 +++++++++++++++++- 1 file changed, 4760 insertions(+), 1 deletion(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index 93448e17369..7876f112ca3 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -124,6 +124,4765 @@ "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", "g1d7set9rsdw7ggt9elvzerep4jz80a4hu4crmzg=9000000000000000000ugnot" ], - "txs": null + "txs": [ + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "bank", + "path": "gno.land/p/demo/bank", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/bank\n" + }, + { + "name": "types.gno", + "body": "// TODO: this is an example, and needs to be fixed up and tested.\n\npackage bank\n\n// NOTE: unexposed struct for security.\ntype order struct {\n\tfrom Address\n\tto Address\n\tamount Coins\n\tprocessed bool\n}\n\n// NOTE: unexposed methods for security.\nfunc (ch *order) string() string {\n\treturn \"TODO\"\n}\n\n// Wraps the internal *order for external use.\ntype Order struct {\n\t*order\n}\n\n// XXX only exposed for demonstration. TODO unexpose, make full demo.\nfunc NewOrder(from Address, to Address, amount Coins) Order {\n\treturn Order{\n\t\torder: \u0026order{\n\t\t\tfrom: from,\n\t\t\tto: to,\n\t\t\tamount: amount,\n\t\t},\n\t}\n}\n\n// Panics if error, or already processed.\nfunc (o Order) Execute() {\n\tif o.order.processed {\n\t\tpanic(\"order already processed\")\n\t}\n\to.order.processed = true\n\t// TODO implemement.\n}\n\nfunc (o Order) IsZero() bool {\n\treturn o.order == nil\n}\n\nfunc (o Order) From() Address {\n\treturn o.order.from\n}\n\nfunc (o Order) To() Address {\n\treturn o.order.to\n}\n\nfunc (o Order) Amount() Coins {\n\treturn o.order.amount\n}\n\nfunc (o Order) Processed() bool {\n\treturn o.order.processed\n}\n\n//----------------------------------------\n// Escrow\n\ntype EscrowTerms struct {\n\tPartyA Address\n\tPartyB Address\n\tAmountA Coins\n\tAmountB Coins\n}\n\ntype EscrowContract struct {\n\tEscrowTerms\n\tOrderA Order\n\tOrderB Order\n}\n\nfunc CreateEscrow(terms EscrowTerms) *EscrowContract {\n\treturn \u0026EscrowContract{\n\t\tEscrowTerms: terms,\n\t}\n}\n\nfunc (esc *EscrowContract) SetOrderA(order Order) {\n\tif !esc.OrderA.IsZero() {\n\t\tpanic(\"order-a already set\")\n\t}\n\tif esc.EscrowTerms.PartyA != order.From() {\n\t\tpanic(\"invalid order-a:from mismatch\")\n\t}\n\tif esc.EscrowTerms.PartyB != order.To() {\n\t\tpanic(\"invalid order-a:to mismatch\")\n\t}\n\tif !esc.EscrowTerms.AmountA.Equal(order.Amount()) {\n\t\tpanic(\"invalid order-a amount\")\n\t}\n\tesc.OrderA = order\n}\n\nfunc (esc *EscrowContract) SetOrderB(order Order) {\n\tif !esc.OrderB.IsZero() {\n\t\tpanic(\"order-b already set\")\n\t}\n\tif esc.EscrowTerms.PartyB != order.From() {\n\t\tpanic(\"invalid order-b:from mismatch\")\n\t}\n\tif esc.EscrowTerms.PartyA != order.To() {\n\t\tpanic(\"invalid order-b:to mismatch\")\n\t}\n\tif !esc.EscrowTerms.AmountB.Equal(order.Amount()) {\n\t\tpanic(\"invalid order-b amount\")\n\t}\n\tesc.OrderA = order\n}\n\nfunc (esc *EscrowContract) Execute() {\n\tif esc.OrderA.IsZero() {\n\t\tpanic(\"order-a not yet set\")\n\t}\n\tif esc.OrderB.IsZero() {\n\t\tpanic(\"order-b not yet set\")\n\t}\n\t// NOTE: succeeds atomically.\n\tesc.OrderA.Execute()\n\tesc.OrderB.Execute()\n}\n\n//----------------------------------------\n// TODO: actually implement these in std package.\n\ntype (\n\tAddress string\n\tCoins []Coin\n\tCoin struct {\n\t\tDenom bool\n\t\tAmount int64\n\t}\n)\n\nfunc (a Coins) Equal(b Coins) bool {\n\tif len(a) != len(b) {\n\t\treturn false\n\t}\n\tfor i, v := range a {\n\t\tif v != b[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "avl", + "path": "gno.land/p/demo/avl", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/avl\n" + }, + { + "name": "node.gno", + "body": "package avl\n\n//----------------------------------------\n// Node\n\n// Node represents a node in an AVL tree.\ntype Node struct {\n\tkey string // key is the unique identifier for the node.\n\tvalue interface{} // value is the data stored in the node.\n\theight int8 // height is the height of the node in the tree.\n\tsize int // size is the number of nodes in the subtree rooted at this node.\n\tleftNode *Node // leftNode is the left child of the node.\n\trightNode *Node // rightNode is the right child of the node.\n}\n\n// NewNode creates a new node with the given key and value.\nfunc NewNode(key string, value interface{}) *Node {\n\treturn \u0026Node{\n\t\tkey: key,\n\t\tvalue: value,\n\t\theight: 0,\n\t\tsize: 1,\n\t}\n}\n\n// Size returns the size of the subtree rooted at the node.\nfunc (node *Node) Size() int {\n\tif node == nil {\n\t\treturn 0\n\t}\n\treturn node.size\n}\n\n// IsLeaf checks if the node is a leaf node (has no children).\nfunc (node *Node) IsLeaf() bool {\n\treturn node.height == 0\n}\n\n// Key returns the key of the node.\nfunc (node *Node) Key() string {\n\treturn node.key\n}\n\n// Value returns the value of the node.\nfunc (node *Node) Value() interface{} {\n\treturn node.value\n}\n\n// _copy creates a copy of the node (excluding value).\nfunc (node *Node) _copy() *Node {\n\tif node.height == 0 {\n\t\tpanic(\"Why are you copying a value node?\")\n\t}\n\treturn \u0026Node{\n\t\tkey: node.key,\n\t\theight: node.height,\n\t\tsize: node.size,\n\t\tleftNode: node.leftNode,\n\t\trightNode: node.rightNode,\n\t}\n}\n\n// Has checks if a node with the given key exists in the subtree rooted at the node.\nfunc (node *Node) Has(key string) (has bool) {\n\tif node == nil {\n\t\treturn false\n\t}\n\tif node.key == key {\n\t\treturn true\n\t}\n\tif node.height == 0 {\n\t\treturn false\n\t}\n\tif key \u003c node.key {\n\t\treturn node.getLeftNode().Has(key)\n\t}\n\treturn node.getRightNode().Has(key)\n}\n\n// Get searches for a node with the given key in the subtree rooted at the node\n// and returns its index, value, and whether it exists.\nfunc (node *Node) Get(key string) (index int, value interface{}, exists bool) {\n\tif node == nil {\n\t\treturn 0, nil, false\n\t}\n\n\tif node.height == 0 {\n\t\tif node.key == key {\n\t\t\treturn 0, node.value, true\n\t\t}\n\t\tif node.key \u003c key {\n\t\t\treturn 1, nil, false\n\t\t}\n\t\treturn 0, nil, false\n\t}\n\n\tif key \u003c node.key {\n\t\treturn node.getLeftNode().Get(key)\n\t}\n\n\trightNode := node.getRightNode()\n\tindex, value, exists = rightNode.Get(key)\n\tindex += node.size - rightNode.size\n\treturn index, value, exists\n}\n\n// GetByIndex retrieves the key-value pair of the node at the given index\n// in the subtree rooted at the node.\nfunc (node *Node) GetByIndex(index int) (key string, value interface{}) {\n\tif node.height == 0 {\n\t\tif index == 0 {\n\t\t\treturn node.key, node.value\n\t\t}\n\t\tpanic(\"GetByIndex asked for invalid index\")\n\t}\n\t// TODO: could improve this by storing the sizes\n\tleftNode := node.getLeftNode()\n\tif index \u003c leftNode.size {\n\t\treturn leftNode.GetByIndex(index)\n\t}\n\treturn node.getRightNode().GetByIndex(index - leftNode.size)\n}\n\n// Set inserts a new node with the given key-value pair into the subtree rooted at the node,\n// and returns the new root of the subtree and whether an existing node was updated.\n//\n// XXX consider a better way to do this... perhaps split Node from Node.\nfunc (node *Node) Set(key string, value interface{}) (newSelf *Node, updated bool) {\n\tif node == nil {\n\t\treturn NewNode(key, value), false\n\t}\n\n\tif node.height == 0 {\n\t\treturn node.setLeaf(key, value)\n\t}\n\n\tnode = node._copy()\n\tif key \u003c node.key {\n\t\tnode.leftNode, updated = node.getLeftNode().Set(key, value)\n\t} else {\n\t\tnode.rightNode, updated = node.getRightNode().Set(key, value)\n\t}\n\n\tif updated {\n\t\treturn node, updated\n\t}\n\n\tnode.calcHeightAndSize()\n\treturn node.balance(), updated\n}\n\n// setLeaf inserts a new leaf node with the given key-value pair into the subtree rooted at the node,\n// and returns the new root of the subtree and whether an existing node was updated.\nfunc (node *Node) setLeaf(key string, value interface{}) (newSelf *Node, updated bool) {\n\tif key == node.key {\n\t\treturn NewNode(key, value), true\n\t}\n\n\tif key \u003c node.key {\n\t\treturn \u0026Node{\n\t\t\tkey: node.key,\n\t\t\theight: 1,\n\t\t\tsize: 2,\n\t\t\tleftNode: NewNode(key, value),\n\t\t\trightNode: node,\n\t\t}, false\n\t}\n\n\treturn \u0026Node{\n\t\tkey: key,\n\t\theight: 1,\n\t\tsize: 2,\n\t\tleftNode: node,\n\t\trightNode: NewNode(key, value),\n\t}, false\n}\n\n// Remove deletes the node with the given key from the subtree rooted at the node.\n// returns the new root of the subtree, the new leftmost leaf key (if changed),\n// the removed value and the removal was successful.\nfunc (node *Node) Remove(key string) (\n\tnewNode *Node, newKey string, value interface{}, removed bool,\n) {\n\tif node == nil {\n\t\treturn nil, \"\", nil, false\n\t}\n\tif node.height == 0 {\n\t\tif key == node.key {\n\t\t\treturn nil, \"\", node.value, true\n\t\t}\n\t\treturn node, \"\", nil, false\n\t}\n\tif key \u003c node.key {\n\t\tvar newLeftNode *Node\n\t\tnewLeftNode, newKey, value, removed = node.getLeftNode().Remove(key)\n\t\tif !removed {\n\t\t\treturn node, \"\", value, false\n\t\t}\n\t\tif newLeftNode == nil { // left node held value, was removed\n\t\t\treturn node.rightNode, node.key, value, true\n\t\t}\n\t\tnode = node._copy()\n\t\tnode.leftNode = newLeftNode\n\t\tnode.calcHeightAndSize()\n\t\tnode = node.balance()\n\t\treturn node, newKey, value, true\n\t}\n\n\tvar newRightNode *Node\n\tnewRightNode, newKey, value, removed = node.getRightNode().Remove(key)\n\tif !removed {\n\t\treturn node, \"\", value, false\n\t}\n\tif newRightNode == nil { // right node held value, was removed\n\t\treturn node.leftNode, \"\", value, true\n\t}\n\tnode = node._copy()\n\tnode.rightNode = newRightNode\n\tif newKey != \"\" {\n\t\tnode.key = newKey\n\t}\n\tnode.calcHeightAndSize()\n\tnode = node.balance()\n\treturn node, \"\", value, true\n}\n\n// getLeftNode returns the left child of the node.\nfunc (node *Node) getLeftNode() *Node {\n\treturn node.leftNode\n}\n\n// getRightNode returns the right child of the node.\nfunc (node *Node) getRightNode() *Node {\n\treturn node.rightNode\n}\n\n// rotateRight performs a right rotation on the node and returns the new root.\n// NOTE: overwrites node\n// TODO: optimize balance \u0026 rotate\nfunc (node *Node) rotateRight() *Node {\n\tnode = node._copy()\n\tl := node.getLeftNode()\n\t_l := l._copy()\n\n\t_lrCached := _l.rightNode\n\t_l.rightNode = node\n\tnode.leftNode = _lrCached\n\n\tnode.calcHeightAndSize()\n\t_l.calcHeightAndSize()\n\n\treturn _l\n}\n\n// rotateLeft performs a left rotation on the node and returns the new root.\n// NOTE: overwrites node\n// TODO: optimize balance \u0026 rotate\nfunc (node *Node) rotateLeft() *Node {\n\tnode = node._copy()\n\tr := node.getRightNode()\n\t_r := r._copy()\n\n\t_rlCached := _r.leftNode\n\t_r.leftNode = node\n\tnode.rightNode = _rlCached\n\n\tnode.calcHeightAndSize()\n\t_r.calcHeightAndSize()\n\n\treturn _r\n}\n\n// calcHeightAndSize updates the height and size of the node based on its children.\n// NOTE: mutates height and size\nfunc (node *Node) calcHeightAndSize() {\n\tnode.height = maxInt8(node.getLeftNode().height, node.getRightNode().height) + 1\n\tnode.size = node.getLeftNode().size + node.getRightNode().size\n}\n\n// calcBalance calculates the balance factor of the node.\nfunc (node *Node) calcBalance() int {\n\treturn int(node.getLeftNode().height) - int(node.getRightNode().height)\n}\n\n// balance balances the subtree rooted at the node and returns the new root.\n// NOTE: assumes that node can be modified\n// TODO: optimize balance \u0026 rotate\nfunc (node *Node) balance() (newSelf *Node) {\n\tbalance := node.calcBalance()\n\tif balance \u003e= -1 {\n\t\treturn node\n\t}\n\tif balance \u003e 1 {\n\t\tif node.getLeftNode().calcBalance() \u003e= 0 {\n\t\t\t// Left Left Case\n\t\t\treturn node.rotateRight()\n\t\t}\n\t\t// Left Right Case\n\t\tleft := node.getLeftNode()\n\t\tnode.leftNode = left.rotateLeft()\n\t\treturn node.rotateRight()\n\t}\n\n\tif node.getRightNode().calcBalance() \u003c= 0 {\n\t\t// Right Right Case\n\t\treturn node.rotateLeft()\n\t}\n\n\t// Right Left Case\n\tright := node.getRightNode()\n\tnode.rightNode = right.rotateRight()\n\treturn node.rotateLeft()\n}\n\n// Shortcut for TraverseInRange.\nfunc (node *Node) Iterate(start, end string, cb func(*Node) bool) bool {\n\treturn node.TraverseInRange(start, end, true, true, cb)\n}\n\n// Shortcut for TraverseInRange.\nfunc (node *Node) ReverseIterate(start, end string, cb func(*Node) bool) bool {\n\treturn node.TraverseInRange(start, end, false, true, cb)\n}\n\n// TraverseInRange traverses all nodes, including inner nodes.\n// Start is inclusive and end is exclusive when ascending,\n// Start and end are inclusive when descending.\n// Empty start and empty end denote no start and no end.\n// If leavesOnly is true, only visit leaf nodes.\n// NOTE: To simulate an exclusive reverse traversal,\n// just append 0x00 to start.\nfunc (node *Node) TraverseInRange(start, end string, ascending bool, leavesOnly bool, cb func(*Node) bool) bool {\n\tif node == nil {\n\t\treturn false\n\t}\n\tafterStart := (start == \"\" || start \u003c node.key)\n\tstartOrAfter := (start == \"\" || start \u003c= node.key)\n\tbeforeEnd := false\n\tif ascending {\n\t\tbeforeEnd = (end == \"\" || node.key \u003c end)\n\t} else {\n\t\tbeforeEnd = (end == \"\" || node.key \u003c= end)\n\t}\n\n\t// Run callback per inner/leaf node.\n\tstop := false\n\tif (!node.IsLeaf() \u0026\u0026 !leavesOnly) ||\n\t\t(node.IsLeaf() \u0026\u0026 startOrAfter \u0026\u0026 beforeEnd) {\n\t\tstop = cb(node)\n\t\tif stop {\n\t\t\treturn stop\n\t\t}\n\t}\n\tif node.IsLeaf() {\n\t\treturn stop\n\t}\n\n\tif ascending {\n\t\t// check lower nodes, then higher\n\t\tif afterStart {\n\t\t\tstop = node.getLeftNode().TraverseInRange(start, end, ascending, leavesOnly, cb)\n\t\t}\n\t\tif stop {\n\t\t\treturn stop\n\t\t}\n\t\tif beforeEnd {\n\t\t\tstop = node.getRightNode().TraverseInRange(start, end, ascending, leavesOnly, cb)\n\t\t}\n\t} else {\n\t\t// check the higher nodes first\n\t\tif beforeEnd {\n\t\t\tstop = node.getRightNode().TraverseInRange(start, end, ascending, leavesOnly, cb)\n\t\t}\n\t\tif stop {\n\t\t\treturn stop\n\t\t}\n\t\tif afterStart {\n\t\t\tstop = node.getLeftNode().TraverseInRange(start, end, ascending, leavesOnly, cb)\n\t\t}\n\t}\n\n\treturn stop\n}\n\n// TraverseByOffset traverses all nodes, including inner nodes.\n// A limit of math.MaxInt means no limit.\nfunc (node *Node) TraverseByOffset(offset, limit int, descending bool, leavesOnly bool, cb func(*Node) bool) bool {\n\tif node == nil {\n\t\treturn false\n\t}\n\n\t// fast paths. these happen only if TraverseByOffset is called directly on a leaf.\n\tif limit \u003c= 0 || offset \u003e= node.size {\n\t\treturn false\n\t}\n\tif node.IsLeaf() {\n\t\tif offset \u003e 0 {\n\t\t\treturn false\n\t\t}\n\t\treturn cb(node)\n\t}\n\n\t// go to the actual recursive function.\n\treturn node.traverseByOffset(offset, limit, descending, leavesOnly, cb)\n}\n\n// TraverseByOffset traverses the subtree rooted at the node by offset and limit,\n// in either ascending or descending order, and applies the callback function to each traversed node.\n// If leavesOnly is true, only leaf nodes are visited.\nfunc (node *Node) traverseByOffset(offset, limit int, descending bool, leavesOnly bool, cb func(*Node) bool) bool {\n\t// caller guarantees: offset \u003c node.size; limit \u003e 0.\n\tif !leavesOnly {\n\t\tif cb(node) {\n\t\t\treturn true\n\t\t}\n\t}\n\tfirst, second := node.getLeftNode(), node.getRightNode()\n\tif descending {\n\t\tfirst, second = second, first\n\t}\n\tif first.IsLeaf() {\n\t\t// either run or skip, based on offset\n\t\tif offset \u003e 0 {\n\t\t\toffset--\n\t\t} else {\n\t\t\tcb(first)\n\t\t\tlimit--\n\t\t\tif limit \u003c= 0 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// possible cases:\n\t\t// 1 the offset given skips the first node entirely\n\t\t// 2 the offset skips none or part of the first node, but the limit requires some of the second node.\n\t\t// 3 the offset skips none or part of the first node, and the limit stops our search on the first node.\n\t\tif offset \u003e= first.size {\n\t\t\toffset -= first.size // 1\n\t\t} else {\n\t\t\tif first.traverseByOffset(offset, limit, descending, leavesOnly, cb) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// number of leaves which could actually be called from inside\n\t\t\tdelta := first.size - offset\n\t\t\toffset = 0\n\t\t\tif delta \u003e= limit {\n\t\t\t\treturn true // 3\n\t\t\t}\n\t\t\tlimit -= delta // 2\n\t\t}\n\t}\n\n\t// because of the caller guarantees and the way we handle the first node,\n\t// at this point we know that limit \u003e 0 and there must be some values in\n\t// this second node that we include.\n\n\t// =\u003e if the second node is a leaf, it has to be included.\n\tif second.IsLeaf() {\n\t\treturn cb(second)\n\t}\n\t// =\u003e if it is not a leaf, it will still be enough to recursively call this\n\t// function with the updated offset and limit\n\treturn second.traverseByOffset(offset, limit, descending, leavesOnly, cb)\n}\n\n// Only used in testing...\nfunc (node *Node) lmd() *Node {\n\tif node.height == 0 {\n\t\treturn node\n\t}\n\treturn node.getLeftNode().lmd()\n}\n\n// Only used in testing...\nfunc (node *Node) rmd() *Node {\n\tif node.height == 0 {\n\t\treturn node\n\t}\n\treturn node.getRightNode().rmd()\n}\n\nfunc maxInt8(a, b int8) int8 {\n\tif a \u003e b {\n\t\treturn a\n\t}\n\treturn b\n}\n" + }, + { + "name": "node_test.gno", + "body": "package avl\n\nimport (\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestTraverseByOffset(t *testing.T) {\n\tconst testStrings = `Alfa\nAlfred\nAlpha\nAlphabet\nBeta\nBeth\nBook\nBrowser`\n\ttt := []struct {\n\t\tname string\n\t\tdesc bool\n\t}{\n\t\t{\"ascending\", false},\n\t\t{\"descending\", true},\n\t}\n\n\tfor _, tt := range tt {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tsl := strings.Split(testStrings, \"\\n\")\n\n\t\t\t// sort a first time in the order opposite to how we'll be traversing\n\t\t\t// the tree, to ensure that we are not just iterating through with\n\t\t\t// insertion order.\n\t\t\tsort.Strings(sl)\n\t\t\tif !tt.desc {\n\t\t\t\treverseSlice(sl)\n\t\t\t}\n\n\t\t\tr := NewNode(sl[0], nil)\n\t\t\tfor _, v := range sl[1:] {\n\t\t\t\tr, _ = r.Set(v, nil)\n\t\t\t}\n\n\t\t\t// then sort sl in the order we'll be traversing it, so that we can\n\t\t\t// compare the result with sl.\n\t\t\treverseSlice(sl)\n\n\t\t\tvar result []string\n\t\t\tfor i := 0; i \u003c len(sl); i++ {\n\t\t\t\tr.TraverseByOffset(i, 1, tt.desc, true, func(n *Node) bool {\n\t\t\t\t\tresult = append(result, n.Key())\n\t\t\t\t\treturn false\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tif !slicesEqual(sl, result) {\n\t\t\t\tt.Errorf(\"want %v got %v\", sl, result)\n\t\t\t}\n\n\t\t\tfor l := 2; l \u003c= len(sl); l++ {\n\t\t\t\t// \"slices\"\n\t\t\t\tfor i := 0; i \u003c= len(sl); i++ {\n\t\t\t\t\tmax := i + l\n\t\t\t\t\tif max \u003e len(sl) {\n\t\t\t\t\t\tmax = len(sl)\n\t\t\t\t\t}\n\t\t\t\t\texp := sl[i:max]\n\t\t\t\t\tactual := []string{}\n\n\t\t\t\t\tr.TraverseByOffset(i, l, tt.desc, true, func(tr *Node) bool {\n\t\t\t\t\t\tactual = append(actual, tr.Key())\n\t\t\t\t\t\treturn false\n\t\t\t\t\t})\n\t\t\t\t\tif !slicesEqual(exp, actual) {\n\t\t\t\t\t\tt.Errorf(\"want %v got %v\", exp, actual)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHas(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []string\n\t\thasKey string\n\t\texpected bool\n\t}{\n\t\t{\n\t\t\t\"has key in non-empty tree\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t\"B\",\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t\"does not have key in non-empty tree\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t\"F\",\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t\"has key in single-node tree\",\n\t\t\t[]string{\"A\"},\n\t\t\t\"A\",\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t\"does not have key in single-node tree\",\n\t\t\t[]string{\"A\"},\n\t\t\t\"B\",\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t\"does not have key in empty tree\",\n\t\t\t[]string{},\n\t\t\t\"A\",\n\t\t\tfalse,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar tree *Node\n\t\t\tfor _, key := range tt.input {\n\t\t\t\ttree, _ = tree.Set(key, nil)\n\t\t\t}\n\n\t\t\tresult := tree.Has(tt.hasKey)\n\n\t\t\tif result != tt.expected {\n\t\t\t\tt.Errorf(\"Expected %v, got %v\", tt.expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGet(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []string\n\t\tgetKey string\n\t\texpectIdx int\n\t\texpectVal interface{}\n\t\texpectExists bool\n\t}{\n\t\t{\n\t\t\t\"get existing key\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t\"B\",\n\t\t\t1,\n\t\t\tnil,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t\"get non-existent key (smaller)\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t\"@\",\n\t\t\t0,\n\t\t\tnil,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t\"get non-existent key (larger)\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t\"F\",\n\t\t\t5,\n\t\t\tnil,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t\"get from empty tree\",\n\t\t\t[]string{},\n\t\t\t\"A\",\n\t\t\t0,\n\t\t\tnil,\n\t\t\tfalse,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar tree *Node\n\t\t\tfor _, key := range tt.input {\n\t\t\t\ttree, _ = tree.Set(key, nil)\n\t\t\t}\n\n\t\t\tidx, val, exists := tree.Get(tt.getKey)\n\n\t\t\tif idx != tt.expectIdx {\n\t\t\t\tt.Errorf(\"Expected index %d, got %d\", tt.expectIdx, idx)\n\t\t\t}\n\n\t\t\tif val != tt.expectVal {\n\t\t\t\tt.Errorf(\"Expected value %v, got %v\", tt.expectVal, val)\n\t\t\t}\n\n\t\t\tif exists != tt.expectExists {\n\t\t\t\tt.Errorf(\"Expected exists %t, got %t\", tt.expectExists, exists)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetByIndex(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []string\n\t\tidx int\n\t\texpectKey string\n\t\texpectVal interface{}\n\t\texpectPanic bool\n\t}{\n\t\t{\n\t\t\t\"get by valid index\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t2,\n\t\t\t\"C\",\n\t\t\tnil,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t\"get by valid index (smallest)\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t0,\n\t\t\t\"A\",\n\t\t\tnil,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t\"get by valid index (largest)\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t4,\n\t\t\t\"E\",\n\t\t\tnil,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t\"get by invalid index (negative)\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t-1,\n\t\t\t\"\",\n\t\t\tnil,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t\"get by invalid index (out of range)\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t5,\n\t\t\t\"\",\n\t\t\tnil,\n\t\t\ttrue,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar tree *Node\n\t\t\tfor _, key := range tt.input {\n\t\t\t\ttree, _ = tree.Set(key, nil)\n\t\t\t}\n\n\t\t\tif tt.expectPanic {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r == nil {\n\t\t\t\t\t\tt.Errorf(\"Expected a panic but didn't get one\")\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\n\t\t\tkey, val := tree.GetByIndex(tt.idx)\n\n\t\t\tif !tt.expectPanic {\n\t\t\t\tif key != tt.expectKey {\n\t\t\t\t\tt.Errorf(\"Expected key %s, got %s\", tt.expectKey, key)\n\t\t\t\t}\n\n\t\t\t\tif val != tt.expectVal {\n\t\t\t\t\tt.Errorf(\"Expected value %v, got %v\", tt.expectVal, val)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRemove(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []string\n\t\tremoveKey string\n\t\texpected []string\n\t}{\n\t\t{\n\t\t\t\"remove leaf node\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"D\"},\n\t\t\t\"B\",\n\t\t\t[]string{\"A\", \"C\", \"D\"},\n\t\t},\n\t\t{\n\t\t\t\"remove node with one child\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"D\"},\n\t\t\t\"A\",\n\t\t\t[]string{\"B\", \"C\", \"D\"},\n\t\t},\n\t\t{\n\t\t\t\"remove node with two children\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t\"C\",\n\t\t\t[]string{\"A\", \"B\", \"D\", \"E\"},\n\t\t},\n\t\t{\n\t\t\t\"remove root node\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t\"C\",\n\t\t\t[]string{\"A\", \"B\", \"D\", \"E\"},\n\t\t},\n\t\t{\n\t\t\t\"remove non-existent key\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t\"F\",\n\t\t\t[]string{\"A\", \"B\", \"C\", \"D\", \"E\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar tree *Node\n\t\t\tfor _, key := range tt.input {\n\t\t\t\ttree, _ = tree.Set(key, nil)\n\t\t\t}\n\n\t\t\ttree, _, _, _ = tree.Remove(tt.removeKey)\n\n\t\t\tresult := make([]string, 0)\n\t\t\ttree.Iterate(\"\", \"\", func(n *Node) bool {\n\t\t\t\tresult = append(result, n.Key())\n\t\t\t\treturn false\n\t\t\t})\n\n\t\t\tif !slicesEqual(tt.expected, result) {\n\t\t\t\tt.Errorf(\"want %v got %v\", tt.expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTraverse(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []string\n\t\texpected []string\n\t}{\n\t\t{\n\t\t\t\"empty tree\",\n\t\t\t[]string{},\n\t\t\t[]string{},\n\t\t},\n\t\t{\n\t\t\t\"single node tree\",\n\t\t\t[]string{\"A\"},\n\t\t\t[]string{\"A\"},\n\t\t},\n\t\t{\n\t\t\t\"small tree\",\n\t\t\t[]string{\"C\", \"A\", \"B\", \"E\", \"D\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"D\", \"E\"},\n\t\t},\n\t\t{\n\t\t\t\"large tree\",\n\t\t\t[]string{\"H\", \"D\", \"L\", \"B\", \"F\", \"J\", \"N\", \"A\", \"C\", \"E\", \"G\", \"I\", \"K\", \"M\", \"O\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar tree *Node\n\t\t\tfor _, key := range tt.input {\n\t\t\t\ttree, _ = tree.Set(key, nil)\n\t\t\t}\n\n\t\t\tt.Run(\"iterate\", func(t *testing.T) {\n\t\t\t\tvar result []string\n\t\t\t\ttree.Iterate(\"\", \"\", func(n *Node) bool {\n\t\t\t\t\tresult = append(result, n.Key())\n\t\t\t\t\treturn false\n\t\t\t\t})\n\t\t\t\tif !slicesEqual(tt.expected, result) {\n\t\t\t\t\tt.Errorf(\"want %v got %v\", tt.expected, result)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"ReverseIterate\", func(t *testing.T) {\n\t\t\t\tvar result []string\n\t\t\t\ttree.ReverseIterate(\"\", \"\", func(n *Node) bool {\n\t\t\t\t\tresult = append(result, n.Key())\n\t\t\t\t\treturn false\n\t\t\t\t})\n\t\t\t\texpected := make([]string, len(tt.expected))\n\t\t\t\tcopy(expected, tt.expected)\n\t\t\t\tfor i, j := 0, len(expected)-1; i \u003c j; i, j = i+1, j-1 {\n\t\t\t\t\texpected[i], expected[j] = expected[j], expected[i]\n\t\t\t\t}\n\t\t\t\tif !slicesEqual(expected, result) {\n\t\t\t\t\tt.Errorf(\"want %v got %v\", expected, result)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"TraverseInRange\", func(t *testing.T) {\n\t\t\t\tvar result []string\n\t\t\t\tstart, end := \"C\", \"M\"\n\t\t\t\ttree.TraverseInRange(start, end, true, true, func(n *Node) bool {\n\t\t\t\t\tresult = append(result, n.Key())\n\t\t\t\t\treturn false\n\t\t\t\t})\n\t\t\t\texpected := make([]string, 0)\n\t\t\t\tfor _, key := range tt.expected {\n\t\t\t\t\tif key \u003e= start \u0026\u0026 key \u003c end {\n\t\t\t\t\t\texpected = append(expected, key)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !slicesEqual(expected, result) {\n\t\t\t\t\tt.Errorf(\"want %v got %v\", expected, result)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestRotateWhenHeightDiffers(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []string\n\t\texpected []string\n\t}{\n\t\t{\n\t\t\t\"right rotation when left subtree is higher\",\n\t\t\t[]string{\"E\", \"C\", \"A\", \"B\", \"D\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"E\", \"D\"},\n\t\t},\n\t\t{\n\t\t\t\"left rotation when right subtree is higher\",\n\t\t\t[]string{\"A\", \"C\", \"E\", \"D\", \"F\"},\n\t\t\t[]string{\"A\", \"C\", \"D\", \"E\", \"F\"},\n\t\t},\n\t\t{\n\t\t\t\"left-right rotation\",\n\t\t\t[]string{\"E\", \"A\", \"C\", \"B\", \"D\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"E\", \"D\"},\n\t\t},\n\t\t{\n\t\t\t\"right-left rotation\",\n\t\t\t[]string{\"A\", \"E\", \"C\", \"B\", \"D\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"E\", \"D\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar tree *Node\n\t\t\tfor _, key := range tt.input {\n\t\t\t\ttree, _ = tree.Set(key, nil)\n\t\t\t}\n\n\t\t\t// perform rotation or balance\n\t\t\ttree = tree.balance()\n\n\t\t\t// check tree structure\n\t\t\tvar result []string\n\t\t\ttree.Iterate(\"\", \"\", func(n *Node) bool {\n\t\t\t\tresult = append(result, n.Key())\n\t\t\t\treturn false\n\t\t\t})\n\n\t\t\tif !slicesEqual(tt.expected, result) {\n\t\t\t\tt.Errorf(\"want %v got %v\", tt.expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRotateAndBalance(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []string\n\t\texpected []string\n\t}{\n\t\t{\n\t\t\t\"right rotation\",\n\t\t\t[]string{\"A\", \"B\", \"C\", \"D\", \"E\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"D\", \"E\"},\n\t\t},\n\t\t{\n\t\t\t\"left rotation\",\n\t\t\t[]string{\"E\", \"D\", \"C\", \"B\", \"A\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"D\", \"E\"},\n\t\t},\n\t\t{\n\t\t\t\"left-right rotation\",\n\t\t\t[]string{\"C\", \"A\", \"E\", \"B\", \"D\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"D\", \"E\"},\n\t\t},\n\t\t{\n\t\t\t\"right-left rotation\",\n\t\t\t[]string{\"C\", \"E\", \"A\", \"D\", \"B\"},\n\t\t\t[]string{\"A\", \"B\", \"C\", \"D\", \"E\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar tree *Node\n\t\t\tfor _, key := range tt.input {\n\t\t\t\ttree, _ = tree.Set(key, nil)\n\t\t\t}\n\n\t\t\ttree = tree.balance()\n\n\t\t\tvar result []string\n\t\t\ttree.Iterate(\"\", \"\", func(n *Node) bool {\n\t\t\t\tresult = append(result, n.Key())\n\t\t\t\treturn false\n\t\t\t})\n\n\t\t\tif !slicesEqual(tt.expected, result) {\n\t\t\t\tt.Errorf(\"want %v got %v\", tt.expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc slicesEqual(w1, w2 []string) bool {\n\tif len(w1) != len(w2) {\n\t\treturn false\n\t}\n\tfor i := 0; i \u003c len(w1); i++ {\n\t\tif w1[0] != w2[0] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc maxint8(a, b int8) int8 {\n\tif a \u003e b {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc reverseSlice(ss []string) {\n\tfor i := 0; i \u003c len(ss)/2; i++ {\n\t\tj := len(ss) - 1 - i\n\t\tss[i], ss[j] = ss[j], ss[i]\n\t}\n}\n" + }, + { + "name": "tree.gno", + "body": "package avl\n\ntype IterCbFn func(key string, value interface{}) bool\n\n//----------------------------------------\n// Tree\n\n// The zero struct can be used as an empty tree.\ntype Tree struct {\n\tnode *Node\n}\n\n// NewTree creates a new empty AVL tree.\nfunc NewTree() *Tree {\n\treturn \u0026Tree{\n\t\tnode: nil,\n\t}\n}\n\n// Size returns the number of key-value pair in the tree.\nfunc (tree *Tree) Size() int {\n\treturn tree.node.Size()\n}\n\n// Has checks whether a key exists in the tree.\n// It returns true if the key exists, otherwise false.\nfunc (tree *Tree) Has(key string) (has bool) {\n\treturn tree.node.Has(key)\n}\n\n// Get retrieves the value associated with the given key.\n// It returns the value and a boolean indicating whether the key exists.\nfunc (tree *Tree) Get(key string) (value interface{}, exists bool) {\n\t_, value, exists = tree.node.Get(key)\n\treturn\n}\n\n// GetByIndex retrieves the key-value pair at the specified index in the tree.\n// It returns the key and value at the given index.\nfunc (tree *Tree) GetByIndex(index int) (key string, value interface{}) {\n\treturn tree.node.GetByIndex(index)\n}\n\n// Set inserts a key-value pair into the tree.\n// If the key already exists, the value will be updated.\n// It returns a boolean indicating whether the key was newly inserted or updated.\nfunc (tree *Tree) Set(key string, value interface{}) (updated bool) {\n\tnewnode, updated := tree.node.Set(key, value)\n\ttree.node = newnode\n\treturn updated\n}\n\n// Remove removes a key-value pair from the tree.\n// It returns the removed value and a boolean indicating whether the key was found and removed.\nfunc (tree *Tree) Remove(key string) (value interface{}, removed bool) {\n\tnewnode, _, value, removed := tree.node.Remove(key)\n\ttree.node = newnode\n\treturn value, removed\n}\n\n// Iterate performs an in-order traversal of the tree within the specified key range.\n// It calls the provided callback function for each key-value pair encountered.\n// If the callback returns true, the iteration is stopped.\nfunc (tree *Tree) Iterate(start, end string, cb IterCbFn) bool {\n\treturn tree.node.TraverseInRange(start, end, true, true,\n\t\tfunc(node *Node) bool {\n\t\t\treturn cb(node.Key(), node.Value())\n\t\t},\n\t)\n}\n\n// ReverseIterate performs a reverse in-order traversal of the tree within the specified key range.\n// It calls the provided callback function for each key-value pair encountered.\n// If the callback returns true, the iteration is stopped.\nfunc (tree *Tree) ReverseIterate(start, end string, cb IterCbFn) bool {\n\treturn tree.node.TraverseInRange(start, end, false, true,\n\t\tfunc(node *Node) bool {\n\t\t\treturn cb(node.Key(), node.Value())\n\t\t},\n\t)\n}\n\n// IterateByOffset performs an in-order traversal of the tree starting from the specified offset.\n// It calls the provided callback function for each key-value pair encountered, up to the specified count.\n// If the callback returns true, the iteration is stopped.\nfunc (tree *Tree) IterateByOffset(offset int, count int, cb IterCbFn) bool {\n\treturn tree.node.TraverseByOffset(offset, count, true, true,\n\t\tfunc(node *Node) bool {\n\t\t\treturn cb(node.Key(), node.Value())\n\t\t},\n\t)\n}\n\n// ReverseIterateByOffset performs a reverse in-order traversal of the tree starting from the specified offset.\n// It calls the provided callback function for each key-value pair encountered, up to the specified count.\n// If the callback returns true, the iteration is stopped.\nfunc (tree *Tree) ReverseIterateByOffset(offset int, count int, cb IterCbFn) bool {\n\treturn tree.node.TraverseByOffset(offset, count, false, true,\n\t\tfunc(node *Node) bool {\n\t\t\treturn cb(node.Key(), node.Value())\n\t\t},\n\t)\n}\n" + }, + { + "name": "tree_test.gno", + "body": "package avl\n\nimport \"testing\"\n\nfunc TestNewTree(t *testing.T) {\n\ttree := NewTree()\n\tif tree.node != nil {\n\t\tt.Error(\"Expected tree.node to be nil\")\n\t}\n}\n\nfunc TestTreeSize(t *testing.T) {\n\ttree := NewTree()\n\tif tree.Size() != 0 {\n\t\tt.Error(\"Expected empty tree size to be 0\")\n\t}\n\n\ttree.Set(\"key1\", \"value1\")\n\ttree.Set(\"key2\", \"value2\")\n\tif tree.Size() != 2 {\n\t\tt.Error(\"Expected tree size to be 2\")\n\t}\n}\n\nfunc TestTreeHas(t *testing.T) {\n\ttree := NewTree()\n\ttree.Set(\"key1\", \"value1\")\n\n\tif !tree.Has(\"key1\") {\n\t\tt.Error(\"Expected tree to have key1\")\n\t}\n\n\tif tree.Has(\"key2\") {\n\t\tt.Error(\"Expected tree to not have key2\")\n\t}\n}\n\nfunc TestTreeGet(t *testing.T) {\n\ttree := NewTree()\n\ttree.Set(\"key1\", \"value1\")\n\n\tvalue, exists := tree.Get(\"key1\")\n\tif !exists || value != \"value1\" {\n\t\tt.Error(\"Expected Get to return value1 and true\")\n\t}\n\n\t_, exists = tree.Get(\"key2\")\n\tif exists {\n\t\tt.Error(\"Expected Get to return false for non-existent key\")\n\t}\n}\n\nfunc TestTreeGetByIndex(t *testing.T) {\n\ttree := NewTree()\n\ttree.Set(\"key1\", \"value1\")\n\ttree.Set(\"key2\", \"value2\")\n\n\tkey, value := tree.GetByIndex(0)\n\tif key != \"key1\" || value != \"value1\" {\n\t\tt.Error(\"Expected GetByIndex(0) to return key1 and value1\")\n\t}\n\n\tkey, value = tree.GetByIndex(1)\n\tif key != \"key2\" || value != \"value2\" {\n\t\tt.Error(\"Expected GetByIndex(1) to return key2 and value2\")\n\t}\n\n\tdefer func() {\n\t\tif r := recover(); r == nil {\n\t\t\tt.Error(\"Expected GetByIndex to panic for out-of-range index\")\n\t\t}\n\t}()\n\ttree.GetByIndex(2)\n}\n\nfunc TestTreeRemove(t *testing.T) {\n\ttree := NewTree()\n\ttree.Set(\"key1\", \"value1\")\n\n\tvalue, removed := tree.Remove(\"key1\")\n\tif !removed || value != \"value1\" || tree.Size() != 0 {\n\t\tt.Error(\"Expected Remove to remove key-value pair\")\n\t}\n\n\t_, removed = tree.Remove(\"key2\")\n\tif removed {\n\t\tt.Error(\"Expected Remove to return false for non-existent key\")\n\t}\n}\n\nfunc TestTreeIterate(t *testing.T) {\n\ttree := NewTree()\n\ttree.Set(\"key1\", \"value1\")\n\ttree.Set(\"key2\", \"value2\")\n\ttree.Set(\"key3\", \"value3\")\n\n\tvar keys []string\n\ttree.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tkeys = append(keys, key)\n\t\treturn false\n\t})\n\n\texpectedKeys := []string{\"key1\", \"key2\", \"key3\"}\n\tif !slicesEqual(keys, expectedKeys) {\n\t\tt.Errorf(\"Expected keys %v, got %v\", expectedKeys, keys)\n\t}\n}\n\nfunc TestTreeReverseIterate(t *testing.T) {\n\ttree := NewTree()\n\ttree.Set(\"key1\", \"value1\")\n\ttree.Set(\"key2\", \"value2\")\n\ttree.Set(\"key3\", \"value3\")\n\n\tvar keys []string\n\ttree.ReverseIterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tkeys = append(keys, key)\n\t\treturn false\n\t})\n\n\texpectedKeys := []string{\"key3\", \"key2\", \"key1\"}\n\tif !slicesEqual(keys, expectedKeys) {\n\t\tt.Errorf(\"Expected keys %v, got %v\", expectedKeys, keys)\n\t}\n}\n\nfunc TestTreeIterateByOffset(t *testing.T) {\n\ttree := NewTree()\n\ttree.Set(\"key1\", \"value1\")\n\ttree.Set(\"key2\", \"value2\")\n\ttree.Set(\"key3\", \"value3\")\n\n\tvar keys []string\n\ttree.IterateByOffset(1, 2, func(key string, value interface{}) bool {\n\t\tkeys = append(keys, key)\n\t\treturn false\n\t})\n\n\texpectedKeys := []string{\"key2\", \"key3\"}\n\tif !slicesEqual(keys, expectedKeys) {\n\t\tt.Errorf(\"Expected keys %v, got %v\", expectedKeys, keys)\n\t}\n}\n\nfunc TestTreeReverseIterateByOffset(t *testing.T) {\n\ttree := NewTree()\n\ttree.Set(\"key1\", \"value1\")\n\ttree.Set(\"key2\", \"value2\")\n\ttree.Set(\"key3\", \"value3\")\n\n\tvar keys []string\n\ttree.ReverseIterateByOffset(1, 2, func(key string, value interface{}) bool {\n\t\tkeys = append(keys, key)\n\t\treturn false\n\t})\n\n\texpectedKeys := []string{\"key2\", \"key1\"}\n\tif !slicesEqual(keys, expectedKeys) {\n\t\tt.Errorf(\"Expected keys %v, got %v\", expectedKeys, keys)\n\t}\n}\n" + }, + { + "name": "z_0_filetest.gno", + "body": "// PKGPATH: gno.land/r/test\npackage test\n\nimport (\n\t\"gno.land/p/demo/avl\"\n)\n\nvar node *avl.Node\n\nfunc init() {\n\tnode = avl.NewNode(\"key0\", \"value0\")\n\t// node, _ = node.Set(\"key0\", \"value0\")\n}\n\nfunc main() {\n\tvar updated bool\n\tnode, updated = node.Set(\"key1\", \"value1\")\n\t// println(node, updated)\n\tprintln(updated, node.Size())\n}\n\n// Output:\n// false 2\n\n// Realm:\n// switchrealm[\"gno.land/r/test\"]\n// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:4\",\n// \"ModTime\": \"7\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:7\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"627e8e517e7ae5db0f3b753e2a32b607989198b6\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:5\"\n// }\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"key1\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"value1\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:9\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:8\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:8\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:7\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"b28057ab7be6383785c0a5503e8a531bdbc21851\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:9\"\n// }\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"key1\"\n// }\n// },\n// {},\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"6da365f0d6cacbcdf53cd5a4b125803cddce08c2\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:4\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"f216afe7b5a17f4ebdbb98dceccedbc22e237596\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:8\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:7\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:6\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:6\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:2\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"ff1a50d8489090af37a2c7766d659f0d717939b5\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:7\"\n// }\n// }\n// }\n// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={\n// \"Blank\": {},\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:2\",\n// \"IsEscaped\": true,\n// \"ModTime\": \"5\",\n// \"RefCount\": \"2\"\n// },\n// \"Parent\": null,\n// \"Source\": {\n// \"@type\": \"/gno.RefNode\",\n// \"BlockNode\": null,\n// \"Location\": {\n// \"Column\": \"0\",\n// \"File\": \"\",\n// \"Line\": \"0\",\n// \"PkgPath\": \"gno.land/r/test\"\n// }\n// },\n// \"Values\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"ae86874f9b47fa5e64c30b3e92e9d07f2ec967a4\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:6\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.FuncType\",\n// \"Params\": [],\n// \"Results\": []\n// },\n// \"V\": {\n// \"@type\": \"/gno.FuncValue\",\n// \"Closure\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Escaped\": true,\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:3\"\n// },\n// \"FileName\": \"main.gno\",\n// \"IsMethod\": false,\n// \"Name\": \"init.1\",\n// \"NativeName\": \"\",\n// \"NativePkg\": \"\",\n// \"PkgPath\": \"gno.land/r/test\",\n// \"Source\": {\n// \"@type\": \"/gno.RefNode\",\n// \"BlockNode\": null,\n// \"Location\": {\n// \"Column\": \"1\",\n// \"File\": \"main.gno\",\n// \"Line\": \"10\",\n// \"PkgPath\": \"gno.land/r/test\"\n// }\n// },\n// \"Type\": {\n// \"@type\": \"/gno.FuncType\",\n// \"Params\": [],\n// \"Results\": []\n// }\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.FuncType\",\n// \"Params\": [],\n// \"Results\": []\n// },\n// \"V\": {\n// \"@type\": \"/gno.FuncValue\",\n// \"Closure\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Escaped\": true,\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:3\"\n// },\n// \"FileName\": \"main.gno\",\n// \"IsMethod\": false,\n// \"Name\": \"main\",\n// \"NativeName\": \"\",\n// \"NativePkg\": \"\",\n// \"PkgPath\": \"gno.land/r/test\",\n// \"Source\": {\n// \"@type\": \"/gno.RefNode\",\n// \"BlockNode\": null,\n// \"Location\": {\n// \"Column\": \"1\",\n// \"File\": \"main.gno\",\n// \"Line\": \"15\",\n// \"PkgPath\": \"gno.land/r/test\"\n// }\n// },\n// \"Type\": {\n// \"@type\": \"/gno.FuncType\",\n// \"Params\": [],\n// \"Results\": []\n// }\n// }\n// }\n// ]\n// }\n" + }, + { + "name": "z_1_filetest.gno", + "body": "// PKGPATH: gno.land/r/test\npackage test\n\nimport (\n\t\"gno.land/p/demo/avl\"\n)\n\nvar node *avl.Node\n\nfunc init() {\n\tnode = avl.NewNode(\"key0\", \"value0\")\n\tnode, _ = node.Set(\"key1\", \"value1\")\n}\n\nfunc main() {\n\tvar updated bool\n\tnode, updated = node.Set(\"key2\", \"value2\")\n\t// println(node, updated)\n\tprintln(updated, node.Size())\n}\n\n// Output:\n// false 3\n\n// Realm:\n// switchrealm[\"gno.land/r/test\"]\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:15]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"key2\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"value2\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:15\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:14\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:14]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:14\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:13\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"143aebc820da33550f7338723fb1e2eec575b196\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:15\"\n// }\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:13]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"key2\"\n// }\n// },\n// {},\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"2f3adc5d0f2a3fe0331cfa93572a7abdde14c9aa\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:8\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"2e733a8e9e74fe14f0a5d10fb0f6728fa53d052d\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:14\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:13\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:12\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:12]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:12\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:11\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"fe20a19f956511f274dc77854e9e5468387260f4\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:13\"\n// }\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:11]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"key1\"\n// }\n// },\n// {},\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AwAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"c89a71bdf045e8bde2059dc9d33839f916e02e5d\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:6\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"90fa67f8c47db4b9b2a60425dff08d5a3385100f\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:12\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:11\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:10\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:10]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:10\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:2\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"83e42caaf53070dd95b5f859053eb51ed900bbda\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:11\"\n// }\n// }\n// }\n// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={\n// \"Blank\": {},\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:2\",\n// \"IsEscaped\": true,\n// \"ModTime\": \"9\",\n// \"RefCount\": \"2\"\n// },\n// \"Parent\": null,\n// \"Source\": {\n// \"@type\": \"/gno.RefNode\",\n// \"BlockNode\": null,\n// \"Location\": {\n// \"Column\": \"0\",\n// \"File\": \"\",\n// \"Line\": \"0\",\n// \"PkgPath\": \"gno.land/r/test\"\n// }\n// },\n// \"Values\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"1faa9fa4ba1935121a6d3f0a623772e9d4499b0a\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:10\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.FuncType\",\n// \"Params\": [],\n// \"Results\": []\n// },\n// \"V\": {\n// \"@type\": \"/gno.FuncValue\",\n// \"Closure\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Escaped\": true,\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:3\"\n// },\n// \"FileName\": \"main.gno\",\n// \"IsMethod\": false,\n// \"Name\": \"init.1\",\n// \"NativeName\": \"\",\n// \"NativePkg\": \"\",\n// \"PkgPath\": \"gno.land/r/test\",\n// \"Source\": {\n// \"@type\": \"/gno.RefNode\",\n// \"BlockNode\": null,\n// \"Location\": {\n// \"Column\": \"1\",\n// \"File\": \"main.gno\",\n// \"Line\": \"10\",\n// \"PkgPath\": \"gno.land/r/test\"\n// }\n// },\n// \"Type\": {\n// \"@type\": \"/gno.FuncType\",\n// \"Params\": [],\n// \"Results\": []\n// }\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.FuncType\",\n// \"Params\": [],\n// \"Results\": []\n// },\n// \"V\": {\n// \"@type\": \"/gno.FuncValue\",\n// \"Closure\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Escaped\": true,\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:3\"\n// },\n// \"FileName\": \"main.gno\",\n// \"IsMethod\": false,\n// \"Name\": \"main\",\n// \"NativeName\": \"\",\n// \"NativePkg\": \"\",\n// \"PkgPath\": \"gno.land/r/test\",\n// \"Source\": {\n// \"@type\": \"/gno.RefNode\",\n// \"BlockNode\": null,\n// \"Location\": {\n// \"Column\": \"1\",\n// \"File\": \"main.gno\",\n// \"Line\": \"15\",\n// \"PkgPath\": \"gno.land/r/test\"\n// }\n// },\n// \"Type\": {\n// \"@type\": \"/gno.FuncType\",\n// \"Params\": [],\n// \"Results\": []\n// }\n// }\n// }\n// ]\n// }\n// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]\n// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]\n" + }, + { + "name": "z_2_filetest.gno", + "body": "// PKGPATH: gno.land/r/test\npackage test\n\nimport (\n\t\"gno.land/p/demo/avl\"\n)\n\nvar tree avl.Tree\n\nfunc init() {\n\ttree.Set(\"key0\", \"value0\")\n\ttree.Set(\"key1\", \"value1\")\n}\n\nfunc main() {\n\tvar updated bool\n\tupdated = tree.Set(\"key2\", \"value2\")\n\tprintln(updated, tree.Size())\n}\n\n// Output:\n// false 3\n\n// Realm:\n// switchrealm[\"gno.land/r/test\"]\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:16]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"key2\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"value2\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:16\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:15\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:15]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:15\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:14\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"db333c89cd6773709e031f1f4e4ed4d3fed66c11\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:16\"\n// }\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:14]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"key2\"\n// }\n// },\n// {},\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"849a50d6c78d65742752e3c89ad8dd556e2e63cb\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:9\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"b4fc2fdd2d0fe936c87ed2ace97136cffeed207f\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:15\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:14\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:13\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:13]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:13\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:12\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"a1160b0060ad752dbfe5fe436f7734bb19136150\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:14\"\n// }\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:12]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"key1\"\n// }\n// },\n// {},\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AwAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"fd95e08763159ac529e26986d652e752e78b6325\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:7\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"3ecdcf148fe2f9e97b72a3bedf303b2ba56d4f4b\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:13\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:12\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:11\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:11]={\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:11\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:3\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"63126557dba88f8556f7a0ccbbfc1d218ae7a302\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:12\"\n// }\n// }\n// }\n// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:3]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"d31c7e797793e03ffe0bbcb72f963264f8300d22\",\n// \"ObjectID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:11\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:3\",\n// \"ModTime\": \"10\",\n// \"OwnerID\": \"a8ada09dee16d791fd406d629fe29bb0ed084a30:2\",\n// \"RefCount\": \"1\"\n// }\n// }\n// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:5]\n// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:6]\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "testutils", + "path": "gno.land/p/demo/testutils", + "files": [ + { + "name": "access.gno", + "body": "package testutils\n\n// for testing access. see tests/files/access*.go\n\n// NOTE: non-package variables cannot be overridden, except during init().\nvar (\n\tTestVar1 int\n\ttestVar2 int\n)\n\nfunc init() {\n\tTestVar1 = 123\n\ttestVar2 = 456\n}\n\ntype TestAccessStruct struct {\n\tPublicField string\n\tprivateField string\n}\n\nfunc (tas TestAccessStruct) PublicMethod() string {\n\treturn tas.PublicField + \"/\" + tas.privateField\n}\n\nfunc (tas TestAccessStruct) privateMethod() string {\n\treturn tas.PublicField + \"/\" + tas.privateField\n}\n\nfunc NewTestAccessStruct(pub, priv string) TestAccessStruct {\n\treturn TestAccessStruct{\n\t\tPublicField: pub,\n\t\tprivateField: priv,\n\t}\n}\n\n// see access6.g0 etc.\ntype PrivateInterface interface {\n\tprivateMethod() string\n}\n\nfunc PrintPrivateInterface(pi PrivateInterface) {\n\tprintln(\"testutils.PrintPrivateInterface\", pi.privateMethod())\n}\n" + }, + { + "name": "crypto.gno", + "body": "package testutils\n\nimport \"std\"\n\nfunc TestAddress(name string) std.Address {\n\tif len(name) \u003e std.RawAddressSize {\n\t\tpanic(\"address name cannot be greater than std.AddressSize bytes\")\n\t}\n\taddr := std.RawAddress{}\n\t// TODO: use strings.RepeatString or similar.\n\t// NOTE: I miss python's \"\".Join().\n\tblanks := \"____________________\"\n\tcopy(addr[:], []byte(blanks))\n\tcopy(addr[:], []byte(name))\n\treturn std.Address(std.EncodeBech32(\"g\", addr))\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/testutils\n" + }, + { + "name": "misc.gno", + "body": "package testutils\n\n// For testing std.GetCallerAt().\nfunc WrapCall(fn func()) {\n\tfn()\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "acl", + "path": "gno.land/p/demo/acl", + "files": [ + { + "name": "acl.gno", + "body": "package acl\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\nfunc New() *Directory {\n\treturn \u0026Directory{\n\t\tuserGroups: avl.Tree{},\n\t\tpermBuckets: avl.Tree{},\n\t}\n}\n\ntype Directory struct {\n\tpermBuckets avl.Tree // identifier -\u003e perms\n\tuserGroups avl.Tree // std.Address -\u003e []string\n}\n\nfunc (d *Directory) HasPerm(addr std.Address, verb, resource string) bool {\n\t// FIXME: consider memoize.\n\n\t// user perms\n\tif d.getBucketPerms(\"u:\"+addr.String()).hasPerm(verb, resource) {\n\t\treturn true\n\t}\n\n\t// everyone's perms.\n\tif d.getBucketPerms(\"g:\"+Everyone).hasPerm(verb, resource) {\n\t\treturn true\n\t}\n\n\t// user groups' perms.\n\tgroups, ok := d.userGroups.Get(addr.String())\n\tif ok {\n\t\tfor _, group := range groups.([]string) {\n\t\t\tif d.getBucketPerms(\"g:\"+group).hasPerm(verb, resource) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (d *Directory) getBucketPerms(bucket string) perms {\n\tres, ok := d.permBuckets.Get(bucket)\n\tif ok {\n\t\treturn res.(perms)\n\t}\n\treturn perms{}\n}\n\nfunc (d *Directory) HasRole(addr std.Address, role string) bool {\n\treturn d.HasPerm(addr, \"role\", role)\n}\n\nfunc (d *Directory) AddUserPerm(addr std.Address, verb, resource string) {\n\tbucket := \"u:\" + addr.String()\n\tp := perm{\n\t\tverbs: []string{verb},\n\t\tresources: []string{resource},\n\t}\n\td.addPermToBucket(bucket, p)\n}\n\nfunc (d *Directory) AddGroupPerm(name string, verb, resource string) {\n\tbucket := \"g:\" + name\n\tp := perm{\n\t\tverbs: []string{verb},\n\t\tresources: []string{resource},\n\t}\n\td.addPermToBucket(bucket, p)\n}\n\nfunc (d *Directory) addPermToBucket(bucket string, p perm) {\n\tvar ps perms\n\n\texisting, ok := d.permBuckets.Get(bucket)\n\tif ok {\n\t\tps = existing.(perms)\n\t}\n\tps = append(ps, p)\n\n\td.permBuckets.Set(bucket, ps)\n}\n\nfunc (d *Directory) AddUserToGroup(user std.Address, group string) {\n\texisting, ok := d.userGroups.Get(user.String())\n\tvar groups []string\n\tif ok {\n\t\tgroups = existing.([]string)\n\t}\n\tgroups = append(groups, group)\n\td.userGroups.Set(user.String(), groups)\n}\n\n// TODO: helpers to remove permissions.\n// TODO: helpers to adds multiple permissions at once -\u003e {verbs: []string{\"read\",\"write\"}}.\n// TODO: helpers to delete users from gorups.\n// TODO: helpers to quickly reset states.\n" + }, + { + "name": "acl_test.gno", + "body": "package acl\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n)\n\nfunc Test(t *testing.T) {\n\tadm := testutils.TestAddress(\"admin\")\n\tmod := testutils.TestAddress(\"mod\")\n\tusr := testutils.TestAddress(\"user\")\n\tcst := testutils.TestAddress(\"custom\")\n\n\tdir := New()\n\n\t// by default, no one has perm.\n\tshouldNotHasRole(t, dir, adm, \"foo\")\n\tshouldNotHasRole(t, dir, mod, \"foo\")\n\tshouldNotHasRole(t, dir, usr, \"foo\")\n\tshouldNotHasRole(t, dir, cst, \"foo\")\n\tshouldNotHasPerm(t, dir, adm, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, mod, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, cst, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, adm, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, mod, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, cst, \"read\", \"r/demo/boards:gnolang/1\")\n\n\t// adding all the rights to admin.\n\tdir.AddUserPerm(adm, \".*\", \".*\")\n\tshouldHasRole(t, dir, adm, \"foo\")\n\tshouldNotHasRole(t, dir, mod, \"foo\")\n\tshouldNotHasRole(t, dir, usr, \"foo\")\n\tshouldNotHasRole(t, dir, cst, \"foo\")\n\tshouldHasPerm(t, dir, adm, \"write\", \"r/demo/boards:gnolang/1\") // new\n\tshouldNotHasPerm(t, dir, mod, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, cst, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, adm, \"read\", \"r/demo/boards:gnolang/1\") // new\n\tshouldNotHasPerm(t, dir, mod, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, cst, \"read\", \"r/demo/boards:gnolang/1\")\n\n\t// adding custom regexp rule for user \"cst\".\n\tdir.AddUserPerm(cst, \"write\", \"r/demo/boards:gnolang/.*\")\n\tshouldHasRole(t, dir, adm, \"foo\")\n\tshouldNotHasRole(t, dir, mod, \"foo\")\n\tshouldNotHasRole(t, dir, usr, \"foo\")\n\tshouldNotHasRole(t, dir, cst, \"foo\")\n\tshouldHasPerm(t, dir, adm, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, mod, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, cst, \"write\", \"r/demo/boards:gnolang/1\") // new\n\tshouldHasPerm(t, dir, adm, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, mod, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, cst, \"read\", \"r/demo/boards:gnolang/1\")\n\n\t// adding a group perm for a new group.\n\t// no changes expected.\n\tdir.AddGroupPerm(\"mods\", \"role\", \"moderator\")\n\tdir.AddGroupPerm(\"mods\", \"write\", \".*\")\n\tshouldHasRole(t, dir, adm, \"foo\")\n\tshouldNotHasRole(t, dir, mod, \"foo\")\n\tshouldNotHasRole(t, dir, usr, \"foo\")\n\tshouldNotHasRole(t, dir, cst, \"foo\")\n\tshouldHasPerm(t, dir, adm, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, mod, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, cst, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, adm, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, mod, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, cst, \"read\", \"r/demo/boards:gnolang/1\")\n\n\t// assigning the user \"mod\" to the \"mods\" group.\n\tdir.AddUserToGroup(mod, \"mods\")\n\tshouldHasRole(t, dir, adm, \"foo\")\n\tshouldNotHasRole(t, dir, mod, \"foo\")\n\tshouldNotHasRole(t, dir, usr, \"foo\")\n\tshouldNotHasRole(t, dir, cst, \"foo\")\n\tshouldHasPerm(t, dir, adm, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, mod, \"write\", \"r/demo/boards:gnolang/1\") // new\n\tshouldNotHasPerm(t, dir, usr, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, cst, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, adm, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, mod, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, cst, \"read\", \"r/demo/boards:gnolang/1\")\n\n\t// adding \"read\" permission for everyone.\n\tdir.AddGroupPerm(Everyone, \"read\", \".*\")\n\tshouldHasRole(t, dir, adm, \"foo\")\n\tshouldNotHasRole(t, dir, mod, \"foo\")\n\tshouldNotHasRole(t, dir, usr, \"foo\")\n\tshouldNotHasRole(t, dir, cst, \"foo\")\n\tshouldHasPerm(t, dir, adm, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, mod, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldNotHasPerm(t, dir, usr, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, cst, \"write\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, adm, \"read\", \"r/demo/boards:gnolang/1\")\n\tshouldHasPerm(t, dir, mod, \"read\", \"r/demo/boards:gnolang/1\") // new\n\tshouldHasPerm(t, dir, usr, \"read\", \"r/demo/boards:gnolang/1\") // new\n\tshouldHasPerm(t, dir, cst, \"read\", \"r/demo/boards:gnolang/1\") // new\n}\n\nfunc shouldHasRole(t *testing.T, dir *Directory, addr std.Address, role string) {\n\tt.Helper()\n\tcheck := dir.HasRole(addr, role)\n\tif !check {\n\t\tt.Errorf(\"%q should has role %q\", addr.String(), role)\n\t}\n}\n\nfunc shouldNotHasRole(t *testing.T, dir *Directory, addr std.Address, role string) {\n\tt.Helper()\n\tcheck := dir.HasRole(addr, role)\n\tif check {\n\t\tt.Errorf(\"%q should not has role %q\", addr.String(), role)\n\t}\n}\n\nfunc shouldHasPerm(t *testing.T, dir *Directory, addr std.Address, verb string, resource string) {\n\tt.Helper()\n\tcheck := dir.HasPerm(addr, verb, resource)\n\tif !check {\n\t\tt.Errorf(\"%q should has perm for %q - %q\", addr.String(), verb, resource)\n\t}\n}\n\nfunc shouldNotHasPerm(t *testing.T, dir *Directory, addr std.Address, verb string, resource string) {\n\tt.Helper()\n\tcheck := dir.HasPerm(addr, verb, resource)\n\tif check {\n\t\tt.Errorf(\"%q should not has perm for %q - %q\", addr.String(), verb, resource)\n\t}\n}\n" + }, + { + "name": "const.gno", + "body": "package acl\n\nconst Everyone string = \"everyone\"\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/acl\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n)\n" + }, + { + "name": "perm.gno", + "body": "package acl\n\nimport \"regexp\"\n\ntype perm struct {\n\tverbs []string\n\tresources []string\n}\n\nfunc (perm perm) hasPerm(verb, resource string) bool {\n\t// check verb\n\tverbOK := false\n\tfor _, pattern := range perm.verbs {\n\t\tif match(pattern, verb) {\n\t\t\tverbOK = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !verbOK {\n\t\treturn false\n\t}\n\n\t// check resource\n\tfor _, pattern := range perm.resources {\n\t\tif match(pattern, resource) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc match(pattern, target string) bool {\n\tif pattern == \".*\" {\n\t\treturn true\n\t}\n\n\tif pattern == target {\n\t\treturn true\n\t}\n\n\t// regexp handling\n\tmatch, _ := regexp.MatchString(pattern, target)\n\treturn match\n}\n" + }, + { + "name": "perms.gno", + "body": "package acl\n\ntype perms []perm\n\nfunc (perms perms) hasPerm(verb, resource string) bool {\n\tfor _, perm := range perms {\n\t\tif perm.hasPerm(verb, resource) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "bf", + "path": "gno.land/p/demo/bf", + "files": [ + { + "name": "bf.gno", + "body": "package bf\n\nimport (\n\t\"strings\"\n)\n\nconst maxlen = 30000\n\nfunc Execute(code string) string {\n\tvar (\n\t\tmemory = make([]byte, maxlen) // memory tape\n\t\tpointer = 0 // initial memory pointer\n\t\tbuf strings.Builder\n\t)\n\n\t// Loop through each character in the code\n\tfor i := 0; i \u003c len(code); i++ {\n\t\tswitch code[i] {\n\t\tcase '\u003e':\n\t\t\t// Increment memory pointer\n\t\t\tpointer++\n\t\t\tif pointer \u003e= maxlen {\n\t\t\t\tpointer = 0\n\t\t\t}\n\t\tcase '\u003c':\n\t\t\t// Decrement memory pointer\n\t\t\tpointer--\n\t\t\tif pointer \u003c 0 {\n\t\t\t\tpointer = maxlen - 1\n\t\t\t}\n\t\tcase '+':\n\t\t\t// Increment the byte at the memory pointer\n\t\t\tmemory[pointer]++\n\t\tcase '-':\n\t\t\t// Decrement the byte at the memory pointer\n\t\t\tmemory[pointer]--\n\t\tcase '.':\n\t\t\t// Output the byte at the memory pointer\n\t\t\tbuf.WriteByte(memory[pointer])\n\t\tcase ',':\n\t\t\t// Input a byte and store it in the memory\n\t\t\tpanic(\"unsupported\")\n\t\t\t// fmt.Scan(\u0026memory[pointer])\n\t\tcase '[':\n\t\t\t// Jump forward past the matching ']' if the byte at the memory pointer is zero\n\t\t\tif memory[pointer] == 0 {\n\t\t\t\tbraceCount := 1\n\t\t\t\tfor braceCount \u003e 0 {\n\t\t\t\t\ti++\n\t\t\t\t\tif code[i] == '[' {\n\t\t\t\t\t\tbraceCount++\n\t\t\t\t\t} else if code[i] == ']' {\n\t\t\t\t\t\tbraceCount--\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase ']':\n\t\t\t// Jump backward to the matching '[' if the byte at the memory pointer is nonzero\n\t\t\tif memory[pointer] != 0 {\n\t\t\t\tbraceCount := 1\n\t\t\t\tfor braceCount \u003e 0 {\n\t\t\t\t\ti--\n\t\t\t\t\tif code[i] == ']' {\n\t\t\t\t\t\tbraceCount++\n\t\t\t\t\t} else if code[i] == '[' {\n\t\t\t\t\t\tbraceCount--\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti-- // Move back one more to compensate for the upcoming increment in the loop\n\t\t\t}\n\t\t}\n\t}\n\treturn buf.String()\n}\n" + }, + { + "name": "bf_test.gno", + "body": "package bf\n\nimport \"testing\"\n\nfunc TestExecuteBrainfuck(t *testing.T) {\n\ttestCases := []struct {\n\t\tname string\n\t\tcode string\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname: \"hello\",\n\t\t\tcode: \"++++++++++[\u003e+++++++\u003e++++++++++\u003e+++\u003e+\u003c\u003c\u003c\u003c-]\u003e++.\u003e+.+++++++..+++.\u003e++.\u003c\u003c+++++++++++++++.\u003e.+++.------.--------.\",\n\t\t\texpected: \"Hello World\",\n\t\t},\n\t\t{\n\t\t\tname: \"increment\",\n\t\t\tcode: \"+++++ +++++ [ \u003e +++++ ++ \u003c - ] \u003e +++++ .\",\n\t\t\texpected: \"K\",\n\t\t},\n\t\t// Add more test cases as needed\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tresult := Execute(tc.code)\n\t\t\tif result != tc.expected {\n\t\t\t\tt.Errorf(\"Expected output: %s, but got: %s\", tc.expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "doc.gno", + "body": "// Package bf implements a minimalist Brainfuck virtual machine in Gno.\n//\n// Brainfuck is an esoteric programming language known for its simplicity and minimalistic design.\n// It operates on an array of memory cells, with a memory pointer that can move left or right.\n// The language consists of eight commands: \u003e \u003c + - . , [ ].\n//\n// Usage:\n// To execute Brainfuck code, use the Execute function and provide the code as a string.\n//\n//\tcode := \"++++++++++[\u003e+++++++\u003e++++++++++\u003e+++\u003e+\u003c\u003c\u003c\u003c-]\u003e++.\u003e+.+++++++..+++.\u003e++.\u003c\u003c+++++++++++++++.\u003e.+++.------.--------.\"\n//\toutput := bf.Execute(code)\n//\n// Note:\n// This implementation is a minimalist version and may not handle all edge cases or advanced features of the Brainfuck language.\n//\n// Reference:\n// For more information on Brainfuck, refer to the Wikipedia page: https://en.wikipedia.org/wiki/Brainfuck\npackage bf // import \"gno.land/p/demo/bf\"\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/bf\n" + }, + { + "name": "run.gno", + "body": "package bf\n\n// for `gno run`\nfunc main() {\n\tcode := \"++++++++++[\u003e+++++++\u003e++++++++++\u003e+++\u003e+\u003c\u003c\u003c\u003c-]\u003e++.\u003e+.+++++++..+++.\u003e++.\u003c\u003c+++++++++++++++.\u003e.+++.------.--------.\"\n\t// TODO: code = os.Args...\n\tExecute(code)\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "mux", + "path": "gno.land/p/demo/mux", + "files": [ + { + "name": "doc.gno", + "body": "// Package mux provides a simple routing and rendering library for handling dynamic path-based requests in Gno contracts.\n//\n// The `mux` package aims to offer similar functionality to `http.ServeMux` in Go, but for Gno's Render() requests.\n// It allows you to define routes with dynamic parts and associate them with corresponding handler functions for rendering outputs.\n//\n// Usage:\n// 1. Create a new Router instance using `NewRouter()` to handle routing and rendering logic.\n// 2. Register routes and their associated handler functions using the `Handle(route, handler)` method.\n// 3. Implement the rendering logic within the handler functions, utilizing the `Request` and `ResponseWriter` types.\n// 4. Use the `Render(path)` method to process a given path and execute the corresponding handler function to obtain the rendered output.\n//\n// Route Patterns:\n// Routes can include dynamic parts enclosed in braces, such as \"users/{id}\" or \"hello/{name}\". The `Request` object's `GetVar(key)`\n// method allows you to extract the value of a specific variable from the path based on routing rules.\n//\n// Example:\n//\n//\trouter := mux.NewRouter()\n//\n//\t// Define a route with a variable and associated handler function\n//\trouter.HandleFunc(\"hello/{name}\", func(res *mux.ResponseWriter, req *mux.Request) {\n//\t\tname := req.GetVar(\"name\")\n//\t\tif name != \"\" {\n//\t\t\tres.Write(\"Hello, \" + name + \"!\")\n//\t\t} else {\n//\t\t\tres.Write(\"Hello, world!\")\n//\t\t}\n//\t})\n//\n//\t// Render the output for the \"/hello/Alice\" path\n//\toutput := router.Render(\"hello/Alice\")\n//\t// Output: \"Hello, Alice!\"\n//\n// Note: The `mux` package provides a basic routing and rendering mechanism for simple use cases. For more advanced routing features,\n// consider using more specialized libraries or frameworks.\npackage mux\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/mux\n" + }, + { + "name": "handler.gno", + "body": "package mux\n\ntype Handler struct {\n\tPattern string\n\tFn HandlerFunc\n}\n\ntype HandlerFunc func(*ResponseWriter, *Request)\n\n// TODO: type ErrHandlerFunc func(*ResponseWriter, *Request) error\n// TODO: NotFoundHandler\n// TODO: AutomaticIndex\n" + }, + { + "name": "helpers.gno", + "body": "package mux\n\nfunc defaultNotFoundHandler(res *ResponseWriter, req *Request) {\n\tres.Write(\"404\")\n}\n" + }, + { + "name": "request.gno", + "body": "package mux\n\nimport \"strings\"\n\n// Request represents an incoming request.\ntype Request struct {\n\tPath string\n\tHandlerPath string\n}\n\n// GetVar retrieves a variable from the path based on routing rules.\nfunc (r *Request) GetVar(key string) string {\n\tvar (\n\t\thandlerParts = strings.Split(r.HandlerPath, \"/\")\n\t\treqParts = strings.Split(r.Path, \"/\")\n\t)\n\n\tfor i := 0; i \u003c len(handlerParts); i++ {\n\t\thandlerPart := handlerParts[i]\n\t\tswitch {\n\t\tcase handlerPart == \"*\":\n\t\t\t// XXX: implement a/b/*/d/e\n\t\t\tpanic(\"not implemented\")\n\t\tcase strings.HasPrefix(handlerPart, \"{\") \u0026\u0026 strings.HasSuffix(handlerPart, \"}\"):\n\t\t\tparameter := handlerPart[1 : len(handlerPart)-1]\n\t\t\tif parameter == key {\n\t\t\t\treturn reqParts[i]\n\t\t\t}\n\t\tdefault:\n\t\t\t// continue\n\t\t}\n\t}\n\n\treturn \"\"\n}\n" + }, + { + "name": "request_test.gno", + "body": "package mux\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestRequest_GetVar(t *testing.T) {\n\tcases := []struct {\n\t\thandlerPath string\n\t\treqPath string\n\t\tgetVarKey string\n\t\texpectedOutput string\n\t}{\n\t\t{\"users/{id}\", \"users/123\", \"id\", \"123\"},\n\t\t{\"users/123\", \"users/123\", \"id\", \"\"},\n\t\t{\"users/{id}\", \"users/123\", \"nonexistent\", \"\"},\n\t\t{\"a/{b}/c/{d}\", \"a/42/c/1337\", \"b\", \"42\"},\n\t\t{\"a/{b}/c/{d}\", \"a/42/c/1337\", \"d\", \"1337\"},\n\t\t{\"{a}\", \"foo\", \"a\", \"foo\"},\n\t\t// TODO: wildcards: a/*/c\n\t\t// TODO: multiple patterns per slashes: a/{b}-{c}/d\n\t}\n\n\tfor _, tt := range cases {\n\t\tname := fmt.Sprintf(\"%s-%s\", tt.handlerPath, tt.reqPath)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\treq := \u0026Request{\n\t\t\t\tHandlerPath: tt.handlerPath,\n\t\t\t\tPath: tt.reqPath,\n\t\t\t}\n\n\t\t\toutput := req.GetVar(tt.getVarKey)\n\t\t\tif output != tt.expectedOutput {\n\t\t\t\tt.Errorf(\"Expected '%q, but got %q\", tt.expectedOutput, output)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "response.gno", + "body": "package mux\n\nimport \"strings\"\n\n// ResponseWriter represents the response writer.\ntype ResponseWriter struct {\n\toutput strings.Builder\n}\n\n// Write appends data to the response output.\nfunc (rw *ResponseWriter) Write(data string) {\n\trw.output.WriteString(data)\n}\n\n// Output returns the final response output.\nfunc (rw *ResponseWriter) Output() string {\n\treturn rw.output.String()\n}\n\n// TODO: func (rw *ResponseWriter) Header()...\n" + }, + { + "name": "router.gno", + "body": "package mux\n\nimport \"strings\"\n\n// Router handles the routing and rendering logic.\ntype Router struct {\n\troutes []Handler\n\tNotFoundHandler HandlerFunc\n}\n\n// NewRouter creates a new Router instance.\nfunc NewRouter() *Router {\n\treturn \u0026Router{\n\t\troutes: make([]Handler, 0),\n\t\tNotFoundHandler: defaultNotFoundHandler,\n\t}\n}\n\n// Render renders the output for the given path using the registered route handler.\nfunc (r *Router) Render(reqPath string) string {\n\treqParts := strings.Split(reqPath, \"/\")\n\n\tfor _, route := range r.routes {\n\t\tpatParts := strings.Split(route.Pattern, \"/\")\n\n\t\tif len(patParts) != len(reqParts) {\n\t\t\tcontinue\n\t\t}\n\n\t\tmatch := true\n\t\tfor i := 0; i \u003c len(patParts); i++ {\n\t\t\tpatPart := patParts[i]\n\t\t\treqPart := reqParts[i]\n\n\t\t\tif patPart == \"*\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif strings.HasPrefix(patPart, \"{\") \u0026\u0026 strings.HasSuffix(patPart, \"}\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif patPart != reqPart {\n\t\t\t\tmatch = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif match {\n\t\t\treq := \u0026Request{\n\t\t\t\tPath: reqPath,\n\t\t\t\tHandlerPath: route.Pattern,\n\t\t\t}\n\t\t\tres := \u0026ResponseWriter{}\n\t\t\troute.Fn(res, req)\n\t\t\treturn res.Output()\n\t\t}\n\t}\n\n\t// not found\n\treq := \u0026Request{Path: reqPath}\n\tres := \u0026ResponseWriter{}\n\tr.NotFoundHandler(res, req)\n\treturn res.Output()\n}\n\n// Handle registers a route and its handler function.\nfunc (r *Router) HandleFunc(pattern string, fn HandlerFunc) {\n\troute := Handler{Pattern: pattern, Fn: fn}\n\tr.routes = append(r.routes, route)\n}\n" + }, + { + "name": "router_test.gno", + "body": "package mux\n\nimport \"testing\"\n\nfunc TestRouter_Render(t *testing.T) {\n\t// Define handlers and route configuration\n\trouter := NewRouter()\n\trouter.HandleFunc(\"hello/{name}\", func(res *ResponseWriter, req *Request) {\n\t\tname := req.GetVar(\"name\")\n\t\tif name != \"\" {\n\t\t\tres.Write(\"Hello, \" + name + \"!\")\n\t\t} else {\n\t\t\tres.Write(\"Hello, world!\")\n\t\t}\n\t})\n\trouter.HandleFunc(\"hi\", func(res *ResponseWriter, req *Request) {\n\t\tres.Write(\"Hi, earth!\")\n\t})\n\n\tcases := []struct {\n\t\tpath string\n\t\texpectedOutput string\n\t}{\n\t\t{\"hello/Alice\", \"Hello, Alice!\"},\n\t\t{\"hi\", \"Hi, earth!\"},\n\t\t{\"hello/Bob\", \"Hello, Bob!\"},\n\t\t// TODO: {\"hello\", \"Hello, world!\"},\n\t\t// TODO: hello/, /hello, hello//Alice, hello/Alice/, hello/Alice/Bob, etc\n\t}\n\tfor _, tt := range cases {\n\t\tt.Run(tt.path, func(t *testing.T) {\n\t\t\toutput := router.Render(tt.path)\n\t\t\tif output != tt.expectedOutput {\n\t\t\t\tt.Errorf(\"Expected output %q, but got %q\", tt.expectedOutput, output)\n\t\t\t}\n\t\t})\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "ufmt", + "path": "gno.land/p/demo/ufmt", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/ufmt\n" + }, + { + "name": "ufmt.gno", + "body": "// Package ufmt provides utility functions for formatting strings, similarly\n// to the Go package \"fmt\", of which only a subset is currently supported\n// (hence the name µfmt - micro fmt).\npackage ufmt\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Println formats using the default formats for its operands and writes to standard output.\n// Println writes the given arguments to standard output with spaces between arguments\n// and a newline at the end.\nfunc Println(args ...interface{}) {\n\tvar strs []string\n\tfor _, arg := range args {\n\t\tswitch v := arg.(type) {\n\t\tcase string:\n\t\t\tstrs = append(strs, v)\n\t\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:\n\t\t\tstrs = append(strs, Sprintf(\"%d\", v))\n\t\tcase bool:\n\t\t\tif v {\n\t\t\t\tstrs = append(strs, \"true\")\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tstrs = append(strs, \"false\")\n\t\tdefault:\n\t\t\tstrs = append(strs, \"(unhandled)\")\n\t\t}\n\t}\n\n\t// TODO: remove println after gno supports os.Stdout\n\tprintln(strings.Join(strs, \" \"))\n}\n\n// Sprintf offers similar functionality to Go's fmt.Sprintf, or the sprintf\n// equivalent available in many languages, including C/C++.\n// The number of args passed must exactly match the arguments consumed by the format.\n// A limited number of formatting verbs and features are currently supported,\n// hence the name ufmt (µfmt, micro-fmt).\n//\n// The currently formatted verbs are the following:\n//\n//\t%s: places a string value directly.\n//\t If the value implements the interface interface{ String() string },\n//\t the String() method is called to retrieve the value.\n//\t%d: formats an integer value using package \"strconv\".\n//\t Currently supports only uint, uint64, int, int64.\n//\t%t: formats a boolean value to \"true\" or \"false\".\n//\t%%: outputs a literal %. Does not consume an argument.\nfunc Sprintf(format string, args ...interface{}) string {\n\t// we use runes to handle multi-byte characters\n\tsTor := []rune(format)\n\tend := len(sTor)\n\targNum := 0\n\targLen := len(args)\n\tbuf := \"\"\n\n\tfor i := 0; i \u003c end; {\n\t\tisLast := i == end-1\n\t\tc := string(sTor[i])\n\n\t\tif isLast || c != \"%\" {\n\t\t\t// we don't check for invalid format like a one ending with \"%\"\n\t\t\tbuf += string(c)\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\n\t\tverb := string(sTor[i+1])\n\t\tif verb == \"%\" {\n\t\t\tbuf += \"%\"\n\t\t\ti += 2\n\t\t\tcontinue\n\t\t}\n\n\t\tif argNum \u003e argLen {\n\t\t\tpanic(\"invalid number of arguments to ufmt.Sprintf\")\n\t\t}\n\t\targ := args[argNum]\n\t\targNum++\n\n\t\tswitch verb {\n\t\tcase \"s\":\n\t\t\tswitch v := arg.(type) {\n\t\t\tcase interface{ String() string }:\n\t\t\t\tbuf += v.String()\n\t\t\tcase string:\n\t\t\t\tbuf += v\n\t\t\tdefault:\n\t\t\t\tbuf += \"(unhandled)\"\n\t\t\t}\n\t\tcase \"d\":\n\t\t\tswitch v := arg.(type) {\n\t\t\tcase int:\n\t\t\t\tbuf += strconv.Itoa(v)\n\t\t\tcase int8:\n\t\t\t\tbuf += strconv.Itoa(int(v))\n\t\t\tcase int16:\n\t\t\t\tbuf += strconv.Itoa(int(v))\n\t\t\tcase int32:\n\t\t\t\tbuf += strconv.Itoa(int(v))\n\t\t\tcase int64:\n\t\t\t\tbuf += strconv.Itoa(int(v))\n\t\t\tcase uint:\n\t\t\t\tbuf += strconv.FormatUint(uint64(v), 10)\n\t\t\tcase uint8:\n\t\t\t\tbuf += strconv.FormatUint(uint64(v), 10)\n\t\t\tcase uint16:\n\t\t\t\tbuf += strconv.FormatUint(uint64(v), 10)\n\t\t\tcase uint32:\n\t\t\t\tbuf += strconv.FormatUint(uint64(v), 10)\n\t\t\tcase uint64:\n\t\t\t\tbuf += strconv.FormatUint(v, 10)\n\t\t\tdefault:\n\t\t\t\tbuf += \"(unhandled)\"\n\t\t\t}\n\t\tcase \"t\":\n\t\t\tswitch v := arg.(type) {\n\t\t\tcase bool:\n\t\t\t\tif v {\n\t\t\t\t\tbuf += \"true\"\n\t\t\t\t} else {\n\t\t\t\t\tbuf += \"false\"\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbuf += \"(unhandled)\"\n\t\t\t}\n\t\t// % handled before, as it does not consume an argument\n\t\tdefault:\n\t\t\tbuf += \"(unhandled)\"\n\t\t}\n\n\t\ti += 2\n\t}\n\tif argNum \u003c argLen {\n\t\tpanic(\"too many arguments to ufmt.Sprintf\")\n\t}\n\treturn buf\n}\n\n// errMsg implements the error interface.\ntype errMsg struct {\n\tmsg string\n}\n\n// Error defines the requirements of the error interface.\n// It functions similarly to Go's errors.New()\nfunc (e *errMsg) Error() string {\n\treturn e.msg\n}\n\n// Errorf is a function that mirrors the functionality of fmt.Errorf.\n//\n// It takes a format string and arguments to create a formatted string,\n// then sets this string as the 'msg' field of an errMsg struct and returns a pointer to this struct.\n//\n// This function operates in a similar manner to Go's fmt.Errorf,\n// providing a way to create formatted error messages.\n//\n// The currently formatted verbs are the following:\n//\n//\t%s: places a string value directly.\n//\t If the value implements the interface interface{ String() string },\n//\t the String() method is called to retrieve the value.\n//\t%d: formats an integer value using package \"strconv\".\n//\t Currently supports only uint, uint64, int, int64.\n//\t%t: formats a boolean value to \"true\" or \"false\".\n//\t%%: outputs a literal %. Does not consume an argument.\nfunc Errorf(format string, args ...interface{}) error {\n\treturn \u0026errMsg{Sprintf(format, args...)}\n}\n" + }, + { + "name": "ufmt_test.gno", + "body": "package ufmt\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype stringer struct{}\n\nfunc (stringer) String() string {\n\treturn \"I'm a stringer\"\n}\n\nfunc TestSprintf(t *testing.T) {\n\tcases := []struct {\n\t\tformat string\n\t\tvalues []interface{}\n\t\texpectedOutput string\n\t}{\n\t\t{\"hello %s!\", []interface{}{\"planet\"}, \"hello planet!\"},\n\t\t{\"hi %%%s!\", []interface{}{\"worl%d\"}, \"hi %worl%d!\"},\n\t\t{\"string [%s]\", []interface{}{\"foo\"}, \"string [foo]\"},\n\t\t{\"int [%d]\", []interface{}{int(42)}, \"int [42]\"},\n\t\t{\"int8 [%d]\", []interface{}{int8(8)}, \"int8 [8]\"},\n\t\t{\"int16 [%d]\", []interface{}{int16(16)}, \"int16 [16]\"},\n\t\t{\"int32 [%d]\", []interface{}{int32(32)}, \"int32 [32]\"},\n\t\t{\"int64 [%d]\", []interface{}{int64(64)}, \"int64 [64]\"},\n\t\t{\"uint [%d]\", []interface{}{uint(42)}, \"uint [42]\"},\n\t\t{\"uint8 [%d]\", []interface{}{uint8(8)}, \"uint8 [8]\"},\n\t\t{\"uint16 [%d]\", []interface{}{uint16(16)}, \"uint16 [16]\"},\n\t\t{\"uint32 [%d]\", []interface{}{uint32(32)}, \"uint32 [32]\"},\n\t\t{\"uint64 [%d]\", []interface{}{uint64(64)}, \"uint64 [64]\"},\n\t\t{\"bool [%t]\", []interface{}{true}, \"bool [true]\"},\n\t\t{\"bool [%t]\", []interface{}{false}, \"bool [false]\"},\n\t\t{\"invalid bool [%t]\", []interface{}{\"invalid\"}, \"invalid bool [(unhandled)]\"},\n\t\t{\"invalid integer [%d]\", []interface{}{\"invalid\"}, \"invalid integer [(unhandled)]\"},\n\t\t{\"invalid string [%s]\", []interface{}{1}, \"invalid string [(unhandled)]\"},\n\t\t{\"no args\", nil, \"no args\"},\n\t\t{\"finish with %\", nil, \"finish with %\"},\n\t\t{\"stringer [%s]\", []interface{}{stringer{}}, \"stringer [I'm a stringer]\"},\n\t\t{\"â\", nil, \"â\"},\n\t\t{\"Hello, World! 😊\", nil, \"Hello, World! 😊\"},\n\t\t{\"unicode formatting: %s\", []interface{}{\"😊\"}, \"unicode formatting: 😊\"},\n\t}\n\n\tfor _, tc := range cases {\n\t\tname := fmt.Sprintf(tc.format, tc.values...)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := Sprintf(tc.format, tc.values...)\n\t\t\tif got != tc.expectedOutput {\n\t\t\t\tt.Errorf(\"got %q, want %q.\", got, tc.expectedOutput)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestErrorf(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tformat string\n\t\targs []interface{}\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname: \"simple string\",\n\t\t\tformat: \"error: %s\",\n\t\t\targs: []interface{}{\"something went wrong\"},\n\t\t\texpected: \"error: something went wrong\",\n\t\t},\n\t\t{\n\t\t\tname: \"integer value\",\n\t\t\tformat: \"value: %d\",\n\t\t\targs: []interface{}{42},\n\t\t\texpected: \"value: 42\",\n\t\t},\n\t\t{\n\t\t\tname: \"boolean value\",\n\t\t\tformat: \"success: %t\",\n\t\t\targs: []interface{}{true},\n\t\t\texpected: \"success: true\",\n\t\t},\n\t\t{\n\t\t\tname: \"multiple values\",\n\t\t\tformat: \"error %d: %s (success=%t)\",\n\t\t\targs: []interface{}{123, \"failure occurred\", false},\n\t\t\texpected: \"error 123: failure occurred (success=false)\",\n\t\t},\n\t\t{\n\t\t\tname: \"literal percent\",\n\t\t\tformat: \"literal %%\",\n\t\t\targs: []interface{}{},\n\t\t\texpected: \"literal %\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := Errorf(tt.format, tt.args...)\n\t\t\tif err.Error() != tt.expected {\n\t\t\t\tt.Errorf(\"Errorf(%q, %v) = %q, expected %q\", tt.format, tt.args, err.Error(), tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// NOTE: Currently, there is no way to get the output of Println without using os.Stdout,\n// so we can only test that it doesn't panic and print arguments well.\nfunc TestPrintln(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\targs []interface{}\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname: \"Empty args\",\n\t\t\targs: []interface{}{},\n\t\t\texpected: \"\",\n\t\t},\n\t\t{\n\t\t\tname: \"String args\",\n\t\t\targs: []interface{}{\"Hello\", \"World\"},\n\t\t\texpected: \"Hello World\",\n\t\t},\n\t\t{\n\t\t\tname: \"Integer args\",\n\t\t\targs: []interface{}{1, 2, 3},\n\t\t\texpected: \"1 2 3\",\n\t\t},\n\t\t{\n\t\t\tname: \"Mixed args\",\n\t\t\targs: []interface{}{\"Hello\", 42, true, false, \"World\"},\n\t\t\texpected: \"Hello 42 true false World\",\n\t\t},\n\t\t{\n\t\t\tname: \"Unhandled type\",\n\t\t\targs: []interface{}{\"Hello\", 3.14, []int{1, 2, 3}},\n\t\t\texpected: \"Hello (unhandled) (unhandled)\",\n\t\t},\n\t}\n\n\t// TODO: replace os.Stdout with a buffer to capture the output and test it.\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tPrintln(tt.args...)\n\t\t})\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "blog", + "path": "gno.land/p/demo/blog", + "files": [ + { + "name": "blog.gno", + "body": "package blog\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/mux\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype Blog struct {\n\tTitle string\n\tPrefix string // i.e. r/gnoland/blog:\n\tPosts avl.Tree // slug -\u003e *Post\n\tPostsPublished avl.Tree // published-date -\u003e *Post\n\tPostsAlphabetical avl.Tree // title -\u003e *Post\n\tNoBreadcrumb bool\n}\n\nfunc (b Blog) RenderLastPostsWidget(limit int) string {\n\tif b.PostsPublished.Size() == 0 {\n\t\treturn \"No posts.\"\n\t}\n\n\toutput := \"\"\n\ti := 0\n\tb.PostsPublished.ReverseIterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tp := value.(*Post)\n\t\toutput += ufmt.Sprintf(\"- [%s](%s)\\n\", p.Title, p.URL())\n\t\ti++\n\t\treturn i \u003e= limit\n\t})\n\treturn output\n}\n\nfunc (b Blog) RenderHome(res *mux.ResponseWriter, req *mux.Request) {\n\tif !b.NoBreadcrumb {\n\t\tres.Write(breadcrumb([]string{b.Title}))\n\t}\n\n\tif b.Posts.Size() == 0 {\n\t\tres.Write(\"No posts.\")\n\t\treturn\n\t}\n\n\tres.Write(\"\u003cdiv class='columns-3'\u003e\")\n\tb.PostsPublished.ReverseIterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tpost := value.(*Post)\n\t\tres.Write(post.RenderListItem())\n\t\treturn false\n\t})\n\tres.Write(\"\u003c/div\u003e\")\n\n\t// FIXME: tag list/cloud.\n}\n\nfunc (b Blog) RenderPost(res *mux.ResponseWriter, req *mux.Request) {\n\tslug := req.GetVar(\"slug\")\n\n\tpost, found := b.Posts.Get(slug)\n\tif !found {\n\t\tres.Write(\"404\")\n\t\treturn\n\t}\n\tp := post.(*Post)\n\n\tres.Write(\"\u003cmain class='gno-tmpl-page'\u003e\" + \"\\n\\n\")\n\n\tres.Write(\"# \" + p.Title + \"\\n\\n\")\n\tres.Write(p.Body + \"\\n\\n\")\n\tres.Write(\"---\\n\\n\")\n\n\tres.Write(p.RenderTagList() + \"\\n\\n\")\n\tres.Write(p.RenderAuthorList() + \"\\n\\n\")\n\tres.Write(p.RenderPublishData() + \"\\n\\n\")\n\n\tres.Write(\"---\\n\")\n\tres.Write(\"\u003cdetails\u003e\u003csummary\u003eComment section\u003c/summary\u003e\\n\\n\")\n\n\t// comments\n\tp.Comments.ReverseIterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tcomment := value.(*Comment)\n\t\tres.Write(comment.RenderListItem())\n\t\treturn false\n\t})\n\n\tres.Write(\"\u003c/details\u003e\\n\")\n\tres.Write(\"\u003c/main\u003e\")\n}\n\nfunc (b Blog) RenderTag(res *mux.ResponseWriter, req *mux.Request) {\n\tslug := req.GetVar(\"slug\")\n\n\tif slug == \"\" {\n\t\tres.Write(\"404\")\n\t\treturn\n\t}\n\n\tif !b.NoBreadcrumb {\n\t\tbreadStr := breadcrumb([]string{\n\t\t\tufmt.Sprintf(\"[%s](%s)\", b.Title, b.Prefix),\n\t\t\t\"t\",\n\t\t\tslug,\n\t\t})\n\t\tres.Write(breadStr)\n\t}\n\n\tnb := 0\n\tb.Posts.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tpost := value.(*Post)\n\t\tif !post.HasTag(slug) {\n\t\t\treturn false\n\t\t}\n\t\tres.Write(post.RenderListItem())\n\t\tnb++\n\t\treturn false\n\t})\n\tif nb == 0 {\n\t\tres.Write(\"No posts.\")\n\t}\n}\n\nfunc (b Blog) Render(path string) string {\n\trouter := mux.NewRouter()\n\trouter.HandleFunc(\"\", b.RenderHome)\n\trouter.HandleFunc(\"p/{slug}\", b.RenderPost)\n\trouter.HandleFunc(\"t/{slug}\", b.RenderTag)\n\treturn router.Render(path)\n}\n\nfunc (b *Blog) NewPost(publisher std.Address, slug, title, body, pubDate string, authors, tags []string) error {\n\tif _, found := b.Posts.Get(slug); found {\n\t\treturn ErrPostSlugExists\n\t}\n\n\tvar parsedTime time.Time\n\tvar err error\n\tif pubDate != \"\" {\n\t\tparsedTime, err = time.Parse(time.RFC3339, pubDate)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\t// If no publication date was passed in by caller, take current block time\n\t\tparsedTime = time.Now()\n\t}\n\n\tpost := \u0026Post{\n\t\tPublisher: publisher,\n\t\tAuthors: authors,\n\t\tSlug: slug,\n\t\tTitle: title,\n\t\tBody: body,\n\t\tTags: tags,\n\t\tCreatedAt: parsedTime,\n\t}\n\n\treturn b.prepareAndSetPost(post, false)\n}\n\nfunc (b *Blog) prepareAndSetPost(post *Post, edit bool) error {\n\tpost.Title = strings.TrimSpace(post.Title)\n\tpost.Body = strings.TrimSpace(post.Body)\n\n\tif post.Title == \"\" {\n\t\treturn ErrPostTitleMissing\n\t}\n\tif post.Body == \"\" {\n\t\treturn ErrPostBodyMissing\n\t}\n\tif post.Slug == \"\" {\n\t\treturn ErrPostSlugMissing\n\t}\n\n\tpost.Blog = b\n\tpost.UpdatedAt = time.Now()\n\n\ttrimmedTitleKey := getTitleKey(post.Title)\n\tpubDateKey := getPublishedKey(post.CreatedAt)\n\n\tif !edit {\n\t\t// Cannot have two posts with same title key\n\t\tif _, found := b.PostsAlphabetical.Get(trimmedTitleKey); found {\n\t\t\treturn ErrPostTitleExists\n\t\t}\n\t\t// Cannot have two posts with *exact* same timestamp\n\t\tif _, found := b.PostsPublished.Get(pubDateKey); found {\n\t\t\treturn ErrPostPubDateExists\n\t\t}\n\t}\n\n\t// Store post under keys\n\tb.PostsAlphabetical.Set(trimmedTitleKey, post)\n\tb.PostsPublished.Set(pubDateKey, post)\n\tb.Posts.Set(post.Slug, post)\n\n\treturn nil\n}\n\nfunc (b *Blog) RemovePost(slug string) {\n\tp, exists := b.Posts.Get(slug)\n\tif !exists {\n\t\tpanic(\"post with specified slug doesn't exist\")\n\t}\n\n\tpost := p.(*Post)\n\n\ttitleKey := getTitleKey(post.Title)\n\tpublishedKey := getPublishedKey(post.CreatedAt)\n\n\t_, _ = b.Posts.Remove(slug)\n\t_, _ = b.PostsAlphabetical.Remove(titleKey)\n\t_, _ = b.PostsPublished.Remove(publishedKey)\n}\n\nfunc (b *Blog) GetPost(slug string) *Post {\n\tpost, found := b.Posts.Get(slug)\n\tif !found {\n\t\treturn nil\n\t}\n\treturn post.(*Post)\n}\n\ntype Post struct {\n\tBlog *Blog\n\tSlug string // FIXME: save space?\n\tTitle string\n\tBody string\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n\tComments avl.Tree\n\tAuthors []string\n\tPublisher std.Address\n\tTags []string\n\tCommentIndex int\n}\n\nfunc (p *Post) Update(title, body, publicationDate string, authors, tags []string) error {\n\tp.Title = title\n\tp.Body = body\n\tp.Tags = tags\n\tp.Authors = authors\n\n\tparsedTime, err := time.Parse(time.RFC3339, publicationDate)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tp.CreatedAt = parsedTime\n\treturn p.Blog.prepareAndSetPost(p, true)\n}\n\nfunc (p *Post) AddComment(author std.Address, comment string) error {\n\tif p == nil {\n\t\treturn ErrNoSuchPost\n\t}\n\tp.CommentIndex++\n\tcommentKey := strconv.Itoa(p.CommentIndex)\n\tcomment = strings.TrimSpace(comment)\n\tp.Comments.Set(commentKey, \u0026Comment{\n\t\tPost: p,\n\t\tCreatedAt: time.Now(),\n\t\tAuthor: author,\n\t\tComment: comment,\n\t})\n\n\treturn nil\n}\n\nfunc (p *Post) DeleteComment(index int) error {\n\tif p == nil {\n\t\treturn ErrNoSuchPost\n\t}\n\tcommentKey := strconv.Itoa(index)\n\tp.Comments.Remove(commentKey)\n\treturn nil\n}\n\nfunc (p *Post) HasTag(tag string) bool {\n\tif p == nil {\n\t\treturn false\n\t}\n\tfor _, t := range p.Tags {\n\t\tif t == tag {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (p *Post) RenderListItem() string {\n\tif p == nil {\n\t\treturn \"error: no such post\\n\"\n\t}\n\toutput := \"\u003cdiv\u003e\\n\\n\"\n\toutput += ufmt.Sprintf(\"### [%s](%s)\\n\", p.Title, p.URL())\n\t// output += ufmt.Sprintf(\"**[Learn More](%s)**\\n\\n\", p.URL())\n\n\toutput += \" \" + p.CreatedAt.Format(\"02 Jan 2006\")\n\t// output += p.Summary() + \"\\n\\n\"\n\t// output += p.RenderTagList() + \"\\n\\n\"\n\toutput += \"\\n\"\n\toutput += \"\u003c/div\u003e\"\n\treturn output\n}\n\n// Render post tags\nfunc (p *Post) RenderTagList() string {\n\tif p == nil {\n\t\treturn \"error: no such post\\n\"\n\t}\n\tif len(p.Tags) == 0 {\n\t\treturn \"\"\n\t}\n\n\toutput := \"Tags: \"\n\tfor idx, tag := range p.Tags {\n\t\tif idx \u003e 0 {\n\t\t\toutput += \" \"\n\t\t}\n\t\ttagURL := p.Blog.Prefix + \"t/\" + tag\n\t\toutput += ufmt.Sprintf(\"[#%s](%s)\", tag, tagURL)\n\n\t}\n\treturn output\n}\n\n// Render authors if there are any\nfunc (p *Post) RenderAuthorList() string {\n\tout := \"Written\"\n\tif len(p.Authors) != 0 {\n\t\tout += \" by \"\n\n\t\tfor idx, author := range p.Authors {\n\t\t\tout += author\n\t\t\tif idx \u003c len(p.Authors)-1 {\n\t\t\t\tout += \", \"\n\t\t\t}\n\t\t}\n\t}\n\tout += \" on \" + p.CreatedAt.Format(\"02 Jan 2006\")\n\n\treturn out\n}\n\nfunc (p *Post) RenderPublishData() string {\n\tout := \"Published \"\n\tif p.Publisher != \"\" {\n\t\tout += \"by \" + p.Publisher.String() + \" \"\n\t}\n\tout += \"to \" + p.Blog.Title\n\n\treturn out\n}\n\nfunc (p *Post) URL() string {\n\tif p == nil {\n\t\treturn p.Blog.Prefix + \"404\"\n\t}\n\treturn p.Blog.Prefix + \"p/\" + p.Slug\n}\n\nfunc (p *Post) Summary() string {\n\tif p == nil {\n\t\treturn \"error: no such post\\n\"\n\t}\n\n\t// FIXME: better summary.\n\tlines := strings.Split(p.Body, \"\\n\")\n\tif len(lines) \u003c= 3 {\n\t\treturn p.Body\n\t}\n\treturn strings.Join(lines[0:3], \"\\n\") + \"...\"\n}\n\ntype Comment struct {\n\tPost *Post\n\tCreatedAt time.Time\n\tAuthor std.Address\n\tComment string\n}\n\nfunc (c Comment) RenderListItem() string {\n\toutput := \"\u003ch5\u003e\"\n\toutput += c.Comment + \"\\n\\n\"\n\toutput += \"\u003c/h5\u003e\"\n\n\toutput += \"\u003ch6\u003e\"\n\toutput += ufmt.Sprintf(\"by %s on %s\", c.Author, c.CreatedAt.Format(time.RFC822))\n\toutput += \"\u003c/h6\u003e\\n\\n\"\n\n\toutput += \"---\\n\\n\"\n\n\treturn output\n}\n" + }, + { + "name": "blog_test.gno", + "body": "package blog\n\n// TODO: add generic tests here.\n// right now, you can checkout r/gnoland/blog/*_test.gno.\n" + }, + { + "name": "errors.gno", + "body": "package blog\n\nimport \"errors\"\n\nvar (\n\tErrPostTitleMissing = errors.New(\"post title is missing\")\n\tErrPostSlugMissing = errors.New(\"post slug is missing\")\n\tErrPostBodyMissing = errors.New(\"post body is missing\")\n\tErrPostSlugExists = errors.New(\"post with specified slug already exists\")\n\tErrPostPubDateExists = errors.New(\"post with specified publication date exists\")\n\tErrPostTitleExists = errors.New(\"post with specified title already exists\")\n\tErrNoSuchPost = errors.New(\"no such post\")\n)\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/blog\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/mux v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "util.gno", + "body": "package blog\n\nimport (\n\t\"strings\"\n\t\"time\"\n)\n\nfunc breadcrumb(parts []string) string {\n\treturn \"# \" + strings.Join(parts, \" / \") + \"\\n\\n\"\n}\n\nfunc getTitleKey(title string) string {\n\treturn strings.Replace(title, \" \", \"\", -1)\n}\n\nfunc getPublishedKey(t time.Time) string {\n\treturn t.Format(time.RFC3339)\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "cford32", + "path": "gno.land/p/demo/cford32", + "files": [ + { + "name": "LICENSE", + "body": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, + { + "name": "README.md", + "body": "# cford32\n\n```\npackage cford32 // import \"gno.land/p/demo/cford32\"\n\nPackage cford32 implements a base32-like encoding/decoding package, with the\nencoding scheme specified by Douglas Crockford.\n\nFrom the website, the requirements of said encoding scheme are to:\n\n - Be human readable and machine readable.\n - Be compact. Humans have difficulty in manipulating long strings of arbitrary\n symbols.\n - Be error resistant. Entering the symbols must not require keyboarding\n gymnastics.\n - Be pronounceable. Humans should be able to accurately transmit the symbols\n to other humans using a telephone.\n\nThis is slightly different from a simple difference in encoding table from\nthe Go's stdlib `encoding/base32`, as when decoding the characters i I l L are\nparsed as 1, and o O is parsed as 0.\n\nThis package additionally provides ways to encode uint64's efficiently, as well\nas efficient encoding to a lowercase variation of the encoding. The encodings\nnever use paddings.\n\n# Uint64 Encoding\n\nAside from lower/uppercase encoding, there is a compact encoding, allowing to\nencode all values in [0,2^34), and the full encoding, allowing all values in\n[0,2^64). The compact encoding uses 7 characters, and the full encoding uses 13\ncharacters. Both are parsed unambiguously by the Uint64 decoder.\n\nThe compact encodings have the first character between ['0','f'], while the\nfull encoding's first character ranges between ['g','z']. Practically, in your\nusage of the package, you should consider which one to use and stick with it,\nwhile considering that the compact encoding, once it reaches 2^34, automatically\nswitches to the full encoding. The properties of the generated strings are still\nmaintained: for instance, any two encoded uint64s x,y consistently generated\nwith the compact encoding, if the numeric value is x \u003c y, will also be x \u003c y in\nlexical ordering. However, values [0,2^34) have a \"double encoding\", which if\nmixed together lose the lexical ordering property.\n\nThe Uint64 encoding is most useful for generating string versions of Uint64 IDs.\nPractically, it allows you to retain sleek and compact IDs for your application\nfor the first 2^34 (\u003e17 billion) entities, while seamlessly rolling over to the\nfull encoding should you exceed that. You are encouraged to use it unless you\nhave a requirement or preferences for IDs consistently being always the same\nsize.\n\nTo use the cford32 encoding for IDs, you may want to consider using package\ngno.land/p/demo/seqid.\n\n[specified by Douglas Crockford]: https://www.crockford.com/base32.html\n\nfunc AppendCompact(id uint64, b []byte) []byte\nfunc AppendDecode(dst, src []byte) ([]byte, error)\nfunc AppendEncode(dst, src []byte) []byte\nfunc AppendEncodeLower(dst, src []byte) []byte\nfunc Decode(dst, src []byte) (n int, err error)\nfunc DecodeString(s string) ([]byte, error)\nfunc DecodedLen(n int) int\nfunc Encode(dst, src []byte)\nfunc EncodeLower(dst, src []byte)\nfunc EncodeToString(src []byte) string\nfunc EncodeToStringLower(src []byte) string\nfunc EncodedLen(n int) int\nfunc NewDecoder(r io.Reader) io.Reader\nfunc NewEncoder(w io.Writer) io.WriteCloser\nfunc NewEncoderLower(w io.Writer) io.WriteCloser\nfunc PutCompact(id uint64) []byte\nfunc PutUint64(id uint64) [13]byte\nfunc PutUint64Lower(id uint64) [13]byte\nfunc Uint64(b []byte) (uint64, error)\ntype CorruptInputError int64\n```\n" + }, + { + "name": "cford32.gno", + "body": "// Modified from the Go Source code for encoding/base32.\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package cford32 implements a base32-like encoding/decoding package, with the\n// encoding scheme [specified by Douglas Crockford].\n//\n// From the website, the requirements of said encoding scheme are to:\n//\n// - Be human readable and machine readable.\n// - Be compact. Humans have difficulty in manipulating long strings of arbitrary symbols.\n// - Be error resistant. Entering the symbols must not require keyboarding gymnastics.\n// - Be pronounceable. Humans should be able to accurately transmit the symbols to other humans using a telephone.\n//\n// This is slightly different from a simple difference in encoding table from\n// the Go's stdlib `encoding/base32`, as when decoding the characters i I l L are\n// parsed as 1, and o O is parsed as 0.\n//\n// This package additionally provides ways to encode uint64's efficiently,\n// as well as efficient encoding to a lowercase variation of the encoding.\n// The encodings never use paddings.\n//\n// # Uint64 Encoding\n//\n// Aside from lower/uppercase encoding, there is a compact encoding, allowing\n// to encode all values in [0,2^34), and the full encoding, allowing all\n// values in [0,2^64). The compact encoding uses 7 characters, and the full\n// encoding uses 13 characters. Both are parsed unambiguously by the Uint64\n// decoder.\n//\n// The compact encodings have the first character between ['0','f'], while the\n// full encoding's first character ranges between ['g','z']. Practically, in\n// your usage of the package, you should consider which one to use and stick\n// with it, while considering that the compact encoding, once it reaches 2^34,\n// automatically switches to the full encoding. The properties of the generated\n// strings are still maintained: for instance, any two encoded uint64s x,y\n// consistently generated with the compact encoding, if the numeric value is\n// x \u003c y, will also be x \u003c y in lexical ordering. However, values [0,2^34) have a\n// \"double encoding\", which if mixed together lose the lexical ordering property.\n//\n// The Uint64 encoding is most useful for generating string versions of Uint64\n// IDs. Practically, it allows you to retain sleek and compact IDs for your\n// application for the first 2^34 (\u003e17 billion) entities, while seamlessly\n// rolling over to the full encoding should you exceed that. You are encouraged\n// to use it unless you have a requirement or preferences for IDs consistently\n// being always the same size.\n//\n// To use the cford32 encoding for IDs, you may want to consider using package\n// [gno.land/p/demo/seqid].\n//\n// [specified by Douglas Crockford]: https://www.crockford.com/base32.html\npackage cford32\n\nimport (\n\t\"io\"\n\t\"strconv\"\n)\n\nconst (\n\tencTable = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\"\n\tencTableLower = \"0123456789abcdefghjkmnpqrstvwxyz\"\n\n\t// each line is 16 bytes\n\tdecTable = \"\" +\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" + // 00-0f\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" + // 10-1f\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" + // 20-2f\n\t\t\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\xff\\xff\\xff\\xff\\xff\\xff\" + // 30-3f\n\t\t\"\\xff\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x01\\x12\\x13\\x01\\x14\\x15\\x00\" + // 40-4f\n\t\t\"\\x16\\x17\\x18\\x19\\x1a\\xff\\x1b\\x1c\\x1d\\x1e\\x1f\\xff\\xff\\xff\\xff\\xff\" + // 50-5f\n\t\t\"\\xff\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x01\\x12\\x13\\x01\\x14\\x15\\x00\" + // 60-6f\n\t\t\"\\x16\\x17\\x18\\x19\\x1a\\xff\\x1b\\x1c\\x1d\\x1e\\x1f\\xff\\xff\\xff\\xff\\xff\" + // 70-7f\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" + // 80-ff (not ASCII)\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" +\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" +\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" +\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" +\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" +\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\" +\n\t\t\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"\n)\n\n// CorruptInputError is returned by parsing functions when an invalid character\n// in the input is found. The integer value represents the byte index where\n// the error occurred.\n//\n// This is typically because the given character does not exist in the encoding.\ntype CorruptInputError int64\n\nfunc (e CorruptInputError) Error() string {\n\treturn \"illegal cford32 data at input byte \" + strconv.FormatInt(int64(e), 10)\n}\n\n// Uint64 parses a cford32-encoded byte slice into a uint64.\n//\n// - The parser requires all provided character to be valid cford32 characters.\n// - The parser disregards case.\n// - If the first character is '0' \u003c= c \u003c= 'f', then the passed value is assumed\n// encoded in the compact encoding, and must be 7 characters long.\n// - If the first character is 'g' \u003c= c \u003c= 'z', then the passed value is\n// assumed encoded in the full encoding, and must be 13 characters long.\n//\n// If any of these requirements fail, a CorruptInputError will be returned.\nfunc Uint64(b []byte) (uint64, error) {\n\tswitch {\n\tdefault:\n\t\treturn 0, CorruptInputError(0)\n\tcase len(b) == 7 \u0026\u0026 b[0] \u003e= '0' \u0026\u0026 b[0] \u003c= 'f':\n\t\tdecVals := [7]byte{\n\t\t\tdecTable[b[0]],\n\t\t\tdecTable[b[1]],\n\t\t\tdecTable[b[2]],\n\t\t\tdecTable[b[3]],\n\t\t\tdecTable[b[4]],\n\t\t\tdecTable[b[5]],\n\t\t\tdecTable[b[6]],\n\t\t}\n\t\tfor idx, v := range decVals {\n\t\t\tif v \u003e= 32 {\n\t\t\t\treturn 0, CorruptInputError(idx)\n\t\t\t}\n\t\t}\n\n\t\treturn 0 +\n\t\t\tuint64(decVals[0])\u003c\u003c30 |\n\t\t\tuint64(decVals[1])\u003c\u003c25 |\n\t\t\tuint64(decVals[2])\u003c\u003c20 |\n\t\t\tuint64(decVals[3])\u003c\u003c15 |\n\t\t\tuint64(decVals[4])\u003c\u003c10 |\n\t\t\tuint64(decVals[5])\u003c\u003c5 |\n\t\t\tuint64(decVals[6]), nil\n\tcase len(b) == 13 \u0026\u0026 b[0] \u003e= 'g' \u0026\u0026 b[0] \u003c= 'z':\n\t\tdecVals := [13]byte{\n\t\t\tdecTable[b[0]] \u0026 0x0F, // disregard high bit\n\t\t\tdecTable[b[1]],\n\t\t\tdecTable[b[2]],\n\t\t\tdecTable[b[3]],\n\t\t\tdecTable[b[4]],\n\t\t\tdecTable[b[5]],\n\t\t\tdecTable[b[6]],\n\t\t\tdecTable[b[7]],\n\t\t\tdecTable[b[8]],\n\t\t\tdecTable[b[9]],\n\t\t\tdecTable[b[10]],\n\t\t\tdecTable[b[11]],\n\t\t\tdecTable[b[12]],\n\t\t}\n\t\tfor idx, v := range decVals {\n\t\t\tif v \u003e= 32 {\n\t\t\t\treturn 0, CorruptInputError(idx)\n\t\t\t}\n\t\t}\n\n\t\treturn 0 +\n\t\t\tuint64(decVals[0])\u003c\u003c60 |\n\t\t\tuint64(decVals[1])\u003c\u003c55 |\n\t\t\tuint64(decVals[2])\u003c\u003c50 |\n\t\t\tuint64(decVals[3])\u003c\u003c45 |\n\t\t\tuint64(decVals[4])\u003c\u003c40 |\n\t\t\tuint64(decVals[5])\u003c\u003c35 |\n\t\t\tuint64(decVals[6])\u003c\u003c30 |\n\t\t\tuint64(decVals[7])\u003c\u003c25 |\n\t\t\tuint64(decVals[8])\u003c\u003c20 |\n\t\t\tuint64(decVals[9])\u003c\u003c15 |\n\t\t\tuint64(decVals[10])\u003c\u003c10 |\n\t\t\tuint64(decVals[11])\u003c\u003c5 |\n\t\t\tuint64(decVals[12]), nil\n\t}\n}\n\nconst mask = 31\n\n// PutUint64 returns a cford32-encoded byte slice.\nfunc PutUint64(id uint64) [13]byte {\n\treturn [13]byte{\n\t\tencTable[id\u003e\u003e60\u0026mask|0x10], // specify full encoding\n\t\tencTable[id\u003e\u003e55\u0026mask],\n\t\tencTable[id\u003e\u003e50\u0026mask],\n\t\tencTable[id\u003e\u003e45\u0026mask],\n\t\tencTable[id\u003e\u003e40\u0026mask],\n\t\tencTable[id\u003e\u003e35\u0026mask],\n\t\tencTable[id\u003e\u003e30\u0026mask],\n\t\tencTable[id\u003e\u003e25\u0026mask],\n\t\tencTable[id\u003e\u003e20\u0026mask],\n\t\tencTable[id\u003e\u003e15\u0026mask],\n\t\tencTable[id\u003e\u003e10\u0026mask],\n\t\tencTable[id\u003e\u003e5\u0026mask],\n\t\tencTable[id\u0026mask],\n\t}\n}\n\n// PutUint64Lower returns a cford32-encoded byte array, swapping uppercase\n// letters with lowercase.\n//\n// For more information on how the value is encoded, see [Uint64].\nfunc PutUint64Lower(id uint64) [13]byte {\n\treturn [13]byte{\n\t\tencTableLower[id\u003e\u003e60\u0026mask|0x10],\n\t\tencTableLower[id\u003e\u003e55\u0026mask],\n\t\tencTableLower[id\u003e\u003e50\u0026mask],\n\t\tencTableLower[id\u003e\u003e45\u0026mask],\n\t\tencTableLower[id\u003e\u003e40\u0026mask],\n\t\tencTableLower[id\u003e\u003e35\u0026mask],\n\t\tencTableLower[id\u003e\u003e30\u0026mask],\n\t\tencTableLower[id\u003e\u003e25\u0026mask],\n\t\tencTableLower[id\u003e\u003e20\u0026mask],\n\t\tencTableLower[id\u003e\u003e15\u0026mask],\n\t\tencTableLower[id\u003e\u003e10\u0026mask],\n\t\tencTableLower[id\u003e\u003e5\u0026mask],\n\t\tencTableLower[id\u0026mask],\n\t}\n}\n\n// PutCompact returns a cford32-encoded byte slice, using the compact\n// representation of cford32 described in the package documentation where\n// possible (all values of id \u003c 1\u003c\u003c34). The lowercase encoding is used.\n//\n// The resulting byte slice will be 7 bytes long for all compact values,\n// and 13 bytes long for\nfunc PutCompact(id uint64) []byte {\n\treturn AppendCompact(id, nil)\n}\n\n// AppendCompact works like [PutCompact] but appends to the given byte slice\n// instead of allocating one anew.\nfunc AppendCompact(id uint64, b []byte) []byte {\n\tconst maxCompact = 1 \u003c\u003c 34\n\tif id \u003c maxCompact {\n\t\treturn append(b,\n\t\t\tencTableLower[id\u003e\u003e30\u0026mask],\n\t\t\tencTableLower[id\u003e\u003e25\u0026mask],\n\t\t\tencTableLower[id\u003e\u003e20\u0026mask],\n\t\t\tencTableLower[id\u003e\u003e15\u0026mask],\n\t\t\tencTableLower[id\u003e\u003e10\u0026mask],\n\t\t\tencTableLower[id\u003e\u003e5\u0026mask],\n\t\t\tencTableLower[id\u0026mask],\n\t\t)\n\t}\n\treturn append(b,\n\t\tencTableLower[id\u003e\u003e60\u0026mask|0x10],\n\t\tencTableLower[id\u003e\u003e55\u0026mask],\n\t\tencTableLower[id\u003e\u003e50\u0026mask],\n\t\tencTableLower[id\u003e\u003e45\u0026mask],\n\t\tencTableLower[id\u003e\u003e40\u0026mask],\n\t\tencTableLower[id\u003e\u003e35\u0026mask],\n\t\tencTableLower[id\u003e\u003e30\u0026mask],\n\t\tencTableLower[id\u003e\u003e25\u0026mask],\n\t\tencTableLower[id\u003e\u003e20\u0026mask],\n\t\tencTableLower[id\u003e\u003e15\u0026mask],\n\t\tencTableLower[id\u003e\u003e10\u0026mask],\n\t\tencTableLower[id\u003e\u003e5\u0026mask],\n\t\tencTableLower[id\u0026mask],\n\t)\n}\n\nfunc DecodedLen(n int) int {\n\treturn n/8*5 + n%8*5/8\n}\n\nfunc EncodedLen(n int) int {\n\treturn n/5*8 + (n%5*8+4)/5\n}\n\n// Encode encodes src using the encoding enc,\n// writing [EncodedLen](len(src)) bytes to dst.\n//\n// The encoding does not contain any padding, unlike Go's base32.\nfunc Encode(dst, src []byte) {\n\t// Copied from encoding/base32/base32.go (go1.22)\n\tif len(src) == 0 {\n\t\treturn\n\t}\n\n\tdi, si := 0, 0\n\tn := (len(src) / 5) * 5\n\tfor si \u003c n {\n\t\t// Combining two 32 bit loads allows the same code to be used\n\t\t// for 32 and 64 bit platforms.\n\t\thi := uint32(src[si+0])\u003c\u003c24 | uint32(src[si+1])\u003c\u003c16 | uint32(src[si+2])\u003c\u003c8 | uint32(src[si+3])\n\t\tlo := hi\u003c\u003c8 | uint32(src[si+4])\n\n\t\tdst[di+0] = encTable[(hi\u003e\u003e27)\u00260x1F]\n\t\tdst[di+1] = encTable[(hi\u003e\u003e22)\u00260x1F]\n\t\tdst[di+2] = encTable[(hi\u003e\u003e17)\u00260x1F]\n\t\tdst[di+3] = encTable[(hi\u003e\u003e12)\u00260x1F]\n\t\tdst[di+4] = encTable[(hi\u003e\u003e7)\u00260x1F]\n\t\tdst[di+5] = encTable[(hi\u003e\u003e2)\u00260x1F]\n\t\tdst[di+6] = encTable[(lo\u003e\u003e5)\u00260x1F]\n\t\tdst[di+7] = encTable[(lo)\u00260x1F]\n\n\t\tsi += 5\n\t\tdi += 8\n\t}\n\n\t// Add the remaining small block\n\tremain := len(src) - si\n\tif remain == 0 {\n\t\treturn\n\t}\n\n\t// Encode the remaining bytes in reverse order.\n\tval := uint32(0)\n\tswitch remain {\n\tcase 4:\n\t\tval |= uint32(src[si+3])\n\t\tdst[di+6] = encTable[val\u003c\u003c3\u00260x1F]\n\t\tdst[di+5] = encTable[val\u003e\u003e2\u00260x1F]\n\t\tfallthrough\n\tcase 3:\n\t\tval |= uint32(src[si+2]) \u003c\u003c 8\n\t\tdst[di+4] = encTable[val\u003e\u003e7\u00260x1F]\n\t\tfallthrough\n\tcase 2:\n\t\tval |= uint32(src[si+1]) \u003c\u003c 16\n\t\tdst[di+3] = encTable[val\u003e\u003e12\u00260x1F]\n\t\tdst[di+2] = encTable[val\u003e\u003e17\u00260x1F]\n\t\tfallthrough\n\tcase 1:\n\t\tval |= uint32(src[si+0]) \u003c\u003c 24\n\t\tdst[di+1] = encTable[val\u003e\u003e22\u00260x1F]\n\t\tdst[di+0] = encTable[val\u003e\u003e27\u00260x1F]\n\t}\n}\n\n// EncodeLower is like [Encode], but uses the lowercase\nfunc EncodeLower(dst, src []byte) {\n\t// Copied from encoding/base32/base32.go (go1.22)\n\tif len(src) == 0 {\n\t\treturn\n\t}\n\n\tdi, si := 0, 0\n\tn := (len(src) / 5) * 5\n\tfor si \u003c n {\n\t\t// Combining two 32 bit loads allows the same code to be used\n\t\t// for 32 and 64 bit platforms.\n\t\thi := uint32(src[si+0])\u003c\u003c24 | uint32(src[si+1])\u003c\u003c16 | uint32(src[si+2])\u003c\u003c8 | uint32(src[si+3])\n\t\tlo := hi\u003c\u003c8 | uint32(src[si+4])\n\n\t\tdst[di+0] = encTableLower[(hi\u003e\u003e27)\u00260x1F]\n\t\tdst[di+1] = encTableLower[(hi\u003e\u003e22)\u00260x1F]\n\t\tdst[di+2] = encTableLower[(hi\u003e\u003e17)\u00260x1F]\n\t\tdst[di+3] = encTableLower[(hi\u003e\u003e12)\u00260x1F]\n\t\tdst[di+4] = encTableLower[(hi\u003e\u003e7)\u00260x1F]\n\t\tdst[di+5] = encTableLower[(hi\u003e\u003e2)\u00260x1F]\n\t\tdst[di+6] = encTableLower[(lo\u003e\u003e5)\u00260x1F]\n\t\tdst[di+7] = encTableLower[(lo)\u00260x1F]\n\n\t\tsi += 5\n\t\tdi += 8\n\t}\n\n\t// Add the remaining small block\n\tremain := len(src) - si\n\tif remain == 0 {\n\t\treturn\n\t}\n\n\t// Encode the remaining bytes in reverse order.\n\tval := uint32(0)\n\tswitch remain {\n\tcase 4:\n\t\tval |= uint32(src[si+3])\n\t\tdst[di+6] = encTableLower[val\u003c\u003c3\u00260x1F]\n\t\tdst[di+5] = encTableLower[val\u003e\u003e2\u00260x1F]\n\t\tfallthrough\n\tcase 3:\n\t\tval |= uint32(src[si+2]) \u003c\u003c 8\n\t\tdst[di+4] = encTableLower[val\u003e\u003e7\u00260x1F]\n\t\tfallthrough\n\tcase 2:\n\t\tval |= uint32(src[si+1]) \u003c\u003c 16\n\t\tdst[di+3] = encTableLower[val\u003e\u003e12\u00260x1F]\n\t\tdst[di+2] = encTableLower[val\u003e\u003e17\u00260x1F]\n\t\tfallthrough\n\tcase 1:\n\t\tval |= uint32(src[si+0]) \u003c\u003c 24\n\t\tdst[di+1] = encTableLower[val\u003e\u003e22\u00260x1F]\n\t\tdst[di+0] = encTableLower[val\u003e\u003e27\u00260x1F]\n\t}\n}\n\n// AppendEncode appends the cford32 encoded src to dst\n// and returns the extended buffer.\nfunc AppendEncode(dst, src []byte) []byte {\n\tn := EncodedLen(len(src))\n\tdst = grow(dst, n)\n\tEncode(dst[len(dst):][:n], src)\n\treturn dst[:len(dst)+n]\n}\n\n// AppendEncodeLower appends the lowercase cford32 encoded src to dst\n// and returns the extended buffer.\nfunc AppendEncodeLower(dst, src []byte) []byte {\n\tn := EncodedLen(len(src))\n\tdst = grow(dst, n)\n\tEncodeLower(dst[len(dst):][:n], src)\n\treturn dst[:len(dst)+n]\n}\n\nfunc grow(s []byte, n int) []byte {\n\t// slices.Grow\n\tif n -= cap(s) - len(s); n \u003e 0 {\n\t\tnews := make([]byte, cap(s)+n)\n\t\tcopy(news[:cap(s)], s[:cap(s)])\n\t\treturn news[:len(s)]\n\t}\n\treturn s\n}\n\n// EncodeToString returns the cford32 encoding of src.\nfunc EncodeToString(src []byte) string {\n\tbuf := make([]byte, EncodedLen(len(src)))\n\tEncode(buf, src)\n\treturn string(buf)\n}\n\n// EncodeToStringLower returns the cford32 lowercase encoding of src.\nfunc EncodeToStringLower(src []byte) string {\n\tbuf := make([]byte, EncodedLen(len(src)))\n\tEncodeLower(buf, src)\n\treturn string(buf)\n}\n\nfunc decode(dst, src []byte) (n int, err error) {\n\tdsti := 0\n\tolen := len(src)\n\n\tfor len(src) \u003e 0 {\n\t\t// Decode quantum using the base32 alphabet\n\t\tvar dbuf [8]byte\n\t\tdlen := 8\n\n\t\tfor j := 0; j \u003c 8; {\n\t\t\tif len(src) == 0 {\n\t\t\t\t// We have reached the end and are not expecting any padding\n\t\t\t\tdlen = j\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tin := src[0]\n\t\t\tsrc = src[1:]\n\t\t\tdbuf[j] = decTable[in]\n\t\t\tif dbuf[j] == 0xFF {\n\t\t\t\treturn n, CorruptInputError(olen - len(src) - 1)\n\t\t\t}\n\t\t\tj++\n\t\t}\n\n\t\t// Pack 8x 5-bit source blocks into 5 byte destination\n\t\t// quantum\n\t\tswitch dlen {\n\t\tcase 8:\n\t\t\tdst[dsti+4] = dbuf[6]\u003c\u003c5 | dbuf[7]\n\t\t\tn++\n\t\t\tfallthrough\n\t\tcase 7:\n\t\t\tdst[dsti+3] = dbuf[4]\u003c\u003c7 | dbuf[5]\u003c\u003c2 | dbuf[6]\u003e\u003e3\n\t\t\tn++\n\t\t\tfallthrough\n\t\tcase 5:\n\t\t\tdst[dsti+2] = dbuf[3]\u003c\u003c4 | dbuf[4]\u003e\u003e1\n\t\t\tn++\n\t\t\tfallthrough\n\t\tcase 4:\n\t\t\tdst[dsti+1] = dbuf[1]\u003c\u003c6 | dbuf[2]\u003c\u003c1 | dbuf[3]\u003e\u003e4\n\t\t\tn++\n\t\t\tfallthrough\n\t\tcase 2:\n\t\t\tdst[dsti+0] = dbuf[0]\u003c\u003c3 | dbuf[1]\u003e\u003e2\n\t\t\tn++\n\t\t}\n\t\tdsti += 5\n\t}\n\treturn n, nil\n}\n\ntype encoder struct {\n\terr error\n\tw io.Writer\n\tenc func(dst, src []byte)\n\tbuf [5]byte // buffered data waiting to be encoded\n\tnbuf int // number of bytes in buf\n\tout [1024]byte // output buffer\n}\n\nfunc NewEncoder(w io.Writer) io.WriteCloser {\n\treturn \u0026encoder{w: w, enc: Encode}\n}\n\nfunc NewEncoderLower(w io.Writer) io.WriteCloser {\n\treturn \u0026encoder{w: w, enc: EncodeLower}\n}\n\nfunc (e *encoder) Write(p []byte) (n int, err error) {\n\tif e.err != nil {\n\t\treturn 0, e.err\n\t}\n\n\t// Leading fringe.\n\tif e.nbuf \u003e 0 {\n\t\tvar i int\n\t\tfor i = 0; i \u003c len(p) \u0026\u0026 e.nbuf \u003c 5; i++ {\n\t\t\te.buf[e.nbuf] = p[i]\n\t\t\te.nbuf++\n\t\t}\n\t\tn += i\n\t\tp = p[i:]\n\t\tif e.nbuf \u003c 5 {\n\t\t\treturn\n\t\t}\n\t\te.enc(e.out[0:], e.buf[0:])\n\t\tif _, e.err = e.w.Write(e.out[0:8]); e.err != nil {\n\t\t\treturn n, e.err\n\t\t}\n\t\te.nbuf = 0\n\t}\n\n\t// Large interior chunks.\n\tfor len(p) \u003e= 5 {\n\t\tnn := len(e.out) / 8 * 5\n\t\tif nn \u003e len(p) {\n\t\t\tnn = len(p)\n\t\t\tnn -= nn % 5\n\t\t}\n\t\te.enc(e.out[0:], p[0:nn])\n\t\tif _, e.err = e.w.Write(e.out[0 : nn/5*8]); e.err != nil {\n\t\t\treturn n, e.err\n\t\t}\n\t\tn += nn\n\t\tp = p[nn:]\n\t}\n\n\t// Trailing fringe.\n\tcopy(e.buf[:], p)\n\te.nbuf = len(p)\n\tn += len(p)\n\treturn\n}\n\n// Close flushes any pending output from the encoder.\n// It is an error to call Write after calling Close.\nfunc (e *encoder) Close() error {\n\t// If there's anything left in the buffer, flush it out\n\tif e.err == nil \u0026\u0026 e.nbuf \u003e 0 {\n\t\te.enc(e.out[0:], e.buf[0:e.nbuf])\n\t\tencodedLen := EncodedLen(e.nbuf)\n\t\te.nbuf = 0\n\t\t_, e.err = e.w.Write(e.out[0:encodedLen])\n\t}\n\treturn e.err\n}\n\n// Decode decodes src using cford32. It writes at most\n// [DecodedLen](len(src)) bytes to dst and returns the number of bytes\n// written. If src contains invalid cford32 data, it will return the\n// number of bytes successfully written and [CorruptInputError].\n// Newline characters (\\r and \\n) are ignored.\nfunc Decode(dst, src []byte) (n int, err error) {\n\tbuf := make([]byte, len(src))\n\tl := stripNewlines(buf, src)\n\treturn decode(dst, buf[:l])\n}\n\n// AppendDecode appends the cford32 decoded src to dst\n// and returns the extended buffer.\n// If the input is malformed, it returns the partially decoded src and an error.\nfunc AppendDecode(dst, src []byte) ([]byte, error) {\n\tn := DecodedLen(len(src))\n\n\tdst = grow(dst, n)\n\tdstsl := dst[len(dst) : len(dst)+n]\n\tn, err := Decode(dstsl, src)\n\treturn dst[:len(dst)+n], err\n}\n\n// DecodeString returns the bytes represented by the cford32 string s.\nfunc DecodeString(s string) ([]byte, error) {\n\tbuf := []byte(s)\n\tl := stripNewlines(buf, buf)\n\tn, err := decode(buf, buf[:l])\n\treturn buf[:n], err\n}\n\n// stripNewlines removes newline characters and returns the number\n// of non-newline characters copied to dst.\nfunc stripNewlines(dst, src []byte) int {\n\toffset := 0\n\tfor _, b := range src {\n\t\tif b == '\\r' || b == '\\n' {\n\t\t\tcontinue\n\t\t}\n\t\tdst[offset] = b\n\t\toffset++\n\t}\n\treturn offset\n}\n\ntype decoder struct {\n\terr error\n\tr io.Reader\n\tbuf [1024]byte // leftover input\n\tnbuf int\n\tout []byte // leftover decoded output\n\toutbuf [1024 / 8 * 5]byte\n}\n\n// NewDecoder constructs a new base32 stream decoder.\nfunc NewDecoder(r io.Reader) io.Reader {\n\treturn \u0026decoder{r: \u0026newlineFilteringReader{r}}\n}\n\nfunc readEncodedData(r io.Reader, buf []byte) (n int, err error) {\n\tfor n \u003c 1 \u0026\u0026 err == nil {\n\t\tvar nn int\n\t\tnn, err = r.Read(buf[n:])\n\t\tn += nn\n\t}\n\treturn\n}\n\nfunc (d *decoder) Read(p []byte) (n int, err error) {\n\t// Use leftover decoded output from last read.\n\tif len(d.out) \u003e 0 {\n\t\tn = copy(p, d.out)\n\t\td.out = d.out[n:]\n\t\tif len(d.out) == 0 {\n\t\t\treturn n, d.err\n\t\t}\n\t\treturn n, nil\n\t}\n\n\tif d.err != nil {\n\t\treturn 0, d.err\n\t}\n\n\t// Read nn bytes from input, bounded [8,len(d.buf)]\n\tnn := (len(p)/5 + 1) * 8\n\tif nn \u003e len(d.buf) {\n\t\tnn = len(d.buf)\n\t}\n\n\tnn, d.err = readEncodedData(d.r, d.buf[d.nbuf:nn])\n\td.nbuf += nn\n\tif d.nbuf \u003c 1 {\n\t\treturn 0, d.err\n\t}\n\n\t// Decode chunk into p, or d.out and then p if p is too small.\n\tnr := d.nbuf\n\tif d.err != io.EOF \u0026\u0026 nr%8 != 0 {\n\t\tnr -= nr % 8\n\t}\n\tnw := DecodedLen(d.nbuf)\n\n\tif nw \u003e len(p) {\n\t\tnw, err = decode(d.outbuf[0:], d.buf[0:nr])\n\t\td.out = d.outbuf[0:nw]\n\t\tn = copy(p, d.out)\n\t\td.out = d.out[n:]\n\t} else {\n\t\tn, err = decode(p, d.buf[0:nr])\n\t}\n\td.nbuf -= nr\n\tfor i := 0; i \u003c d.nbuf; i++ {\n\t\td.buf[i] = d.buf[i+nr]\n\t}\n\n\tif err != nil \u0026\u0026 (d.err == nil || d.err == io.EOF) {\n\t\td.err = err\n\t}\n\n\tif len(d.out) \u003e 0 {\n\t\t// We cannot return all the decoded bytes to the caller in this\n\t\t// invocation of Read, so we return a nil error to ensure that Read\n\t\t// will be called again. The error stored in d.err, if any, will be\n\t\t// returned with the last set of decoded bytes.\n\t\treturn n, nil\n\t}\n\n\treturn n, d.err\n}\n\ntype newlineFilteringReader struct {\n\twrapped io.Reader\n}\n\nfunc (r *newlineFilteringReader) Read(p []byte) (int, error) {\n\tn, err := r.wrapped.Read(p)\n\tfor n \u003e 0 {\n\t\ts := p[0:n]\n\t\toffset := stripNewlines(s, s)\n\t\tif err != nil || offset \u003e 0 {\n\t\t\treturn offset, err\n\t\t}\n\t\t// Previous buffer entirely whitespace, read again\n\t\tn, err = r.wrapped.Read(p)\n\t}\n\treturn n, err\n}\n" + }, + { + "name": "cford32_test.gno", + "body": "package cford32\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestCompactRoundtrip(t *testing.T) {\n\tbuf := make([]byte, 13)\n\tprev := make([]byte, 13)\n\tfor i := uint64(0); i \u003c (1 \u003c\u003c 12); i++ {\n\t\tres := AppendCompact(i, buf[:0])\n\t\tback, err := Uint64(res)\n\t\ttestEqual(t, \"Uint64(%q) = (%d, %v), want %v\", string(res), back, err, nil)\n\t\ttestEqual(t, \"Uint64(%q) = %d, want %v\", string(res), back, i)\n\n\t\ttestEqual(t, \"bytes.Compare(prev, res) = %d, want %d\", bytes.Compare(prev, res), -1)\n\t\tprev, buf = res, prev\n\t}\n\tfor i := uint64(1\u003c\u003c34 - 1024); i \u003c (1\u003c\u003c34 + 1024); i++ {\n\t\tres := AppendCompact(i, buf[:0])\n\t\tback, err := Uint64(res)\n\t\t// println(string(res))\n\t\ttestEqual(t, \"Uint64(%q) = (%d, %v), want %v\", string(res), back, err, nil)\n\t\ttestEqual(t, \"Uint64(%q) = %d, want %v\", string(res), back, i)\n\n\t\ttestEqual(t, \"bytes.Compare(prev, res) = %d, want %d\", bytes.Compare(prev, res), -1)\n\t\tprev, buf = res, prev\n\t}\n\tfor i := uint64(1\u003c\u003c64 - 5000); i != 0; i++ {\n\t\tres := AppendCompact(i, buf[:0])\n\t\tback, err := Uint64(res)\n\t\ttestEqual(t, \"Uint64(%q) = (%d, %v), want %v\", string(res), back, err, nil)\n\t\ttestEqual(t, \"Uint64(%q) = %d, want %v\", string(res), back, i)\n\n\t\ttestEqual(t, \"bytes.Compare(prev, res) = %d, want %d\", bytes.Compare(prev, res), -1)\n\t\tprev, buf = res, prev\n\t}\n}\n\nfunc BenchmarkCompact(b *testing.B) {\n\tbuf := make([]byte, 13)\n\tfor i := 0; i \u003c b.N; i++ {\n\t\t_ = AppendCompact(uint64(i), buf[:0])\n\t}\n}\n\ntype testpair struct {\n\tdecoded, encoded string\n}\n\nvar pairs = []testpair{\n\t{\"\", \"\"},\n\t{\"f\", \"CR\"},\n\t{\"fo\", \"CSQG\"},\n\t{\"foo\", \"CSQPY\"},\n\t{\"foob\", \"CSQPYRG\"},\n\t{\"fooba\", \"CSQPYRK1\"},\n\t{\"foobar\", \"CSQPYRK1E8\"},\n\n\t{\"sure.\", \"EDTQ4S9E\"},\n\t{\"sure\", \"EDTQ4S8\"},\n\t{\"sur\", \"EDTQ4\"},\n\t{\"su\", \"EDTG\"},\n\t{\"leasure.\", \"DHJP2WVNE9JJW\"},\n\t{\"easure.\", \"CNGQ6XBJCMQ0\"},\n\t{\"asure.\", \"C5SQAWK55R\"},\n}\n\nvar bigtest = testpair{\n\t\"Twas brillig, and the slithy toves\",\n\t\"AHVP2WS0C9S6JV3CD5KJR831DSJ20X38CMG76V39EHM7J83MDXV6AWR\",\n}\n\nfunc testEqual(t *testing.T, msg string, args ...interface{}) bool {\n\tt.Helper()\n\tif args[len(args)-2] != args[len(args)-1] {\n\t\tt.Errorf(msg, args...)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc TestEncode(t *testing.T) {\n\tfor _, p := range pairs {\n\t\tgot := EncodeToString([]byte(p.decoded))\n\t\ttestEqual(t, \"Encode(%q) = %q, want %q\", p.decoded, got, p.encoded)\n\t\tdst := AppendEncode([]byte(\"lead\"), []byte(p.decoded))\n\t\ttestEqual(t, `AppendEncode(\"lead\", %q) = %q, want %q`, p.decoded, string(dst), \"lead\"+p.encoded)\n\t}\n}\n\nfunc TestEncoder(t *testing.T) {\n\tfor _, p := range pairs {\n\t\tbb := \u0026strings.Builder{}\n\t\tencoder := NewEncoder(bb)\n\t\tencoder.Write([]byte(p.decoded))\n\t\tencoder.Close()\n\t\ttestEqual(t, \"Encode(%q) = %q, want %q\", p.decoded, bb.String(), p.encoded)\n\t}\n}\n\nfunc TestEncoderBuffering(t *testing.T) {\n\tinput := []byte(bigtest.decoded)\n\tfor bs := 1; bs \u003c= 12; bs++ {\n\t\tbb := \u0026strings.Builder{}\n\t\tencoder := NewEncoder(bb)\n\t\tfor pos := 0; pos \u003c len(input); pos += bs {\n\t\t\tend := pos + bs\n\t\t\tif end \u003e len(input) {\n\t\t\t\tend = len(input)\n\t\t\t}\n\t\t\tn, err := encoder.Write(input[pos:end])\n\t\t\ttestEqual(t, \"Write(%q) gave error %v, want %v\", input[pos:end], err, error(nil))\n\t\t\ttestEqual(t, \"Write(%q) gave length %v, want %v\", input[pos:end], n, end-pos)\n\t\t}\n\t\terr := encoder.Close()\n\t\ttestEqual(t, \"Close gave error %v, want %v\", err, error(nil))\n\t\ttestEqual(t, \"Encoding/%d of %q = %q, want %q\", bs, bigtest.decoded, bb.String(), bigtest.encoded)\n\t}\n}\n\nfunc TestDecode(t *testing.T) {\n\tfor _, p := range pairs {\n\t\tdbuf := make([]byte, DecodedLen(len(p.encoded)))\n\t\tcount, err := decode(dbuf, []byte(p.encoded))\n\t\ttestEqual(t, \"Decode(%q) = error %v, want %v\", p.encoded, err, error(nil))\n\t\ttestEqual(t, \"Decode(%q) = length %v, want %v\", p.encoded, count, len(p.decoded))\n\t\ttestEqual(t, \"Decode(%q) = %q, want %q\", p.encoded, string(dbuf[0:count]), p.decoded)\n\n\t\tdbuf, err = DecodeString(p.encoded)\n\t\ttestEqual(t, \"DecodeString(%q) = error %v, want %v\", p.encoded, err, error(nil))\n\t\ttestEqual(t, \"DecodeString(%q) = %q, want %q\", p.encoded, string(dbuf), p.decoded)\n\n\t\t// XXX: https://github.com/gnolang/gno/issues/1570\n\t\tdst, err := AppendDecode(append([]byte(nil), []byte(\"lead\")...), []byte(p.encoded))\n\t\ttestEqual(t, \"AppendDecode(%q) = error %v, want %v\", p.encoded, err, error(nil))\n\t\ttestEqual(t, `AppendDecode(\"lead\", %q) = %q, want %q`, p.encoded, string(dst), \"lead\"+p.decoded)\n\n\t\tdst2, err := AppendDecode(dst[:0:len(p.decoded)], []byte(p.encoded))\n\t\ttestEqual(t, \"AppendDecode(%q) = error %v, want %v\", p.encoded, err, error(nil))\n\t\ttestEqual(t, `AppendDecode(\"\", %q) = %q, want %q`, p.encoded, string(dst2), p.decoded)\n\t\t// XXX: https://github.com/gnolang/gno/issues/1569\n\t\t// old used \u0026dst2[0] != \u0026dst[0] as a check.\n\t\tif len(dst) \u003e 0 \u0026\u0026 len(dst2) \u003e 0 \u0026\u0026 cap(dst2) != len(p.decoded) {\n\t\t\tt.Errorf(\"unexpected capacity growth: got %d, want %d\", cap(dst2), len(p.decoded))\n\t\t}\n\t}\n}\n\n// A minimal variation on strings.Reader.\n// Here, we return a io.EOF immediately on Read if the read has reached the end\n// of the reader. It's used to simplify TestDecoder.\ntype stringReader struct {\n\ts string\n\ti int64\n}\n\nfunc (r *stringReader) Read(b []byte) (n int, err error) {\n\tif r.i \u003e= int64(len(r.s)) {\n\t\treturn 0, io.EOF\n\t}\n\tn = copy(b, r.s[r.i:])\n\tr.i += int64(n)\n\tif r.i \u003e= int64(len(r.s)) {\n\t\treturn n, io.EOF\n\t}\n\treturn\n}\n\nfunc TestDecoder(t *testing.T) {\n\tfor _, p := range pairs {\n\t\tdecoder := NewDecoder(\u0026stringReader{p.encoded, 0})\n\t\tdbuf := make([]byte, DecodedLen(len(p.encoded)))\n\t\tcount, err := decoder.Read(dbuf)\n\t\tif err != nil \u0026\u0026 err != io.EOF {\n\t\t\tt.Fatal(\"Read failed\", err)\n\t\t}\n\t\ttestEqual(t, \"Read from %q = length %v, want %v\", p.encoded, count, len(p.decoded))\n\t\ttestEqual(t, \"Decoding of %q = %q, want %q\", p.encoded, string(dbuf[0:count]), p.decoded)\n\t\tif err != io.EOF {\n\t\t\t_, err = decoder.Read(dbuf)\n\t\t}\n\t\ttestEqual(t, \"Read from %q = %v, want %v\", p.encoded, err, io.EOF)\n\t}\n}\n\ntype badReader struct {\n\tdata []byte\n\terrs []error\n\tcalled int\n\tlimit int\n}\n\n// Populates p with data, returns a count of the bytes written and an\n// error. The error returned is taken from badReader.errs, with each\n// invocation of Read returning the next error in this slice, or io.EOF,\n// if all errors from the slice have already been returned. The\n// number of bytes returned is determined by the size of the input buffer\n// the test passes to decoder.Read and will be a multiple of 8, unless\n// badReader.limit is non zero.\nfunc (b *badReader) Read(p []byte) (int, error) {\n\tlim := len(p)\n\tif b.limit != 0 \u0026\u0026 b.limit \u003c lim {\n\t\tlim = b.limit\n\t}\n\tif len(b.data) \u003c lim {\n\t\tlim = len(b.data)\n\t}\n\tfor i := range p[:lim] {\n\t\tp[i] = b.data[i]\n\t}\n\tb.data = b.data[lim:]\n\terr := io.EOF\n\tif b.called \u003c len(b.errs) {\n\t\terr = b.errs[b.called]\n\t}\n\tb.called++\n\treturn lim, err\n}\n\n// TestIssue20044 tests that decoder.Read behaves correctly when the caller\n// supplied reader returns an error.\nfunc TestIssue20044(t *testing.T) {\n\tbadErr := errors.New(\"bad reader error\")\n\ttestCases := []struct {\n\t\tr badReader\n\t\tres string\n\t\terr error\n\t\tdbuflen int\n\t}{\n\t\t// Check valid input data accompanied by an error is processed and the error is propagated.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"d1jprv3fexqq4v34\"), errs: []error{badErr}},\n\t\t\tres: \"helloworld\", err: badErr,\n\t\t},\n\t\t// Check a read error accompanied by input data consisting of newlines only is propagated.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"\\n\\n\\n\\n\\n\\n\\n\\n\"), errs: []error{badErr, nil}},\n\t\t\tres: \"\", err: badErr,\n\t\t},\n\t\t// Reader will be called twice. The first time it will return 8 newline characters. The\n\t\t// second time valid base32 encoded data and an error. The data should be decoded\n\t\t// correctly and the error should be propagated.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"\\n\\n\\n\\n\\n\\n\\n\\nd1jprv3fexqq4v34\"), errs: []error{nil, badErr}},\n\t\t\tres: \"helloworld\", err: badErr, dbuflen: 8,\n\t\t},\n\t\t// Reader returns invalid input data (too short) and an error. Verify the reader\n\t\t// error is returned.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"c\"), errs: []error{badErr}},\n\t\t\tres: \"\", err: badErr,\n\t\t},\n\t\t// Reader returns invalid input data (too short) but no error. Verify io.ErrUnexpectedEOF\n\t\t// is returned.\n\t\t// NOTE(thehowl): I don't think this should applyto us?\n\t\t/* {\n\t\t\tr: badReader{data: []byte(\"c\"), errs: []error{nil}},\n\t\t\tres: \"\", err: io.ErrUnexpectedEOF,\n\t\t},*/\n\t\t// Reader returns invalid input data and an error. Verify the reader and not the\n\t\t// decoder error is returned.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"cu\"), errs: []error{badErr}},\n\t\t\tres: \"\", err: badErr,\n\t\t},\n\t\t// Reader returns valid data and io.EOF. Check data is decoded and io.EOF is propagated.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"csqpyrk1\"), errs: []error{io.EOF}},\n\t\t\tres: \"fooba\", err: io.EOF,\n\t\t},\n\t\t// Check errors are properly reported when decoder.Read is called multiple times.\n\t\t// decoder.Read will be called 8 times, badReader.Read will be called twice, returning\n\t\t// valid data both times but an error on the second call.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjwc9g\"), errs: []error{nil, badErr}},\n\t\t\tres: \"leasure.10\", err: badErr, dbuflen: 1,\n\t\t},\n\t\t// Check io.EOF is properly reported when decoder.Read is called multiple times.\n\t\t// decoder.Read will be called 8 times, badReader.Read will be called twice, returning\n\t\t// valid data both times but io.EOF on the second call.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjw\"), errs: []error{nil, io.EOF}},\n\t\t\tres: \"leasure.\", err: io.EOF, dbuflen: 1,\n\t\t},\n\t\t// The following two test cases check that errors are propagated correctly when more than\n\t\t// 8 bytes are read at a time.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjw\"), errs: []error{io.EOF}},\n\t\t\tres: \"leasure.\", err: io.EOF, dbuflen: 11,\n\t\t},\n\t\t{\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjwc9g\"), errs: []error{badErr}},\n\t\t\tres: \"leasure.10\", err: badErr, dbuflen: 11,\n\t\t},\n\t\t// Check that errors are correctly propagated when the reader returns valid bytes in\n\t\t// groups that are not divisible by 8. The first read will return 11 bytes and no\n\t\t// error. The second will return 7 and an error. The data should be decoded correctly\n\t\t// and the error should be propagated.\n\t\t// NOTE(thehowl): again, this is on the assumption that this is padded, and it's not.\n\t\t/* {\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjw\"), errs: []error{nil, badErr}, limit: 11},\n\t\t\tres: \"leasure.\", err: badErr,\n\t\t}, */\n\t}\n\n\tfor idx, tc := range testCases {\n\t\tt.Run(fmt.Sprintf(\"%d-%s\", idx, string(tc.res)), func(t *testing.T) {\n\t\t\tinput := tc.r.data\n\t\t\tdecoder := NewDecoder(\u0026tc.r)\n\t\t\tvar dbuflen int\n\t\t\tif tc.dbuflen \u003e 0 {\n\t\t\t\tdbuflen = tc.dbuflen\n\t\t\t} else {\n\t\t\t\tdbuflen = DecodedLen(len(input))\n\t\t\t}\n\t\t\tdbuf := make([]byte, dbuflen)\n\t\t\tvar err error\n\t\t\tvar res []byte\n\t\t\tfor err == nil {\n\t\t\t\tvar n int\n\t\t\t\tn, err = decoder.Read(dbuf)\n\t\t\t\tif n \u003e 0 {\n\t\t\t\t\tres = append(res, dbuf[:n]...)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttestEqual(t, \"Decoding of %q = %q, want %q\", string(input), string(res), tc.res)\n\t\t\ttestEqual(t, \"Decoding of %q err = %v, expected %v\", string(input), err, tc.err)\n\t\t})\n\t}\n}\n\n// TestDecoderError verifies decode errors are propagated when there are no read\n// errors.\nfunc TestDecoderError(t *testing.T) {\n\tfor _, readErr := range []error{io.EOF, nil} {\n\t\tinput := \"ucsqpyrk1u\"\n\t\tdbuf := make([]byte, DecodedLen(len(input)))\n\t\tbr := badReader{data: []byte(input), errs: []error{readErr}}\n\t\tdecoder := NewDecoder(\u0026br)\n\t\tn, err := decoder.Read(dbuf)\n\t\ttestEqual(t, \"Read after EOF, n = %d, expected %d\", n, 0)\n\t\tif _, ok := err.(CorruptInputError); !ok {\n\t\t\tt.Errorf(\"Corrupt input error expected. Found %T\", err)\n\t\t}\n\t}\n}\n\n// TestReaderEOF ensures decoder.Read behaves correctly when input data is\n// exhausted.\nfunc TestReaderEOF(t *testing.T) {\n\tfor _, readErr := range []error{io.EOF, nil} {\n\t\tinput := \"MZXW6YTB\"\n\t\tbr := badReader{data: []byte(input), errs: []error{nil, readErr}}\n\t\tdecoder := NewDecoder(\u0026br)\n\t\tdbuf := make([]byte, DecodedLen(len(input)))\n\t\tn, err := decoder.Read(dbuf)\n\t\ttestEqual(t, \"Decoding of %q err = %v, expected %v\", input, err, error(nil))\n\t\tn, err = decoder.Read(dbuf)\n\t\ttestEqual(t, \"Read after EOF, n = %d, expected %d\", n, 0)\n\t\ttestEqual(t, \"Read after EOF, err = %v, expected %v\", err, io.EOF)\n\t\tn, err = decoder.Read(dbuf)\n\t\ttestEqual(t, \"Read after EOF, n = %d, expected %d\", n, 0)\n\t\ttestEqual(t, \"Read after EOF, err = %v, expected %v\", err, io.EOF)\n\t}\n}\n\nfunc TestDecoderBuffering(t *testing.T) {\n\tfor bs := 1; bs \u003c= 12; bs++ {\n\t\tdecoder := NewDecoder(strings.NewReader(bigtest.encoded))\n\t\tbuf := make([]byte, len(bigtest.decoded)+12)\n\t\tvar total int\n\t\tvar n int\n\t\tvar err error\n\t\tfor total = 0; total \u003c len(bigtest.decoded) \u0026\u0026 err == nil; {\n\t\t\tn, err = decoder.Read(buf[total : total+bs])\n\t\t\ttotal += n\n\t\t}\n\t\tif err != nil \u0026\u0026 err != io.EOF {\n\t\t\tt.Errorf(\"Read from %q at pos %d = %d, unexpected error %v\", bigtest.encoded, total, n, err)\n\t\t}\n\t\ttestEqual(t, \"Decoding/%d of %q = %q, want %q\", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded)\n\t}\n}\n\nfunc TestDecodeCorrupt(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput string\n\t\toffset int // -1 means no corruption.\n\t}{\n\t\t{\"\", -1},\n\t\t{\"iIoOlL\", -1},\n\t\t{\"!!!!\", 0},\n\t\t{\"uxp10\", 0},\n\t\t{\"x===\", 1},\n\t\t{\"AA=A====\", 2},\n\t\t{\"AAA=AAAA\", 3},\n\t\t// Much fewer cases compared to Go as there are much fewer cases where input\n\t\t// can be \"corrupted\".\n\t}\n\tfor _, tc := range testCases {\n\t\tdbuf := make([]byte, DecodedLen(len(tc.input)))\n\t\t_, err := Decode(dbuf, []byte(tc.input))\n\t\tif tc.offset == -1 {\n\t\t\tif err != nil {\n\t\t\t\tt.Error(\"Decoder wrongly detected corruption in\", tc.input)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tswitch err := err.(type) {\n\t\tcase CorruptInputError:\n\t\t\ttestEqual(t, \"Corruption in %q at offset %v, want %v\", tc.input, int(err), tc.offset)\n\t\tdefault:\n\t\t\tt.Error(\"Decoder failed to detect corruption in\", tc)\n\t\t}\n\t}\n}\n\nfunc TestBig(t *testing.T) {\n\tn := 3*1000 + 1\n\traw := make([]byte, n)\n\tconst alpha = \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\tfor i := 0; i \u003c n; i++ {\n\t\traw[i] = alpha[i%len(alpha)]\n\t}\n\tencoded := new(bytes.Buffer)\n\tw := NewEncoder(encoded)\n\tnn, err := w.Write(raw)\n\tif nn != n || err != nil {\n\t\tt.Fatalf(\"Encoder.Write(raw) = %d, %v want %d, nil\", nn, err, n)\n\t}\n\terr = w.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"Encoder.Close() = %v want nil\", err)\n\t}\n\tdecoded, err := io.ReadAll(NewDecoder(encoded))\n\tif err != nil {\n\t\tt.Fatalf(\"io.ReadAll(NewDecoder(...)): %v\", err)\n\t}\n\n\tif !bytes.Equal(raw, decoded) {\n\t\tvar i int\n\t\tfor i = 0; i \u003c len(decoded) \u0026\u0026 i \u003c len(raw); i++ {\n\t\t\tif decoded[i] != raw[i] {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tt.Errorf(\"Decode(Encode(%d-byte string)) failed at offset %d\", n, i)\n\t}\n}\n\nfunc testStringEncoding(t *testing.T, expected string, examples []string) {\n\tfor _, e := range examples {\n\t\tbuf, err := DecodeString(e)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Decode(%q) failed: %v\", e, err)\n\t\t\tcontinue\n\t\t}\n\t\tif s := string(buf); s != expected {\n\t\t\tt.Errorf(\"Decode(%q) = %q, want %q\", e, s, expected)\n\t\t}\n\t}\n}\n\nfunc TestNewLineCharacters(t *testing.T) {\n\t// Each of these should decode to the string \"sure\", without errors.\n\texamples := []string{\n\t\t\"EDTQ4S8\",\n\t\t\"EDTQ4S8\\r\",\n\t\t\"EDTQ4S8\\n\",\n\t\t\"EDTQ4S8\\r\\n\",\n\t\t\"EDTQ4S\\r\\n8\",\n\t\t\"EDT\\rQ4S\\n8\",\n\t\t\"edt\\nq4s\\r8\",\n\t\t\"edt\\nq4s8\",\n\t\t\"EDTQ4S\\n8\",\n\t}\n\ttestStringEncoding(t, \"sure\", examples)\n}\n\nfunc BenchmarkEncode(b *testing.B) {\n\tdata := make([]byte, 8192)\n\tbuf := make([]byte, EncodedLen(len(data)))\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tEncode(buf, data)\n\t}\n}\n\nfunc BenchmarkEncodeToString(b *testing.B) {\n\tdata := make([]byte, 8192)\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tEncodeToString(data)\n\t}\n}\n\nfunc BenchmarkDecode(b *testing.B) {\n\tdata := make([]byte, EncodedLen(8192))\n\tEncode(data, make([]byte, 8192))\n\tbuf := make([]byte, 8192)\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tDecode(buf, data)\n\t}\n}\n\nfunc BenchmarkDecodeString(b *testing.B) {\n\tdata := EncodeToString(make([]byte, 8192))\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tDecodeString(data)\n\t}\n}\n\n/* TODO: rewrite without using goroutines\nfunc TestBufferedDecodingSameError(t *testing.T) {\n\ttestcases := []struct {\n\t\tprefix string\n\t\tchunkCombinations [][]string\n\t\texpected error\n\t}{\n\t\t// Normal case, this is valid input\n\t\t{\"helloworld\", [][]string{\n\t\t\t{\"D1JP\", \"RV3F\", \"EXQQ\", \"4V34\"},\n\t\t\t{\"D1JPRV3FEXQQ4V34\"},\n\t\t\t{\"D1J\", \"PRV\", \"3FE\", \"XQQ\", \"4V3\", \"4\"},\n\t\t\t{\"D1JPRV3FEXQQ4V\", \"34\"},\n\t\t}, nil},\n\n\t\t// Normal case, this is valid input\n\t\t{\"fooba\", [][]string{\n\t\t\t{\"CSQPYRK1\"},\n\t\t\t{\"CSQPYRK\", \"1\"},\n\t\t\t{\"CSQPYR\", \"K1\"},\n\t\t\t{\"CSQPY\", \"RK1\"},\n\t\t\t{\"CSQPY\", \"RK\", \"1\"},\n\t\t\t{\"CSQPY\", \"RK1\"},\n\t\t\t{\"CSQP\", \"YR\", \"K1\"},\n\t\t}, nil},\n\n\t\t// NOTE: many test cases have been removed as we don't return ErrUnexpectedEOF.\n\t}\n\n\tfor _, testcase := range testcases {\n\t\tfor _, chunks := range testcase.chunkCombinations {\n\t\t\tpr, pw := io.Pipe()\n\n\t\t\t// Write the encoded chunks into the pipe\n\t\t\tgo func() {\n\t\t\t\tfor _, chunk := range chunks {\n\t\t\t\t\tpw.Write([]byte(chunk))\n\t\t\t\t}\n\t\t\t\tpw.Close()\n\t\t\t}()\n\n\t\t\tdecoder := NewDecoder(pr)\n\t\t\tback, err := io.ReadAll(decoder)\n\n\t\t\tif err != testcase.expected {\n\t\t\t\tt.Errorf(\"Expected %v, got %v; case %s %+v\", testcase.expected, err, testcase.prefix, chunks)\n\t\t\t}\n\t\t\tif testcase.expected == nil {\n\t\t\t\ttestEqual(t, \"Decode from NewDecoder(chunkReader(%v)) = %q, want %q\", chunks, string(back), testcase.prefix)\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n\nfunc TestEncodedLen(t *testing.T) {\n\ttype test struct {\n\t\tn int\n\t\twant int64\n\t}\n\ttests := []test{\n\t\t{0, 0},\n\t\t{1, 2},\n\t\t{2, 4},\n\t\t{3, 5},\n\t\t{4, 7},\n\t\t{5, 8},\n\t\t{6, 10},\n\t\t{7, 12},\n\t\t{10, 16},\n\t\t{11, 18},\n\t}\n\t// check overflow\n\ttests = append(tests, test{(math.MaxInt-4)/8 + 1, 1844674407370955162})\n\ttests = append(tests, test{math.MaxInt/8*5 + 4, math.MaxInt})\n\tfor _, tt := range tests {\n\t\tif got := EncodedLen(tt.n); int64(got) != tt.want {\n\t\t\tt.Errorf(\"EncodedLen(%d): got %d, want %d\", tt.n, got, tt.want)\n\t\t}\n\t}\n}\n\nfunc TestDecodedLen(t *testing.T) {\n\ttype test struct {\n\t\tn int\n\t\twant int64\n\t}\n\ttests := []test{\n\t\t{0, 0},\n\t\t{2, 1},\n\t\t{4, 2},\n\t\t{5, 3},\n\t\t{7, 4},\n\t\t{8, 5},\n\t\t{10, 6},\n\t\t{12, 7},\n\t\t{16, 10},\n\t\t{18, 11},\n\t}\n\t// check overflow\n\ttests = append(tests, test{math.MaxInt/5 + 1, 1152921504606846976})\n\ttests = append(tests, test{math.MaxInt, 5764607523034234879})\n\tfor _, tt := range tests {\n\t\tif got := DecodedLen(tt.n); int64(got) != tt.want {\n\t\t\tt.Errorf(\"DecodedLen(%d): got %d, want %d\", tt.n, got, tt.want)\n\t\t}\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/cford32\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "context", + "path": "gno.land/p/demo/context", + "files": [ + { + "name": "context.gno", + "body": "// Package context provides a minimal implementation of Go context with support\n// for Value and WithValue.\n//\n// Adapted from https://github.com/golang/go/tree/master/src/context/.\n// Copyright 2016 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\npackage context\n\ntype Context interface {\n\t// Value returns the value associated with this context for key, or nil\n\t// if no value is associated with key.\n\tValue(key interface{}) interface{}\n}\n\n// Empty returns a non-nil, empty context, similar with context.Background and\n// context.TODO in Go.\nfunc Empty() Context {\n\treturn \u0026emptyCtx{}\n}\n\ntype emptyCtx struct{}\n\nfunc (ctx emptyCtx) Value(key interface{}) interface{} {\n\treturn nil\n}\n\nfunc (ctx emptyCtx) String() string {\n\treturn \"context.Empty\"\n}\n\ntype valueCtx struct {\n\tparent Context\n\tkey, val interface{}\n}\n\nfunc (ctx *valueCtx) Value(key interface{}) interface{} {\n\tif ctx.key == key {\n\t\treturn ctx.val\n\t}\n\treturn ctx.parent.Value(key)\n}\n\nfunc stringify(v interface{}) string {\n\tswitch s := v.(type) {\n\tcase stringer:\n\t\treturn s.String()\n\tcase string:\n\t\treturn s\n\t}\n\treturn \"non-stringer\"\n}\n\ntype stringer interface {\n\tString() string\n}\n\nfunc (c *valueCtx) String() string {\n\treturn stringify(c.parent) + \".WithValue(\" +\n\t\tstringify(c.key) + \", \" +\n\t\tstringify(c.val) + \")\"\n}\n\n// WithValue returns a copy of parent in which the value associated with key is\n// val.\nfunc WithValue(parent Context, key, val interface{}) Context {\n\tif key == nil {\n\t\tpanic(\"nil key\")\n\t}\n\t// XXX: if !reflect.TypeOf(key).Comparable() { panic(\"key is not comparable\") }\n\treturn \u0026valueCtx{parent, key, val}\n}\n" + }, + { + "name": "context_test.gno", + "body": "package context\n\nimport \"testing\"\n\nfunc TestContextExample(t *testing.T) {\n\ttype favContextKey string\n\n\tk := favContextKey(\"language\")\n\tctx := WithValue(Empty(), k, \"Gno\")\n\n\tif v := ctx.Value(k); v != nil {\n\t\tif string(v) != \"Gno\" {\n\t\t\tt.Errorf(\"language value should be Gno, but is %s\", v)\n\t\t}\n\t} else {\n\t\tt.Errorf(\"language key value was not found\")\n\t}\n\n\tif v := ctx.Value(favContextKey(\"color\")); v != nil {\n\t\tt.Errorf(\"color key was found\")\n\t}\n}\n\n// otherContext is a Context that's not one of the types defined in context.go.\n// This lets us test code paths that differ based on the underlying type of the\n// Context.\ntype otherContext struct {\n\tContext\n}\n\ntype (\n\tkey1 int\n\tkey2 int\n)\n\n// func (k key2) String() string { return fmt.Sprintf(\"%[1]T(%[1]d)\", k) }\n\nvar (\n\tk1 = key1(1)\n\tk2 = key2(1) // same int as k1, different type\n\tk3 = key2(3) // same type as k2, different int\n)\n\nfunc TestValues(t *testing.T) {\n\tcheck := func(c Context, nm, v1, v2, v3 string) {\n\t\tif v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {\n\t\t\tt.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)\n\t\t}\n\t\tif v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {\n\t\t\tt.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)\n\t\t}\n\t\tif v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {\n\t\t\tt.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)\n\t\t}\n\t}\n\n\tc0 := Empty()\n\tcheck(c0, \"c0\", \"\", \"\", \"\")\n\n\tt.Skip() // XXX: depends on https://github.com/gnolang/gno/issues/2386\n\n\tc1 := WithValue(Empty(), k1, \"c1k1\")\n\tcheck(c1, \"c1\", \"c1k1\", \"\", \"\")\n\n\t/*if got, want := c1.String(), `context.Empty.WithValue(context_test.key1, c1k1)`; got != want {\n\t\tt.Errorf(\"c.String() = %q want %q\", got, want)\n\t}*/\n\n\tc2 := WithValue(c1, k2, \"c2k2\")\n\tcheck(c2, \"c2\", \"c1k1\", \"c2k2\", \"\")\n\n\t/*if got, want := fmt.Sprint(c2), `context.Empty.WithValue(context_test.key1, c1k1).WithValue(context_test.key2(1), c2k2)`; got != want {\n\t\tt.Errorf(\"c.String() = %q want %q\", got, want)\n\t}*/\n\n\tc3 := WithValue(c2, k3, \"c3k3\")\n\tcheck(c3, \"c2\", \"c1k1\", \"c2k2\", \"c3k3\")\n\n\tc4 := WithValue(c3, k1, nil)\n\tcheck(c4, \"c4\", \"\", \"c2k2\", \"c3k3\")\n\n\to0 := otherContext{Empty()}\n\tcheck(o0, \"o0\", \"\", \"\", \"\")\n\n\to1 := otherContext{WithValue(Empty(), k1, \"c1k1\")}\n\tcheck(o1, \"o1\", \"c1k1\", \"\", \"\")\n\n\to2 := WithValue(o1, k2, \"o2k2\")\n\tcheck(o2, \"o2\", \"c1k1\", \"o2k2\", \"\")\n\n\to3 := otherContext{c4}\n\tcheck(o3, \"o3\", \"\", \"c2k2\", \"c3k3\")\n\n\to4 := WithValue(o3, k3, nil)\n\tcheck(o4, \"o4\", \"\", \"c2k2\", \"\")\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/context\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "dom", + "path": "gno.land/p/demo/dom", + "files": [ + { + "name": "dom.gno", + "body": "// XXX This is only used for testing in ./tests.\n// Otherwise this package is deprecated.\n// TODO: replace with a package that is supported, and delete this.\n\npackage dom\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\ntype Plot struct {\n\tName string\n\tPosts avl.Tree // postsCtr -\u003e *Post\n\tPostsCtr int\n}\n\nfunc (plot *Plot) AddPost(title string, body string) {\n\tctr := plot.PostsCtr\n\tplot.PostsCtr++\n\tkey := strconv.Itoa(ctr)\n\tpost := \u0026Post{\n\t\tTitle: title,\n\t\tBody: body,\n\t}\n\tplot.Posts.Set(key, post)\n}\n\nfunc (plot *Plot) String() string {\n\tstr := \"# [plot] \" + plot.Name + \"\\n\"\n\tif plot.Posts.Size() \u003e 0 {\n\t\tplot.Posts.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tstr += \"\\n\"\n\t\t\tstr += value.(*Post).String()\n\t\t\treturn false\n\t\t})\n\t}\n\treturn str\n}\n\ntype Post struct {\n\tTitle string\n\tBody string\n\tComments avl.Tree\n}\n\nfunc (post *Post) String() string {\n\tstr := \"## \" + post.Title + \"\\n\"\n\tstr += \"\"\n\tstr += post.Body\n\tif post.Comments.Size() \u003e 0 {\n\t\tpost.Comments.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tstr += \"\\n\"\n\t\t\tstr += value.(*Comment).String()\n\t\t\treturn false\n\t\t})\n\t}\n\treturn str\n}\n\ntype Comment struct {\n\tCreator string\n\tBody string\n}\n\nfunc (cmm Comment) String() string {\n\treturn cmm.Body + \" - @\" + cmm.Creator + \"\\n\"\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/dom\n\nrequire gno.land/p/demo/avl v0.0.0-latest\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "flow", + "path": "gno.land/p/demo/flow", + "files": [ + { + "name": "LICENSE", + "body": "https://github.com/mxk/go-flowrate/blob/master/LICENSE\nBSD 3-Clause \"New\" or \"Revised\" License\n\nCopyright (c) 2014 The Go-FlowRate Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in the\n documentation and/or other materials provided with the\n distribution.\n\n * Neither the name of the go-flowrate project nor the names of its\n contributors may be used to endorse or promote products derived\n from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, + { + "name": "README.md", + "body": "Data Flow Rate Control\n======================\n\nTo download and install this package run:\n\ngo get github.com/mxk/go-flowrate/flowrate\n\nThe documentation is available at:\n\nhttp://godoc.org/github.com/mxk/go-flowrate/flowrate\n" + }, + { + "name": "flow.gno", + "body": "//\n// Written by Maxim Khitrov (November 2012)\n//\n// XXX modified to disable blocking, time.Sleep().\n\n// Package flow provides the tools for monitoring and limiting the flow rate\n// of an arbitrary data stream.\npackage flow\n\nimport (\n\t\"math\"\n\t// \"sync\"\n\t\"time\"\n)\n\n// Monitor monitors and limits the transfer rate of a data stream.\ntype Monitor struct {\n\t// mu sync.Mutex // Mutex guarding access to all internal fields\n\tactive bool // Flag indicating an active transfer\n\tstart time.Duration // Transfer start time (clock() value)\n\tbytes int64 // Total number of bytes transferred\n\tsamples int64 // Total number of samples taken\n\n\trSample float64 // Most recent transfer rate sample (bytes per second)\n\trEMA float64 // Exponential moving average of rSample\n\trPeak float64 // Peak transfer rate (max of all rSamples)\n\trWindow float64 // rEMA window (seconds)\n\n\tsBytes int64 // Number of bytes transferred since sLast\n\tsLast time.Duration // Most recent sample time (stop time when inactive)\n\tsRate time.Duration // Sampling rate\n\n\ttBytes int64 // Number of bytes expected in the current transfer\n\ttLast time.Duration // Time of the most recent transfer of at least 1 byte\n}\n\n// New creates a new flow control monitor. Instantaneous transfer rate is\n// measured and updated for each sampleRate interval. windowSize determines the\n// weight of each sample in the exponential moving average (EMA) calculation.\n// The exact formulas are:\n//\n//\tsampleTime = currentTime - prevSampleTime\n//\tsampleRate = byteCount / sampleTime\n//\tweight = 1 - exp(-sampleTime/windowSize)\n//\tnewRate = weight*sampleRate + (1-weight)*oldRate\n//\n// The default values for sampleRate and windowSize (if \u003c= 0) are 100ms and 1s,\n// respectively.\nfunc New(sampleRate, windowSize time.Duration) *Monitor {\n\tif sampleRate = clockRound(sampleRate); sampleRate \u003c= 0 {\n\t\tsampleRate = 5 * clockRate\n\t}\n\tif windowSize \u003c= 0 {\n\t\twindowSize = 1 * time.Second\n\t}\n\tnow := clock()\n\treturn \u0026Monitor{\n\t\tactive: true,\n\t\tstart: now,\n\t\trWindow: windowSize.Seconds(),\n\t\tsLast: now,\n\t\tsRate: sampleRate,\n\t\ttLast: now,\n\t}\n}\n\n// Update records the transfer of n bytes and returns n. It should be called\n// after each Read/Write operation, even if n is 0.\nfunc (m *Monitor) Update(n int) int {\n\t// m.mu.Lock()\n\tm.update(n)\n\t// m.mu.Unlock()\n\treturn n\n}\n\n// Hack to set the current rEMA.\nfunc (m *Monitor) SetREMA(rEMA float64) {\n\t// m.mu.Lock()\n\tm.rEMA = rEMA\n\tm.samples++\n\t// m.mu.Unlock()\n}\n\n// IO is a convenience method intended to wrap io.Reader and io.Writer method\n// execution. It calls m.Update(n) and then returns (n, err) unmodified.\nfunc (m *Monitor) IO(n int, err error) (int, error) {\n\treturn m.Update(n), err\n}\n\n// Done marks the transfer as finished and prevents any further updates or\n// limiting. Instantaneous and current transfer rates drop to 0. Update, IO, and\n// Limit methods become NOOPs. It returns the total number of bytes transferred.\nfunc (m *Monitor) Done() int64 {\n\t// m.mu.Lock()\n\tif now := m.update(0); m.sBytes \u003e 0 {\n\t\tm.reset(now)\n\t}\n\tm.active = false\n\tm.tLast = 0\n\tn := m.bytes\n\t// m.mu.Unlock()\n\treturn n\n}\n\n// timeRemLimit is the maximum Status.TimeRem value.\nconst timeRemLimit = 999*time.Hour + 59*time.Minute + 59*time.Second\n\n// Status represents the current Monitor status. All transfer rates are in bytes\n// per second rounded to the nearest byte.\ntype Status struct {\n\tActive bool // Flag indicating an active transfer\n\tStart time.Time // Transfer start time\n\tDuration time.Duration // Time period covered by the statistics\n\tIdle time.Duration // Time since the last transfer of at least 1 byte\n\tBytes int64 // Total number of bytes transferred\n\tSamples int64 // Total number of samples taken\n\tInstRate int64 // Instantaneous transfer rate\n\tCurRate int64 // Current transfer rate (EMA of InstRate)\n\tAvgRate int64 // Average transfer rate (Bytes / Duration)\n\tPeakRate int64 // Maximum instantaneous transfer rate\n\tBytesRem int64 // Number of bytes remaining in the transfer\n\tTimeRem time.Duration // Estimated time to completion\n\tProgress Percent // Overall transfer progress\n}\n\nfunc (s Status) String() string {\n\treturn \"STATUS{}\"\n}\n\n// Status returns current transfer status information. The returned value\n// becomes static after a call to Done.\nfunc (m *Monitor) Status() Status {\n\t// m.mu.Lock()\n\tnow := m.update(0)\n\ts := Status{\n\t\tActive: m.active,\n\t\tStart: clockToTime(m.start),\n\t\tDuration: m.sLast - m.start,\n\t\tIdle: now - m.tLast,\n\t\tBytes: m.bytes,\n\t\tSamples: m.samples,\n\t\tPeakRate: round(m.rPeak),\n\t\tBytesRem: m.tBytes - m.bytes,\n\t\tProgress: percentOf(float64(m.bytes), float64(m.tBytes)),\n\t}\n\tif s.BytesRem \u003c 0 {\n\t\ts.BytesRem = 0\n\t}\n\tif s.Duration \u003e 0 {\n\t\trAvg := float64(s.Bytes) / s.Duration.Seconds()\n\t\ts.AvgRate = round(rAvg)\n\t\tif s.Active {\n\t\t\ts.InstRate = round(m.rSample)\n\t\t\ts.CurRate = round(m.rEMA)\n\t\t\tif s.BytesRem \u003e 0 {\n\t\t\t\tif tRate := 0.8*m.rEMA + 0.2*rAvg; tRate \u003e 0 {\n\t\t\t\t\tns := float64(s.BytesRem) / tRate * 1e9\n\t\t\t\t\tif ns \u003e float64(timeRemLimit) {\n\t\t\t\t\t\tns = float64(timeRemLimit)\n\t\t\t\t\t}\n\t\t\t\t\ts.TimeRem = clockRound(time.Duration(ns))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// m.mu.Unlock()\n\treturn s\n}\n\n// Limit restricts the instantaneous (per-sample) data flow to rate bytes per\n// second. It returns the maximum number of bytes (0 \u003c= n \u003c= want) that may be\n// transferred immediately without exceeding the limit. If block == true, the\n// call blocks until n \u003e 0. want is returned unmodified if want \u003c 1, rate \u003c 1,\n// or the transfer is inactive (after a call to Done).\n//\n// At least one byte is always allowed to be transferred in any given sampling\n// period. Thus, if the sampling rate is 100ms, the lowest achievable flow rate\n// is 10 bytes per second.\n//\n// For usage examples, see the implementation of Reader and Writer in io.go.\nfunc (m *Monitor) Limit(want int, rate int64, block bool) (n int) {\n\tif block {\n\t\tpanic(\"blocking not yet supported\")\n\t}\n\tif want \u003c 1 || rate \u003c 1 {\n\t\treturn want\n\t}\n\t// m.mu.Lock()\n\n\t// Determine the maximum number of bytes that can be sent in one sample\n\tlimit := round(float64(rate) * m.sRate.Seconds())\n\tif limit \u003c= 0 {\n\t\tlimit = 1\n\t}\n\n\t_ = m.update(0)\n\t/* XXX\n\t// If block == true, wait until m.sBytes \u003c limit\n\tif now := m.update(0); block {\n\t\tfor m.sBytes \u003e= limit \u0026\u0026 m.active {\n\t\t\tnow = m.waitNextSample(now)\n\t\t}\n\t}\n\t*/\n\n\t// Make limit \u003c= want (unlimited if the transfer is no longer active)\n\tif limit -= m.sBytes; limit \u003e int64(want) || !m.active {\n\t\tlimit = int64(want)\n\t}\n\t// m.mu.Unlock()\n\n\tif limit \u003c 0 {\n\t\tlimit = 0\n\t}\n\treturn int(limit)\n}\n\n// SetTransferSize specifies the total size of the data transfer, which allows\n// the Monitor to calculate the overall progress and time to completion.\nfunc (m *Monitor) SetTransferSize(bytes int64) {\n\tif bytes \u003c 0 {\n\t\tbytes = 0\n\t}\n\t// m.mu.Lock()\n\tm.tBytes = bytes\n\t// m.mu.Unlock()\n}\n\n// update accumulates the transferred byte count for the current sample until\n// clock() - m.sLast \u003e= m.sRate. The monitor status is updated once the current\n// sample is done.\nfunc (m *Monitor) update(n int) (now time.Duration) {\n\tif !m.active {\n\t\treturn\n\t}\n\tif now = clock(); n \u003e 0 {\n\t\tm.tLast = now\n\t}\n\tm.sBytes += int64(n)\n\tif sTime := now - m.sLast; sTime \u003e= m.sRate {\n\t\tt := sTime.Seconds()\n\t\tif m.rSample = float64(m.sBytes) / t; m.rSample \u003e m.rPeak {\n\t\t\tm.rPeak = m.rSample\n\t\t}\n\n\t\t// Exponential moving average using a method similar to *nix load\n\t\t// average calculation. Longer sampling periods carry greater weight.\n\t\tif m.samples \u003e 0 {\n\t\t\tw := math.Exp(-t / m.rWindow)\n\t\t\tm.rEMA = m.rSample + w*(m.rEMA-m.rSample)\n\t\t} else {\n\t\t\tm.rEMA = m.rSample\n\t\t}\n\t\tm.reset(now)\n\t}\n\treturn\n}\n\n// reset clears the current sample state in preparation for the next sample.\nfunc (m *Monitor) reset(sampleTime time.Duration) {\n\tm.bytes += m.sBytes\n\tm.samples++\n\tm.sBytes = 0\n\tm.sLast = sampleTime\n}\n\n/*\n// waitNextSample sleeps for the remainder of the current sample. The lock is\n// released and reacquired during the actual sleep period, so it's possible for\n// the transfer to be inactive when this method returns.\nfunc (m *Monitor) waitNextSample(now time.Duration) time.Duration {\n\tconst minWait = 5 * time.Millisecond\n\tcurrent := m.sLast\n\n\t// sleep until the last sample time changes (ideally, just one iteration)\n\tfor m.sLast == current \u0026\u0026 m.active {\n\t\td := current + m.sRate - now\n\t\t// m.mu.Unlock()\n\t\tif d \u003c minWait {\n\t\t\td = minWait\n\t\t}\n\t\ttime.Sleep(d)\n\t\t// m.mu.Lock()\n\t\tnow = m.update(0)\n\t}\n\treturn now\n}\n*/\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/flow\n" + }, + { + "name": "io.gno", + "body": "//\n// Written by Maxim Khitrov (November 2012)\n//\n\npackage flow\n\nimport (\n\t\"errors\"\n\t\"io\"\n)\n\n// ErrLimit is returned by the Writer when a non-blocking write is short due to\n// the transfer rate limit.\nvar ErrLimit = errors.New(\"flowrate: flow rate limit exceeded\")\n\n// Limiter is implemented by the Reader and Writer to provide a consistent\n// interface for monitoring and controlling data transfer.\ntype Limiter interface {\n\tDone() int64\n\tStatus() Status\n\tSetTransferSize(bytes int64)\n\tSetLimit(new int64) (old int64)\n\tSetBlocking(new bool) (old bool)\n}\n\n// Reader implements io.ReadCloser with a restriction on the rate of data\n// transfer.\ntype Reader struct {\n\tio.Reader // Data source\n\t*Monitor // Flow control monitor\n\n\tlimit int64 // Rate limit in bytes per second (unlimited when \u003c= 0)\n\tblock bool // What to do when no new bytes can be read due to the limit\n}\n\n// NewReader restricts all Read operations on r to limit bytes per second.\nfunc NewReader(r io.Reader, limit int64) *Reader {\n\treturn \u0026Reader{r, New(0, 0), limit, false} // XXX default false\n}\n\n// Read reads up to len(p) bytes into p without exceeding the current transfer\n// rate limit. It returns (0, nil) immediately if r is non-blocking and no new\n// bytes can be read at this time.\nfunc (r *Reader) Read(p []byte) (n int, err error) {\n\tp = p[:r.Limit(len(p), r.limit, r.block)]\n\tif len(p) \u003e 0 {\n\t\tn, err = r.IO(r.Reader.Read(p))\n\t}\n\treturn\n}\n\n// SetLimit changes the transfer rate limit to new bytes per second and returns\n// the previous setting.\nfunc (r *Reader) SetLimit(new int64) (old int64) {\n\told, r.limit = r.limit, new\n\treturn\n}\n\n// SetBlocking changes the blocking behavior and returns the previous setting. A\n// Read call on a non-blocking reader returns immediately if no additional bytes\n// may be read at this time due to the rate limit.\nfunc (r *Reader) SetBlocking(new bool) (old bool) {\n\tif new == true {\n\t\tpanic(\"blocking not yet supported\")\n\t}\n\told, r.block = r.block, new\n\treturn\n}\n\n// Close closes the underlying reader if it implements the io.Closer interface.\nfunc (r *Reader) Close() error {\n\tdefer r.Done()\n\tif c, ok := r.Reader.(io.Closer); ok {\n\t\treturn c.Close()\n\t}\n\treturn nil\n}\n\n// Writer implements io.WriteCloser with a restriction on the rate of data\n// transfer.\ntype Writer struct {\n\tio.Writer // Data destination\n\t*Monitor // Flow control monitor\n\n\tlimit int64 // Rate limit in bytes per second (unlimited when \u003c= 0)\n\tblock bool // What to do when no new bytes can be written due to the limit\n}\n\n// NewWriter restricts all Write operations on w to limit bytes per second. The\n// transfer rate and the default blocking behavior (true) can be changed\n// directly on the returned *Writer.\nfunc NewWriter(w io.Writer, limit int64) *Writer {\n\treturn \u0026Writer{w, New(0, 0), limit, false} // XXX default false\n}\n\n// Write writes len(p) bytes from p to the underlying data stream without\n// exceeding the current transfer rate limit. It returns (n, ErrLimit) if w is\n// non-blocking and no additional bytes can be written at this time.\nfunc (w *Writer) Write(p []byte) (n int, err error) {\n\tvar c int\n\tfor len(p) \u003e 0 \u0026\u0026 err == nil {\n\t\ts := p[:w.Limit(len(p), w.limit, w.block)]\n\t\tif len(s) \u003e 0 {\n\t\t\tc, err = w.IO(w.Writer.Write(s))\n\t\t} else {\n\t\t\treturn n, ErrLimit\n\t\t}\n\t\tp = p[c:]\n\t\tn += c\n\t}\n\treturn\n}\n\n// SetLimit changes the transfer rate limit to new bytes per second and returns\n// the previous setting.\nfunc (w *Writer) SetLimit(new int64) (old int64) {\n\told, w.limit = w.limit, new\n\treturn\n}\n\n// SetBlocking changes the blocking behavior and returns the previous setting. A\n// Write call on a non-blocking writer returns as soon as no additional bytes\n// may be written at this time due to the rate limit.\nfunc (w *Writer) SetBlocking(new bool) (old bool) {\n\told, w.block = w.block, new\n\treturn\n}\n\n// Close closes the underlying writer if it implements the io.Closer interface.\nfunc (w *Writer) Close() error {\n\tdefer w.Done()\n\tif c, ok := w.Writer.(io.Closer); ok {\n\t\treturn c.Close()\n\t}\n\treturn nil\n}\n" + }, + { + "name": "io_test.gno", + "body": "//\n// Written by Maxim Khitrov (November 2012)\n//\n\npackage flow\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"time\"\n\n\tios_test \"internal/os_test\"\n)\n\n// XXX ugh, I can't even sleep milliseconds.\n// XXX\n\nconst (\n\t_50ms = 50 * time.Millisecond\n\t_100ms = 100 * time.Millisecond\n\t_200ms = 200 * time.Millisecond\n\t_300ms = 300 * time.Millisecond\n\t_400ms = 400 * time.Millisecond\n\t_500ms = 500 * time.Millisecond\n)\n\nfunc nextStatus(m *Monitor) Status {\n\tsamples := m.samples\n\tfor i := 0; i \u003c 30; i++ {\n\t\tif s := m.Status(); s.Samples != samples {\n\t\t\treturn s\n\t\t}\n\t\tios_test.Sleep(5 * time.Millisecond)\n\t}\n\treturn m.Status()\n}\n\nfunc TestReader(t *testing.T) {\n\tin := make([]byte, 100)\n\tfor i := range in {\n\t\tin[i] = byte(i)\n\t}\n\tb := make([]byte, 100)\n\tr := NewReader(bytes.NewReader(in), 100)\n\tstart := time.Now()\n\n\t// Make sure r implements Limiter\n\t_ = Limiter(r)\n\n\t// 1st read of 10 bytes is performed immediately\n\tif n, err := r.Read(b); n != 10 {\n\t\tt.Fatalf(\"r.Read(b) expected 10 (\u003cnil\u003e); got %v\", n)\n\t} else if err != nil {\n\t\tt.Fatalf(\"r.Read(b) expected 10 (\u003cnil\u003e); got %v (%v)\", n, err.Error())\n\t} else if rt := time.Since(start); rt \u003e _50ms {\n\t\tt.Fatalf(\"r.Read(b) took too long (%v)\", rt.String())\n\t}\n\n\t// No new Reads allowed in the current sample\n\tr.SetBlocking(false)\n\tif n, err := r.Read(b); n != 0 {\n\t\tt.Fatalf(\"r.Read(b) expected 0 (\u003cnil\u003e); got %v\", n)\n\t} else if err != nil {\n\t\tt.Fatalf(\"r.Read(b) expected 0 (\u003cnil\u003e); got %v (%v)\", n, err.Error())\n\t} else if rt := time.Since(start); rt \u003e _50ms {\n\t\tt.Fatalf(\"r.Read(b) took too long (%v)\", rt.String())\n\t}\n\n\tstatus := [6]Status{0: r.Status()} // No samples in the first status\n\n\t// 2nd read of 10 bytes blocks until the next sample\n\t// r.SetBlocking(true)\n\tios_test.Sleep(100 * time.Millisecond)\n\tif n, err := r.Read(b[10:]); n != 10 {\n\t\tt.Fatalf(\"r.Read(b[10:]) expected 10 (\u003cnil\u003e); got %v\", n)\n\t} else if err != nil {\n\t\tt.Fatalf(\"r.Read(b[10:]) expected 10 (\u003cnil\u003e); got %v (%v)\", n, err.Error())\n\t} else if rt := time.Since(start); rt \u003c _100ms {\n\t\tt.Fatalf(\"r.Read(b[10:]) returned ahead of time (%v)\", rt.String())\n\t}\n\n\tstatus[1] = r.Status() // 1st sample\n\tstatus[2] = nextStatus(r.Monitor) // 2nd sample\n\tstatus[3] = nextStatus(r.Monitor) // No activity for the 3rd sample\n\n\tif n := r.Done(); n != 20 {\n\t\tt.Fatalf(\"r.Done() expected 20; got %v\", n)\n\t}\n\n\tstatus[4] = r.Status()\n\tstatus[5] = nextStatus(r.Monitor) // Timeout\n\tstart = status[0].Start\n\n\t// Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress\n\twant := []Status{\n\t\t{true, start, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},\n\t\t{true, start, _100ms, 0, 10, 1, 100, 100, 100, 100, 0, 0, 0},\n\t\t{true, start, _200ms, _100ms, 20, 2, 100, 100, 100, 100, 0, 0, 0},\n\t\t{true, start, _300ms, _200ms, 20, 3, 0, 90, 67, 100, 0, 0, 0},\n\t\t{false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0},\n\t\t{false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0},\n\t}\n\tfor i, s := range status {\n\t\t// XXX s := s\n\t\tif !statusesAreEqual(\u0026s, \u0026want[i]) {\n\t\t\tt.Errorf(\"r.Status(%v)\\nexpected: %v\\ngot : %v\", i, want[i].String(), s.String())\n\t\t}\n\t}\n\tif !bytes.Equal(b[:20], in[:20]) {\n\t\tt.Errorf(\"r.Read() input doesn't match output\")\n\t}\n}\n\n// XXX blocking writer test doesn't work.\nfunc _TestWriter(t *testing.T) {\n\tb := make([]byte, 100)\n\tfor i := range b {\n\t\tb[i] = byte(i)\n\t}\n\tw := NewWriter(\u0026bytes.Buffer{}, 200)\n\tstart := time.Now()\n\n\t// Make sure w implements Limiter\n\t_ = Limiter(w)\n\n\t// Non-blocking 20-byte write for the first sample returns ErrLimit\n\tw.SetBlocking(false)\n\tif n, err := w.Write(b); n != 20 || err != ErrLimit {\n\t\tt.Fatalf(\"w.Write(b) expected 20 (ErrLimit); got %v (%v)\", n, err.Error())\n\t} else if rt := time.Since(start); rt \u003e _50ms {\n\t\tt.Fatalf(\"w.Write(b) took too long (%v)\", rt)\n\t}\n\n\t// Blocking 80-byte write\n\t// w.SetBlocking(true)\n\t// XXX This test doesn't work, because w.Write calls w.Limit(block=false),\n\t// XXX and it returns ErrLimit after 20. What we want is to keep waiting until 80 is returned,\n\t// XXX but blocking isn't supported. Sleeping 800 shouldn't be sufficient either (its a burst).\n\t// XXX This limits the usage of Limiter and m.Limit().\n\tios_test.Sleep(800 * time.Millisecond)\n\tif n, err := w.Write(b[20:]); n \u003c 80 {\n\t} else if n != 80 || err != nil {\n\t\tt.Fatalf(\"w.Write(b[20:]) expected 80 (\u003cnil\u003e); got %v (%v)\", n, err.Error())\n\t} else if rt := time.Since(start); rt \u003c _300ms {\n\t\t// Explanation for `rt \u003c _300ms` (as opposed to `\u003c _400ms`)\n\t\t//\n\t\t// |\u003c-- start | |\n\t\t// epochs: -----0ms|---100ms|---200ms|---300ms|---400ms\n\t\t// sends: 20|20 |20 |20 |20#\n\t\t//\n\t\t// NOTE: The '#' symbol can thus happen before 400ms is up.\n\t\t// Thus, we can only panic if rt \u003c _300ms.\n\t\tt.Fatalf(\"w.Write(b[20:]) returned ahead of time (%v)\", rt.String())\n\t}\n\n\tw.SetTransferSize(100)\n\tstatus := []Status{w.Status(), nextStatus(w.Monitor)}\n\tstart = status[0].Start\n\n\t// Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress\n\twant := []Status{\n\t\t{true, start, _400ms, 0, 80, 4, 200, 200, 200, 200, 20, _100ms, 80000},\n\t\t{true, start, _500ms, _100ms, 100, 5, 200, 200, 200, 200, 0, 0, 100000},\n\t}\n\tfor i, s := range status {\n\t\t// XXX s := s\n\t\tif !statusesAreEqual(\u0026s, \u0026want[i]) {\n\t\t\tt.Errorf(\"w.Status(%v)\\nexpected: %v\\ngot : %v\\n\", i, want[i].String(), s.String())\n\t\t}\n\t}\n\tif !bytes.Equal(b, w.Writer.(*bytes.Buffer).Bytes()) {\n\t\tt.Errorf(\"w.Write() input doesn't match output\")\n\t}\n}\n\nconst (\n\tmaxDeviationForDuration = 50 * time.Millisecond\n\tmaxDeviationForRate int64 = 50\n)\n\n// statusesAreEqual returns true if s1 is equal to s2. Equality here means\n// general equality of fields except for the duration and rates, which can\n// drift due to unpredictable delays (e.g. thread wakes up 25ms after\n// `time.Sleep` has ended).\nfunc statusesAreEqual(s1 *Status, s2 *Status) bool {\n\tif s1.Active == s2.Active \u0026\u0026\n\t\ts1.Start == s2.Start \u0026\u0026\n\t\tdurationsAreEqual(s1.Duration, s2.Duration, maxDeviationForDuration) \u0026\u0026\n\t\ts1.Idle == s2.Idle \u0026\u0026\n\t\ts1.Bytes == s2.Bytes \u0026\u0026\n\t\ts1.Samples == s2.Samples \u0026\u0026\n\t\tratesAreEqual(s1.InstRate, s2.InstRate, maxDeviationForRate) \u0026\u0026\n\t\tratesAreEqual(s1.CurRate, s2.CurRate, maxDeviationForRate) \u0026\u0026\n\t\tratesAreEqual(s1.AvgRate, s2.AvgRate, maxDeviationForRate) \u0026\u0026\n\t\tratesAreEqual(s1.PeakRate, s2.PeakRate, maxDeviationForRate) \u0026\u0026\n\t\ts1.BytesRem == s2.BytesRem \u0026\u0026\n\t\tdurationsAreEqual(s1.TimeRem, s2.TimeRem, maxDeviationForDuration) \u0026\u0026\n\t\ts1.Progress == s2.Progress {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc durationsAreEqual(d1 time.Duration, d2 time.Duration, maxDeviation time.Duration) bool {\n\treturn d2-d1 \u003c= maxDeviation\n}\n\nfunc ratesAreEqual(r1 int64, r2 int64, maxDeviation int64) bool {\n\tsub := r1 - r2\n\tif sub \u003c 0 {\n\t\tsub = -sub\n\t}\n\tif sub \u003c= maxDeviation {\n\t\treturn true\n\t}\n\treturn false\n}\n" + }, + { + "name": "util.gno", + "body": "//\n// Written by Maxim Khitrov (November 2012)\n//\n\npackage flow\n\nimport (\n\t\"math\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// clockRate is the resolution and precision of clock().\nconst clockRate = 20 * time.Millisecond\n\n// czero is the process start time rounded down to the nearest clockRate\n// increment.\nvar czero = time.Now().Round(clockRate)\n\n// clock returns a low resolution timestamp relative to the process start time.\nfunc clock() time.Duration {\n\treturn time.Now().Round(clockRate).Sub(czero)\n}\n\n// clockToTime converts a clock() timestamp to an absolute time.Time value.\nfunc clockToTime(c time.Duration) time.Time {\n\treturn czero.Add(c)\n}\n\n// clockRound returns d rounded to the nearest clockRate increment.\nfunc clockRound(d time.Duration) time.Duration {\n\treturn (d + clockRate\u003e\u003e1) / clockRate * clockRate\n}\n\n// round returns x rounded to the nearest int64 (non-negative values only).\nfunc round(x float64) int64 {\n\tif _, frac := math.Modf(x); frac \u003e= 0.5 {\n\t\treturn int64(math.Ceil(x))\n\t}\n\treturn int64(math.Floor(x))\n}\n\n// Percent represents a percentage in increments of 1/1000th of a percent.\ntype Percent uint32\n\n// percentOf calculates what percent of the total is x.\nfunc percentOf(x, total float64) Percent {\n\tif x \u003c 0 || total \u003c= 0 {\n\t\treturn 0\n\t} else if p := round(x / total * 1e5); p \u003c= math.MaxUint32 {\n\t\treturn Percent(p)\n\t}\n\treturn Percent(math.MaxUint32)\n}\n\nfunc (p Percent) Float() float64 {\n\treturn float64(p) * 1e-3\n}\n\nfunc (p Percent) String() string {\n\tvar buf [12]byte\n\tb := strconv.AppendUint(buf[:0], uint64(p)/1000, 10)\n\tn := len(b)\n\tb = strconv.AppendUint(b, 1000+uint64(p)%1000, 10)\n\tb[n] = '.'\n\treturn string(append(b, '%'))\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "gnode", + "path": "gno.land/p/demo/gnode", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnode\n" + }, + { + "name": "gnode.gno", + "body": "package gnode\n\n// XXX what about Gnodes signing on behalf of others?\n// XXX like a multi-sig of Gnodes?\n\ntype Name string\n\ntype Gnode interface {\n\t//----------------------------------------\n\t// Basic properties\n\tGetName() Name\n\n\t//----------------------------------------\n\t// Affiliate Gnodes\n\tNumAffiliates() int\n\tGetAffiliates(Name) Affiliate\n\tAddAffiliate(Affiliate) error // must be affiliated\n\tRemAffiliate(Name) error // must have become unaffiliated\n\n\t//----------------------------------------\n\t// Signing\n\tNumSignedDocuments() int\n\tGetSignedDocument(idx int) Document\n\tSignDocument(doc Document) (int, error) // index relative to signer\n\n\t//----------------------------------------\n\t// Rendering\n\tRenderLines() []string\n}\n\ntype Affiliate struct {\n\tType string\n\tGnode Gnode\n\tTags []string\n}\n\ntype MyGnode struct {\n\tName\n\t// Owners // voting set, something that gives authority of action.\n\t// Treasury //\n\t// Affiliates //\n\t// Board // discussions\n\t// Data // XXX ?\n}\n\ntype Affiliates []*Affiliate\n\n// Documents are equal if they compare equal.\n// NOTE: requires all fields to be comparable.\ntype Document struct {\n\tAuthors string\n\t// Timestamp\n\t// Body\n\t// Attachments\n}\n\n// ACTIONS\n\n// * Lend tokens\n// * Pay tokens\n// * Administrate transferrable and non-transferrable tokens\n// * Sum tokens\n// * Passthrough dependencies\n// * Code\n// * ...\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "agent", + "path": "gno.land/p/demo/gnorkle/agent", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/agent\n\nrequire gno.land/p/demo/avl v0.0.0-latest\n" + }, + { + "name": "whitelist.gno", + "body": "package agent\n\nimport \"gno.land/p/demo/avl\"\n\n// Whitelist manages whitelisted agent addresses.\ntype Whitelist struct {\n\tstore *avl.Tree\n}\n\n// ClearAddresses removes all addresses from the whitelist and puts into a state\n// that indicates it is moot and has no whitelist defined.\nfunc (m *Whitelist) ClearAddresses() {\n\tm.store = nil\n}\n\n// AddAddresses adds the given addresses to the whitelist.\nfunc (m *Whitelist) AddAddresses(addresses []string) {\n\tif m.store == nil {\n\t\tm.store = avl.NewTree()\n\t}\n\n\tfor _, address := range addresses {\n\t\tm.store.Set(address, struct{}{})\n\t}\n}\n\n// RemoveAddress removes the given address from the whitelist if it exists.\nfunc (m *Whitelist) RemoveAddress(address string) {\n\tif m.store == nil {\n\t\treturn\n\t}\n\n\tm.store.Remove(address)\n}\n\n// HasDefinition returns true if the whitelist has a definition. It retuns false if\n// `ClearAddresses` has been called without any subsequent `AddAddresses` calls, or\n// if `AddAddresses` has never been called.\nfunc (m Whitelist) HasDefinition() bool {\n\treturn m.store != nil\n}\n\n// HasAddress returns true if the given address is in the whitelist.\nfunc (m Whitelist) HasAddress(address string) bool {\n\tif m.store == nil {\n\t\treturn false\n\t}\n\n\treturn m.store.Has(address)\n}\n" + }, + { + "name": "whitelist_test.gno", + "body": "package agent_test\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/gnorkle/agent\"\n)\n\nfunc TestWhitelist(t *testing.T) {\n\tvar whitelist agent.Whitelist\n\n\tif whitelist.HasDefinition() {\n\t\tt.Error(\"whitelist should not be defined initially\")\n\t}\n\n\twhitelist.AddAddresses([]string{\"a\", \"b\"})\n\tif !whitelist.HasAddress(\"a\") {\n\t\tt.Error(`whitelist should have address \"a\"`)\n\t}\n\tif !whitelist.HasAddress(\"b\") {\n\t\tt.Error(`whitelist should have address \"b\"`)\n\t}\n\n\tif !whitelist.HasDefinition() {\n\t\tt.Error(\"whitelist should be defined after adding addresses\")\n\t}\n\n\twhitelist.RemoveAddress(\"a\")\n\tif whitelist.HasAddress(\"a\") {\n\t\tt.Error(`whitelist should not have address \"a\"`)\n\t}\n\tif !whitelist.HasAddress(\"b\") {\n\t\tt.Error(`whitelist should still have address \"b\"`)\n\t}\n\n\twhitelist.ClearAddresses()\n\tif whitelist.HasAddress(\"a\") {\n\t\tt.Error(`whitelist cleared; should not have address \"a\"`)\n\t}\n\tif whitelist.HasAddress(\"b\") {\n\t\tt.Error(`whitelist cleared; should still have address \"b\"`)\n\t}\n\n\tif whitelist.HasDefinition() {\n\t\tt.Error(\"whitelist cleared; should not be defined\")\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "feed", + "path": "gno.land/p/demo/gnorkle/feed", + "files": [ + { + "name": "errors.gno", + "body": "package feed\n\nimport \"errors\"\n\nvar ErrUndefined = errors.New(\"undefined feed\")\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/feed\n" + }, + { + "name": "task.gno", + "body": "package feed\n\n// Task is a unit of work that can be part of a `Feed` definition. Tasks\n// are executed by agents.\ntype Task interface {\n\tMarshalJSON() ([]byte, error)\n}\n" + }, + { + "name": "type.gno", + "body": "package feed\n\n// Type indicates the type of a feed.\ntype Type int\n\nconst (\n\t// TypeStatic indicates a feed cannot be changed once the first value is committed.\n\tTypeStatic Type = iota\n\t// TypeContinuous indicates a feed can continuously ingest values and will publish\n\t// a new value on request using the values it has ingested.\n\tTypeContinuous\n\t// TypePeriodic indicates a feed can accept one or more values within a certain period\n\t// and will proceed to commit these values at the end up each period to produce an\n\t// aggregate value before starting a new period.\n\tTypePeriodic\n)\n" + }, + { + "name": "value.gno", + "body": "package feed\n\nimport \"time\"\n\n// Value represents a value published by a feed. The `Time` is when the value was published.\ntype Value struct {\n\tString string\n\tTime time.Time\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "ingester", + "path": "gno.land/p/demo/gnorkle/ingester", + "files": [ + { + "name": "errors.gno", + "body": "package ingester\n\nimport \"errors\"\n\nvar ErrUndefined = errors.New(\"ingester undefined\")\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/ingester\n" + }, + { + "name": "type.gno", + "body": "package ingester\n\n// Type indicates an ingester type.\ntype Type int\n\nconst (\n\t// TypeSingle indicates an ingester that can only ingest a single within a given period or no period.\n\tTypeSingle Type = iota\n\t// TypeMulti indicates an ingester that can ingest multiple within a given period or no period\n\tTypeMulti\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "message", + "path": "gno.land/p/demo/gnorkle/message", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/message\n" + }, + { + "name": "parse.gno", + "body": "package message\n\nimport \"strings\"\n\n// ParseFunc parses a raw message and returns the message function\n// type extracted from the remainder of the message.\nfunc ParseFunc(rawMsg string) (FuncType, string) {\n\tfuncType, remainder := parseFirstToken(rawMsg)\n\treturn FuncType(funcType), remainder\n}\n\n// ParseID parses a raw message and returns the ID extracted from\n// the remainder of the message.\nfunc ParseID(rawMsg string) (string, string) {\n\treturn parseFirstToken(rawMsg)\n}\n\nfunc parseFirstToken(rawMsg string) (string, string) {\n\tmsgParts := strings.SplitN(rawMsg, \",\", 2)\n\tif len(msgParts) \u003c 2 {\n\t\treturn msgParts[0], \"\"\n\t}\n\n\treturn msgParts[0], msgParts[1]\n}\n" + }, + { + "name": "parse_test.gno", + "body": "package message_test\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/gnorkle/message\"\n)\n\nfunc TestParseFunc(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput string\n\t\texpFuncType message.FuncType\n\t\texpRemainder string\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t},\n\t\t{\n\t\t\tname: \"func only\",\n\t\t\tinput: \"ingest\",\n\t\t\texpFuncType: message.FuncTypeIngest,\n\t\t},\n\t\t{\n\t\t\tname: \"func with short remainder\",\n\t\t\tinput: \"commit,asdf\",\n\t\t\texpFuncType: message.FuncTypeCommit,\n\t\t\texpRemainder: \"asdf\",\n\t\t},\n\t\t{\n\t\t\tname: \"func with long remainder\",\n\t\t\tinput: \"request,hello,world,goodbye\",\n\t\t\texpFuncType: message.FuncTypeRequest,\n\t\t\texpRemainder: \"hello,world,goodbye\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tfuncType, remainder := message.ParseFunc(tt.input)\n\t\t\tif funcType != tt.expFuncType {\n\t\t\t\tt.Errorf(\"expected func type %s, got %s\", tt.expFuncType, funcType)\n\t\t\t}\n\n\t\t\tif remainder != tt.expRemainder {\n\t\t\t\tt.Errorf(\"expected remainder of %s, got %s\", tt.expRemainder, remainder)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "type.gno", + "body": "package message\n\n// FuncType is the type of function that is being called by the agent.\ntype FuncType string\n\nconst (\n\t// FuncTypeIngest means the agent is sending data for ingestion.\n\tFuncTypeIngest FuncType = \"ingest\"\n\t// FuncTypeCommit means the agent is requesting a feed commit the transitive data\n\t// being held by its ingester.\n\tFuncTypeCommit FuncType = \"commit\"\n\t// FuncTypeRequest means the agent is requesting feed definitions for all those\n\t// that it is whitelisted to provide data for.\n\tFuncTypeRequest FuncType = \"request\"\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "gnorkle", + "path": "gno.land/p/demo/gnorkle/gnorkle", + "files": [ + { + "name": "feed.gno", + "body": "package gnorkle\n\nimport (\n\t\"gno.land/p/demo/gnorkle/feed\"\n\t\"gno.land/p/demo/gnorkle/message\"\n)\n\n// Feed is an abstraction used by a gnorkle `Instance` to ingest data from\n// agents and provide data feeds to consumers.\ntype Feed interface {\n\tID() string\n\tType() feed.Type\n\tValue() (value feed.Value, dataType string, consumable bool)\n\tIngest(funcType message.FuncType, rawMessage, providerAddress string) error\n\tMarshalJSON() ([]byte, error)\n\tTasks() []feed.Task\n\tIsActive() bool\n}\n\n// FeedWithWhitelist associates a `Whitelist` with a `Feed`.\ntype FeedWithWhitelist struct {\n\tFeed\n\tWhitelist\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/gnorkle\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/gnorkle/agent v0.0.0-latest\n\tgno.land/p/demo/gnorkle/feed v0.0.0-latest\n\tgno.land/p/demo/gnorkle/ingester v0.0.0-latest\n\tgno.land/p/demo/gnorkle/message v0.0.0-latest\n)\n" + }, + { + "name": "ingester.gno", + "body": "package gnorkle\n\nimport \"gno.land/p/demo/gnorkle/ingester\"\n\n// Ingester is the abstraction that allows a `Feed` to ingest data from agents\n// and commit it to storage using zero or more intermediate aggregation steps.\ntype Ingester interface {\n\tType() ingester.Type\n\tIngest(value, providerAddress string) (canAutoCommit bool, err error)\n\tCommitValue(storage Storage, providerAddress string) error\n}\n" + }, + { + "name": "instance.gno", + "body": "package gnorkle\n\nimport (\n\t\"errors\"\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/gnorkle/agent\"\n\t\"gno.land/p/demo/gnorkle/feed\"\n\t\"gno.land/p/demo/gnorkle/message\"\n)\n\n// Instance is a single instance of an oracle.\ntype Instance struct {\n\tfeeds *avl.Tree\n\twhitelist agent.Whitelist\n}\n\n// NewInstance creates a new instance of an oracle.\nfunc NewInstance() *Instance {\n\treturn \u0026Instance{\n\t\tfeeds: avl.NewTree(),\n\t}\n}\n\nfunc assertValidID(id string) error {\n\tif len(id) == 0 {\n\t\treturn errors.New(\"feed ids cannot be empty\")\n\t}\n\n\tif strings.Contains(id, \",\") {\n\t\treturn errors.New(\"feed ids cannot contain commas\")\n\t}\n\n\treturn nil\n}\n\nfunc (i *Instance) assertFeedDoesNotExist(id string) error {\n\tif i.feeds.Has(id) {\n\t\treturn errors.New(\"feed already exists\")\n\t}\n\n\treturn nil\n}\n\n// AddFeeds adds feeds to the instance with empty whitelists.\nfunc (i *Instance) AddFeeds(feeds ...Feed) error {\n\tfor _, feed := range feeds {\n\t\tif err := assertValidID(feed.ID()); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := i.assertFeedDoesNotExist(feed.ID()); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\ti.feeds.Set(\n\t\t\tfeed.ID(),\n\t\t\tFeedWithWhitelist{\n\t\t\t\tWhitelist: new(agent.Whitelist),\n\t\t\t\tFeed: feed,\n\t\t\t},\n\t\t)\n\t}\n\n\treturn nil\n}\n\n// AddFeedsWithWhitelists adds feeds to the instance with the given whitelists.\nfunc (i *Instance) AddFeedsWithWhitelists(feeds ...FeedWithWhitelist) error {\n\tfor _, feed := range feeds {\n\t\tif err := i.assertFeedDoesNotExist(feed.ID()); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := assertValidID(feed.ID()); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\ti.feeds.Set(\n\t\t\tfeed.ID(),\n\t\t\tFeedWithWhitelist{\n\t\t\t\tWhitelist: feed.Whitelist,\n\t\t\t\tFeed: feed,\n\t\t\t},\n\t\t)\n\t}\n\n\treturn nil\n}\n\n// RemoveFeed removes a feed from the instance.\nfunc (i *Instance) RemoveFeed(id string) {\n\ti.feeds.Remove(id)\n}\n\n// PostMessageHandler is a type that allows for post-processing of feed state after a feed\n// ingests a message from an agent.\ntype PostMessageHandler interface {\n\tHandle(i *Instance, funcType message.FuncType, feed Feed) error\n}\n\n// HandleMessage handles a message from an agent and routes to either the logic that returns\n// feed definitions or the logic that allows a feed to ingest a message.\n//\n// TODO: Consider further message types that could allow administrative action such as modifying\n// a feed's whitelist without the owner of this oracle having to maintain a reference to it.\nfunc (i *Instance) HandleMessage(msg string, postHandler PostMessageHandler) (string, error) {\n\tcaller := string(std.GetOrigCaller())\n\n\tfuncType, msg := message.ParseFunc(msg)\n\n\tswitch funcType {\n\tcase message.FuncTypeRequest:\n\t\treturn i.GetFeedDefinitions(caller)\n\n\tdefault:\n\t\tid, msg := message.ParseID(msg)\n\t\tif err := assertValidID(id); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tfeedWithWhitelist, err := i.getFeedWithWhitelist(id)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif !addressIsWhitelisted(\u0026i.whitelist, feedWithWhitelist, caller, nil) {\n\t\t\treturn \"\", errors.New(\"caller not whitelisted\")\n\t\t}\n\n\t\tif err := feedWithWhitelist.Ingest(funcType, msg, caller); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif postHandler != nil {\n\t\t\tpostHandler.Handle(i, funcType, feedWithWhitelist)\n\t\t}\n\t}\n\n\treturn \"\", nil\n}\n\nfunc (i *Instance) getFeed(id string) (Feed, error) {\n\tuntypedFeed, ok := i.feeds.Get(id)\n\tif !ok {\n\t\treturn nil, errors.New(\"invalid ingest id: \" + id)\n\t}\n\n\tfeed, ok := untypedFeed.(Feed)\n\tif !ok {\n\t\treturn nil, errors.New(\"invalid feed type\")\n\t}\n\n\treturn feed, nil\n}\n\nfunc (i *Instance) getFeedWithWhitelist(id string) (FeedWithWhitelist, error) {\n\tuntypedFeedWithWhitelist, ok := i.feeds.Get(id)\n\tif !ok {\n\t\treturn FeedWithWhitelist{}, errors.New(\"invalid ingest id: \" + id)\n\t}\n\n\tfeedWithWhitelist, ok := untypedFeedWithWhitelist.(FeedWithWhitelist)\n\tif !ok {\n\t\treturn FeedWithWhitelist{}, errors.New(\"invalid feed with whitelist type\")\n\t}\n\n\treturn feedWithWhitelist, nil\n}\n\n// GetFeedValue returns the most recently published value of a feed along with a string\n// representation of the value's type and boolean indicating whether the value is\n// okay for consumption.\nfunc (i *Instance) GetFeedValue(id string) (feed.Value, string, bool, error) {\n\tfoundFeed, err := i.getFeed(id)\n\tif err != nil {\n\t\treturn feed.Value{}, \"\", false, err\n\t}\n\n\tvalue, valueType, consumable := foundFeed.Value()\n\treturn value, valueType, consumable, nil\n}\n\n// GetFeedDefinitions returns a JSON string representing the feed definitions for which the given\n// agent address is whitelisted to provide values for ingestion.\nfunc (i *Instance) GetFeedDefinitions(forAddress string) (string, error) {\n\tinstanceHasAddressWhitelisted := !i.whitelist.HasDefinition() || i.whitelist.HasAddress(forAddress)\n\n\tbuf := new(strings.Builder)\n\tbuf.WriteString(\"[\")\n\tfirst := true\n\tvar err error\n\n\t// The boolean value returned by this callback function indicates whether to stop iterating.\n\ti.feeds.Iterate(\"\", \"\", func(_ string, value interface{}) bool {\n\t\tfeedWithWhitelist, ok := value.(FeedWithWhitelist)\n\t\tif !ok {\n\t\t\terr = errors.New(\"invalid feed type\")\n\t\t\treturn true\n\t\t}\n\n\t\t// Don't give agents the ability to try to publish to inactive feeds.\n\t\tif !feedWithWhitelist.IsActive() {\n\t\t\treturn false\n\t\t}\n\n\t\t// Skip feeds the address is not whitelisted for.\n\t\tif !addressIsWhitelisted(\u0026i.whitelist, feedWithWhitelist, forAddress, \u0026instanceHasAddressWhitelisted) {\n\t\t\treturn false\n\t\t}\n\n\t\tvar taskBytes []byte\n\t\tif taskBytes, err = feedWithWhitelist.Feed.MarshalJSON(); err != nil {\n\t\t\treturn true\n\t\t}\n\n\t\t// Guard against any tasks that shouldn't be returned; maybe they are not active because they have\n\t\t// already been completed.\n\t\tif len(taskBytes) == 0 {\n\t\t\treturn false\n\t\t}\n\n\t\tif !first {\n\t\t\tbuf.WriteString(\",\")\n\t\t}\n\n\t\tfirst = false\n\t\tbuf.Write(taskBytes)\n\t\treturn true\n\t})\n\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tbuf.WriteString(\"]\")\n\treturn buf.String(), nil\n}\n" + }, + { + "name": "storage.gno", + "body": "package gnorkle\n\nimport \"gno.land/p/demo/gnorkle/feed\"\n\n// Storage defines how published feed values should be read\n// and written.\ntype Storage interface {\n\tPut(value string) error\n\tGetLatest() feed.Value\n\tGetHistory() []feed.Value\n}\n" + }, + { + "name": "whitelist.gno", + "body": "package gnorkle\n\n// Whitelist is used to manage which agents are allowed to interact.\ntype Whitelist interface {\n\tClearAddresses()\n\tAddAddresses(addresses []string)\n\tRemoveAddress(address string)\n\tHasDefinition() bool\n\tHasAddress(address string) bool\n}\n\n// ClearWhitelist clears the whitelist of the instance or feed depending on the feed ID.\nfunc (i *Instance) ClearWhitelist(feedID string) error {\n\tif feedID == \"\" {\n\t\ti.whitelist.ClearAddresses()\n\t\treturn nil\n\t}\n\n\tfeedWithWhitelist, err := i.getFeedWithWhitelist(feedID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfeedWithWhitelist.ClearAddresses()\n\treturn nil\n}\n\n// AddToWhitelist adds the given addresses to the whitelist of the instance or feed depending on the feed ID.\nfunc (i *Instance) AddToWhitelist(feedID string, addresses []string) error {\n\tif feedID == \"\" {\n\t\ti.whitelist.AddAddresses(addresses)\n\t\treturn nil\n\t}\n\n\tfeedWithWhitelist, err := i.getFeedWithWhitelist(feedID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfeedWithWhitelist.AddAddresses(addresses)\n\treturn nil\n}\n\n// RemoveFromWhitelist removes the given address from the whitelist of the instance or feed depending on the feed ID.\nfunc (i *Instance) RemoveFromWhitelist(feedID string, address string) error {\n\tif feedID == \"\" {\n\t\ti.whitelist.RemoveAddress(address)\n\t\treturn nil\n\t}\n\n\tfeedWithWhitelist, err := i.getFeedWithWhitelist(feedID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfeedWithWhitelist.RemoveAddress(address)\n\treturn nil\n}\n\n// addressWhiteListed returns true if:\n// - the feed has a white list and the address is whitelisted, or\n// - the feed has no white list and the instance has a white list and the address is whitelisted, or\n// - the feed has no white list and the instance has no white list.\nfunc addressIsWhitelisted(instanceWhitelist, feedWhitelist Whitelist, address string, instanceWhitelistedOverride *bool) bool {\n\t// A feed whitelist takes priority, so it will return false if the feed has a whitelist and the caller is\n\t// not a part of it. An empty whitelist defers to the instance whitelist.\n\tif feedWhitelist != nil {\n\t\tif feedWhitelist.HasDefinition() \u0026\u0026 !feedWhitelist.HasAddress(address) {\n\t\t\treturn false\n\t\t}\n\n\t\t// Getting to this point means that one of the following is true:\n\t\t// - the feed has no defined whitelist (so it can't possibly have the address whitelisted)\n\t\t// - the feed has a defined whitelist and the caller is a part of it\n\t\t//\n\t\t// In this case, we can be sure that the boolean indicating whether the feed has this address whitelisted\n\t\t// is equivalent to the boolean indicating whether the feed has a defined whitelist.\n\t\tif feedWhitelist.HasDefinition() {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tif instanceWhitelistedOverride != nil {\n\t\treturn *instanceWhitelistedOverride\n\t}\n\n\t// We were unable able to determine whether this address is allowed after looking at the feed whitelist,\n\t// so fall back to the instance whitelist. A complete absence of values in the instance whitelist means\n\t// that the instance has no whitelist so we can return true because everything is allowed by default.\n\tif instanceWhitelist == nil || !instanceWhitelist.HasDefinition() {\n\t\treturn true\n\t}\n\n\t// The instance whitelist is defined so if the address is present then it is allowed.\n\treturn instanceWhitelist.HasAddress(address)\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "storage", + "path": "gno.land/p/demo/gnorkle/storage", + "files": [ + { + "name": "errors.gno", + "body": "package storage\n\nimport \"errors\"\n\nvar ErrUndefined = errors.New(\"undefined storage\")\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/storage\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "simple", + "path": "gno.land/p/demo/gnorkle/storage/simple", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/storage/simple\n\nrequire (\n\tgno.land/p/demo/gnorkle/feed v0.0.0-latest\n\tgno.land/p/demo/gnorkle/storage v0.0.0-latest\n)\n" + }, + { + "name": "storage.gno", + "body": "package simple\n\nimport (\n\t\"time\"\n\n\t\"gno.land/p/demo/gnorkle/feed\"\n\t\"gno.land/p/demo/gnorkle/storage\"\n)\n\n// Storage is simple, bounded storage for published feed values.\ntype Storage struct {\n\tvalues []feed.Value\n\tmaxValues uint\n}\n\n// NewStorage creates a new Storage with the given maximum number of values.\n// If maxValues is 0, the storage is bounded to a size of one. If this is not desirable,\n// then don't provide a value of 0.\nfunc NewStorage(maxValues uint) *Storage {\n\tif maxValues == 0 {\n\t\tmaxValues = 1\n\t}\n\n\treturn \u0026Storage{\n\t\tmaxValues: maxValues,\n\t}\n}\n\n// Put adds a new value to the storage. If the storage is full, the oldest value\n// is removed. If maxValues is 0, the storage is bounded to a size of one.\nfunc (s *Storage) Put(value string) error {\n\tif s == nil {\n\t\treturn storage.ErrUndefined\n\t}\n\n\ts.values = append(s.values, feed.Value{String: value, Time: time.Now()})\n\tif uint(len(s.values)) \u003e s.maxValues {\n\t\ts.values = s.values[1:]\n\t}\n\n\treturn nil\n}\n\n// GetLatest returns the most recently added value, or an empty value if none exist.\nfunc (s Storage) GetLatest() feed.Value {\n\tif len(s.values) == 0 {\n\t\treturn feed.Value{}\n\t}\n\n\treturn s.values[len(s.values)-1]\n}\n\n// GetHistory returns all values in the storage, from oldest to newest.\nfunc (s Storage) GetHistory() []feed.Value {\n\treturn s.values\n}\n" + }, + { + "name": "storage_test.gno", + "body": "package simple_test\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/gnorkle/storage\"\n\t\"gno.land/p/demo/gnorkle/storage/simple\"\n)\n\nfunc TestStorage(t *testing.T) {\n\tvar undefinedStorage *simple.Storage\n\tif err := undefinedStorage.Put(\"\"); err != storage.ErrUndefined {\n\t\tt.Error(\"expected storage.ErrUndefined on undefined storage\")\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\tvaluesToPut []string\n\t\texpLatestValueString string\n\t\texpLatestValueTimeIsZero bool\n\t\texpHistoricalValueStrings []string\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\texpLatestValueTimeIsZero: true,\n\t\t},\n\t\t{\n\t\t\tname: \"one value\",\n\t\t\tvaluesToPut: []string{\"one\"},\n\t\t\texpLatestValueString: \"one\",\n\t\t\texpHistoricalValueStrings: []string{\"one\"},\n\t\t},\n\t\t{\n\t\t\tname: \"two values\",\n\t\t\tvaluesToPut: []string{\"one\", \"two\"},\n\t\t\texpLatestValueString: \"two\",\n\t\t\texpHistoricalValueStrings: []string{\"one\", \"two\"},\n\t\t},\n\t\t{\n\t\t\tname: \"three values\",\n\t\t\tvaluesToPut: []string{\"one\", \"two\", \"three\"},\n\t\t\texpLatestValueString: \"three\",\n\t\t\texpHistoricalValueStrings: []string{\"two\", \"three\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tsimpleStorage := simple.NewStorage(2)\n\t\t\tfor _, value := range tt.valuesToPut {\n\t\t\t\tif err := simpleStorage.Put(value); err != nil {\n\t\t\t\t\tt.Fatal(\"unexpected error putting value in storage\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlatestValue := simpleStorage.GetLatest()\n\t\t\tif latestValue.String != tt.expLatestValueString {\n\t\t\t\tt.Errorf(\"expected latest value of %s, got %s\", tt.expLatestValueString, latestValue.String)\n\t\t\t}\n\n\t\t\tif latestValue.Time.IsZero() != tt.expLatestValueTimeIsZero {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"expected latest value time zero value of %t, got %t\",\n\t\t\t\t\ttt.expLatestValueTimeIsZero,\n\t\t\t\t\tlatestValue.Time.IsZero(),\n\t\t\t\t)\n\t\t\t}\n\n\t\t\thistoricalValues := simpleStorage.GetHistory()\n\t\t\tif len(historicalValues) != len(tt.expHistoricalValueStrings) {\n\t\t\t\tt.Fatal(\"historical values length does not match\")\n\t\t\t}\n\n\t\t\tfor i, expValue := range tt.expHistoricalValueStrings {\n\t\t\t\tif expValue != historicalValues[i].String {\n\t\t\t\t\tt.Errorf(\n\t\t\t\t\t\t\"expected historical value at idx %d to be %s, got %s\",\n\t\t\t\t\t\ti,\n\t\t\t\t\t\texpValue,\n\t\t\t\t\t\thistoricalValues[i].String,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tif historicalValues[i].Time.IsZero() {\n\t\t\t\t\tt.Errorf(\"unexpeced zero time for historical value at index %d\", i)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "single", + "path": "gno.land/p/demo/gnorkle/ingesters/single", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/ingesters/single\n\nrequire (\n\tgno.land/p/demo/gnorkle/gnorkle v0.0.0-latest\n\tgno.land/p/demo/gnorkle/ingester v0.0.0-latest\n\tgno.land/p/demo/gnorkle/storage/simple v0.0.0-latest\n)\n" + }, + { + "name": "ingester.gno", + "body": "package single\n\nimport (\n\t\"gno.land/p/demo/gnorkle/gnorkle\"\n\t\"gno.land/p/demo/gnorkle/ingester\"\n)\n\n// ValueIngester is an ingester that ingests a single value.\ntype ValueIngester struct {\n\tvalue string\n}\n\n// Type returns the type of the ingester.\nfunc (i *ValueIngester) Type() ingester.Type {\n\treturn ingester.TypeSingle\n}\n\n// Ingest ingests a value provided by the given agent address.\nfunc (i *ValueIngester) Ingest(value, providerAddress string) (bool, error) {\n\tif i == nil {\n\t\treturn false, ingester.ErrUndefined\n\t}\n\n\ti.value = value\n\treturn true, nil\n}\n\n// CommitValue commits the ingested value to the given storage instance.\nfunc (i *ValueIngester) CommitValue(valueStorer gnorkle.Storage, providerAddress string) error {\n\tif i == nil {\n\t\treturn ingester.ErrUndefined\n\t}\n\n\treturn valueStorer.Put(i.value)\n}\n" + }, + { + "name": "ingester_test.gno", + "body": "package single_test\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/gnorkle/ingester\"\n\t\"gno.land/p/demo/gnorkle/ingesters/single\"\n\t\"gno.land/p/demo/gnorkle/storage/simple\"\n)\n\nfunc TestValueIngester(t *testing.T) {\n\tstorage := simple.NewStorage(1)\n\n\tvar undefinedIngester *single.ValueIngester\n\tif _, err := undefinedIngester.Ingest(\"asdf\", \"gno11111\"); err != ingester.ErrUndefined {\n\t\tt.Error(\"undefined ingester call to Ingest should return ingester.ErrUndefined\")\n\t}\n\tif err := undefinedIngester.CommitValue(storage, \"gno11111\"); err != ingester.ErrUndefined {\n\t\tt.Error(\"undefined ingester call to CommitValue should return ingester.ErrUndefined\")\n\t}\n\n\tvar valueIngester single.ValueIngester\n\tif typ := valueIngester.Type(); typ != ingester.TypeSingle {\n\t\tt.Error(\"single value ingester should return type ingester.TypeSingle\")\n\t}\n\n\tingestValue := \"value\"\n\tautocommit, err := valueIngester.Ingest(ingestValue, \"gno11111\")\n\tif !autocommit {\n\t\tt.Error(\"single value ingester should return autocommit true\")\n\t}\n\tif err != nil {\n\t\tt.Errorf(\"unexpected ingest error %s\", err.Error())\n\t}\n\n\tif err := valueIngester.CommitValue(storage, \"gno11111\"); err != nil {\n\t\tt.Errorf(\"unexpected commit error %s\", err.Error())\n\t}\n\n\tif latestValue := storage.GetLatest(); latestValue.String != ingestValue {\n\t\tt.Errorf(\"expected latest value of %s, got %s\", ingestValue, latestValue.String)\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "static", + "path": "gno.land/p/demo/gnorkle/feeds/static", + "files": [ + { + "name": "feed.gno", + "body": "package static\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\n\t\"gno.land/p/demo/gnorkle/feed\"\n\t\"gno.land/p/demo/gnorkle/gnorkle\"\n\t\"gno.land/p/demo/gnorkle/ingesters/single\"\n\t\"gno.land/p/demo/gnorkle/message\"\n\t\"gno.land/p/demo/gnorkle/storage/simple\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// Feed is a static feed.\ntype Feed struct {\n\tid string\n\tisLocked bool\n\tvalueDataType string\n\tingester gnorkle.Ingester\n\tstorage gnorkle.Storage\n\ttasks []feed.Task\n}\n\n// NewFeed creates a new static feed.\nfunc NewFeed(\n\tid string,\n\tvalueDataType string,\n\tingester gnorkle.Ingester,\n\tstorage gnorkle.Storage,\n\ttasks ...feed.Task,\n) *Feed {\n\treturn \u0026Feed{\n\t\tid: id,\n\t\tvalueDataType: valueDataType,\n\t\tingester: ingester,\n\t\tstorage: storage,\n\t\ttasks: tasks,\n\t}\n}\n\n// NewSingleValueFeed is a convenience function for creating a static feed\n// that autocommits a value after a single ingestion.\nfunc NewSingleValueFeed(\n\tid string,\n\tvalueDataType string,\n\ttasks ...feed.Task,\n) *Feed {\n\treturn NewFeed(\n\t\tid,\n\t\tvalueDataType,\n\t\t\u0026single.ValueIngester{},\n\t\tsimple.NewStorage(1),\n\t\ttasks...,\n\t)\n}\n\n// ID returns the feed's ID.\nfunc (f Feed) ID() string {\n\treturn f.id\n}\n\n// Type returns the feed's type.\nfunc (f Feed) Type() feed.Type {\n\treturn feed.TypeStatic\n}\n\n// Ingest ingests a message into the feed. It either adds the value to the ingester's\n// pending values or commits the value to the storage.\nfunc (f *Feed) Ingest(funcType message.FuncType, msg, providerAddress string) error {\n\tif f == nil {\n\t\treturn feed.ErrUndefined\n\t}\n\n\tif f.isLocked {\n\t\treturn errors.New(\"feed locked\")\n\t}\n\n\tswitch funcType {\n\tcase message.FuncTypeIngest:\n\t\t// Autocommit the ingester's value if it's a single value ingester\n\t\t// because this is a static feed and this is the only value it will ever have.\n\t\tif canAutoCommit, err := f.ingester.Ingest(msg, providerAddress); canAutoCommit \u0026\u0026 err == nil {\n\t\t\tif err := f.ingester.CommitValue(f.storage, providerAddress); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tf.isLocked = true\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\n\tcase message.FuncTypeCommit:\n\t\tif err := f.ingester.CommitValue(f.storage, providerAddress); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tf.isLocked = true\n\n\tdefault:\n\t\treturn errors.New(\"invalid message function \" + string(funcType))\n\t}\n\n\treturn nil\n}\n\n// Value returns the feed's latest value, it's data type, and whether or not it can\n// be safely consumed. In this case it uses `f.isLocked` because, this being a static\n// feed, it will only ever have one value; once that value is committed the feed is locked\n// and there is a valid, non-empty value to consume.\nfunc (f Feed) Value() (feed.Value, string, bool) {\n\treturn f.storage.GetLatest(), f.valueDataType, f.isLocked\n}\n\n// MarshalJSON marshals the components of the feed that are needed for\n// an agent to execute tasks and send values for ingestion.\nfunc (f Feed) MarshalJSON() ([]byte, error) {\n\tbuf := new(bytes.Buffer)\n\tw := bufio.NewWriter(buf)\n\n\tw.Write([]byte(\n\t\t`{\"id\":\"` + f.id +\n\t\t\t`\",\"type\":\"` + ufmt.Sprintf(\"%d\", int(f.Type())) +\n\t\t\t`\",\"value_type\":\"` + f.valueDataType +\n\t\t\t`\",\"tasks\":[`),\n\t)\n\n\tfirst := true\n\tfor _, task := range f.tasks {\n\t\tif !first {\n\t\t\tw.WriteString(\",\")\n\t\t}\n\n\t\ttaskJSON, err := task.MarshalJSON()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tw.Write(taskJSON)\n\t\tfirst = false\n\t}\n\n\tw.Write([]byte(\"]}\"))\n\tw.Flush()\n\n\treturn buf.Bytes(), nil\n}\n\n// Tasks returns the feed's tasks. This allows task consumers to extract task\n// contents without having to marshal the entire feed.\nfunc (f Feed) Tasks() []feed.Task {\n\treturn f.tasks\n}\n\n// IsActive returns true if the feed is accepting ingestion requests from agents.\nfunc (f Feed) IsActive() bool {\n\treturn !f.isLocked\n}\n" + }, + { + "name": "feed_test.gno", + "body": "package static_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/gnorkle/feed\"\n\t\"gno.land/p/demo/gnorkle/feeds/static\"\n\t\"gno.land/p/demo/gnorkle/gnorkle\"\n\t\"gno.land/p/demo/gnorkle/ingester\"\n\t\"gno.land/p/demo/gnorkle/message\"\n\t\"gno.land/p/demo/gnorkle/storage/simple\"\n)\n\ntype mockIngester struct {\n\tcanAutoCommit bool\n\tingestErr error\n\tcommitErr error\n\tvalue string\n\tproviderAddress string\n}\n\nfunc (i mockIngester) Type() ingester.Type {\n\treturn ingester.Type(0)\n}\n\nfunc (i *mockIngester) Ingest(value, providerAddress string) (bool, error) {\n\tif i.ingestErr != nil {\n\t\treturn false, i.ingestErr\n\t}\n\n\ti.value = value\n\ti.providerAddress = providerAddress\n\treturn i.canAutoCommit, nil\n}\n\nfunc (i *mockIngester) CommitValue(storage gnorkle.Storage, providerAddress string) error {\n\tif i.commitErr != nil {\n\t\treturn i.commitErr\n\t}\n\n\treturn storage.Put(i.value)\n}\n\nfunc TestNewSingleValueFeed(t *testing.T) {\n\tstaticFeed := static.NewSingleValueFeed(\"1\", \"\")\n\tif staticFeed.ID() != \"1\" {\n\t\tt.Errorf(\"expected ID to be 1, got %s\", staticFeed.ID())\n\t}\n\n\tif staticFeed.Type() != feed.TypeStatic {\n\t\tt.Errorf(\"expected static feed type, got %s\", staticFeed.Type())\n\t}\n}\n\nfunc TestFeed_Ingest(t *testing.T) {\n\tvar undefinedFeed *static.Feed\n\tif undefinedFeed.Ingest(\"\", \"\", \"\") != feed.ErrUndefined {\n\t\tt.Errorf(\"expected ErrUndefined, got nil\")\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\tingester *mockIngester\n\t\tverifyIsLocked bool\n\t\tdoCommit bool\n\t\tfuncType message.FuncType\n\t\tmsg string\n\t\tproviderAddress string\n\t\texpFeedValueString string\n\t\texpErrText string\n\t\texpIsActive bool\n\t}{\n\t\t{\n\t\t\tname: \"func invalid error\",\n\t\t\tingester: \u0026mockIngester{},\n\t\t\tfuncType: message.FuncType(\"derp\"),\n\t\t\texpErrText: \"invalid message function derp\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"func ingest ingest error\",\n\t\t\tingester: \u0026mockIngester{\n\t\t\t\tingestErr: errors.New(\"ingest error\"),\n\t\t\t},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\texpErrText: \"ingest error\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"func ingest commit error\",\n\t\t\tingester: \u0026mockIngester{\n\t\t\t\tcommitErr: errors.New(\"commit error\"),\n\t\t\t\tcanAutoCommit: true,\n\t\t\t},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\texpErrText: \"commit error\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"func commit commit error\",\n\t\t\tingester: \u0026mockIngester{\n\t\t\t\tcommitErr: errors.New(\"commit error\"),\n\t\t\t\tcanAutoCommit: true,\n\t\t\t},\n\t\t\tfuncType: message.FuncTypeCommit,\n\t\t\texpErrText: \"commit error\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"only ingest\",\n\t\t\tingester: \u0026mockIngester{},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\tmsg: \"still active feed\",\n\t\t\tproviderAddress: \"gno1234\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ingest autocommit\",\n\t\t\tingester: \u0026mockIngester{canAutoCommit: true},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\tmsg: \"still active feed\",\n\t\t\tproviderAddress: \"gno1234\",\n\t\t\texpFeedValueString: \"still active feed\",\n\t\t\tverifyIsLocked: true,\n\t\t},\n\t\t{\n\t\t\tname: \"commit no value\",\n\t\t\tingester: \u0026mockIngester{},\n\t\t\tfuncType: message.FuncTypeCommit,\n\t\t\tmsg: \"shouldn't be stored\",\n\t\t\tverifyIsLocked: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ingest then commmit\",\n\t\t\tingester: \u0026mockIngester{},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\tmsg: \"blahblah\",\n\t\t\tdoCommit: true,\n\t\t\texpFeedValueString: \"blahblah\",\n\t\t\tverifyIsLocked: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstaticFeed := static.NewFeed(\n\t\t\t\t\"1\",\n\t\t\t\t\"string\",\n\t\t\t\ttt.ingester,\n\t\t\t\tsimple.NewStorage(1),\n\t\t\t\tnil,\n\t\t\t)\n\n\t\t\tvar errText string\n\t\t\tif err := staticFeed.Ingest(tt.funcType, tt.msg, tt.providerAddress); err != nil {\n\t\t\t\terrText = err.Error()\n\t\t\t}\n\n\t\t\tif errText != tt.expErrText {\n\t\t\t\tt.Fatalf(\"expected error text %s, got %s\", tt.expErrText, errText)\n\t\t\t}\n\n\t\t\tif tt.doCommit {\n\t\t\t\terr := staticFeed.Ingest(message.FuncTypeCommit, \"\", \"\")\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"follow up commit failed: %s\", err.Error())\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif tt.verifyIsLocked {\n\t\t\t\terrText = \"\"\n\t\t\t\tif err := staticFeed.Ingest(tt.funcType, tt.msg, tt.providerAddress); err != nil {\n\t\t\t\t\terrText = err.Error()\n\t\t\t\t}\n\n\t\t\t\tif errText != \"feed locked\" {\n\t\t\t\t\tt.Fatalf(\"expected error text feed locked, got %s\", errText)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif tt.ingester.providerAddress != tt.providerAddress {\n\t\t\t\tt.Errorf(\"expected provider address %s, got %s\", tt.providerAddress, tt.ingester.providerAddress)\n\t\t\t}\n\n\t\t\tfeedValue, dataType, isLocked := staticFeed.Value()\n\t\t\tif feedValue.String != tt.expFeedValueString {\n\t\t\t\tt.Errorf(\"expected feed value string %s, got %s\", tt.expFeedValueString, feedValue.String)\n\t\t\t}\n\n\t\t\tif dataType != \"string\" {\n\t\t\t\tt.Errorf(\"expected data type string, got %s\", dataType)\n\t\t\t}\n\n\t\t\tif isLocked != tt.verifyIsLocked {\n\t\t\t\tt.Errorf(\"expected is locked %t, got %t\", tt.verifyIsLocked, isLocked)\n\t\t\t}\n\n\t\t\tif staticFeed.IsActive() != tt.expIsActive {\n\t\t\t\tt.Errorf(\"expected is active %t, got %t\", tt.expIsActive, staticFeed.IsActive())\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype mockTask struct {\n\terr error\n\tvalue string\n}\n\nfunc (t mockTask) MarshalJSON() ([]byte, error) {\n\tif t.err != nil {\n\t\treturn nil, t.err\n\t}\n\n\treturn []byte(`{\"value\":\"` + t.value + `\"}`), nil\n}\n\nfunc TestFeed_Tasks(t *testing.T) {\n\tid := \"99\"\n\tvalueDataType := \"int\"\n\n\ttests := []struct {\n\t\tname string\n\t\ttasks []feed.Task\n\t\texpErrText string\n\t\texpJSON string\n\t}{\n\t\t{\n\t\t\tname: \"no tasks\",\n\t\t\texpJSON: `{\"id\":\"99\",\"type\":\"0\",\"value_type\":\"int\",\"tasks\":[]}`,\n\t\t},\n\t\t{\n\t\t\tname: \"marshal error\",\n\t\t\ttasks: []feed.Task{\n\t\t\t\tmockTask{err: errors.New(\"marshal error\")},\n\t\t\t},\n\t\t\texpErrText: \"marshal error\",\n\t\t},\n\t\t{\n\t\t\tname: \"one task\",\n\t\t\ttasks: []feed.Task{\n\t\t\t\tmockTask{value: \"single\"},\n\t\t\t},\n\t\t\texpJSON: `{\"id\":\"99\",\"type\":\"0\",\"value_type\":\"int\",\"tasks\":[{\"value\":\"single\"}]}`,\n\t\t},\n\t\t{\n\t\t\tname: \"two tasks\",\n\t\t\ttasks: []feed.Task{\n\t\t\t\tmockTask{value: \"first\"},\n\t\t\t\tmockTask{value: \"second\"},\n\t\t\t},\n\t\t\texpJSON: `{\"id\":\"99\",\"type\":\"0\",\"value_type\":\"int\",\"tasks\":[{\"value\":\"first\"},{\"value\":\"second\"}]}`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstaticFeed := static.NewSingleValueFeed(\n\t\t\t\tid,\n\t\t\t\tvalueDataType,\n\t\t\t\ttt.tasks...,\n\t\t\t)\n\n\t\t\tif len(staticFeed.Tasks()) != len(tt.tasks) {\n\t\t\t\tt.Fatalf(\"expected %d tasks, got %d\", len(tt.tasks), len(staticFeed.Tasks()))\n\t\t\t}\n\n\t\t\tvar errText string\n\t\t\tjson, err := staticFeed.MarshalJSON()\n\t\t\tif err != nil {\n\t\t\t\terrText = err.Error()\n\t\t\t}\n\n\t\t\tif errText != tt.expErrText {\n\t\t\t\tt.Fatalf(\"expected error text %s, got %s\", tt.expErrText, errText)\n\t\t\t}\n\n\t\t\tif string(json) != tt.expJSON {\n\t\t\t\tt.Errorf(\"expected json %s, got %s\", tt.expJSON, string(json))\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/gnorkle/feeds/static\n\nrequire (\n\tgno.land/p/demo/gnorkle/feed v0.0.0-latest\n\tgno.land/p/demo/gnorkle/gnorkle v0.0.0-latest\n\tgno.land/p/demo/gnorkle/ingester v0.0.0-latest\n\tgno.land/p/demo/gnorkle/ingesters/single v0.0.0-latest\n\tgno.land/p/demo/gnorkle/message v0.0.0-latest\n\tgno.land/p/demo/gnorkle/storage/simple v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "exts", + "path": "gno.land/p/demo/grc/exts", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/grc/exts\n" + }, + { + "name": "token_metadata.gno", + "body": "package exts\n\ntype TokenMetadata interface {\n\t// Returns the name of the token.\n\tGetName() string\n\n\t// Returns the symbol of the token, usually a shorter version of the\n\t// name.\n\tGetSymbol() string\n\n\t// Returns the decimals places of the token.\n\tGetDecimals() uint\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "grc1155", + "path": "gno.land/p/demo/grc/grc1155", + "files": [ + { + "name": "README.md", + "body": "# GRC-1155 Spec: Multi Token Standard\n\nGRC1155 is a specification for managing multiple tokens based on Gnoland. The name and design is based on Ethereum's ERC1155 standard.\n\n## See also:\n\n[ERC-1155 Spec][erc-1155]\n\n[erc-1155]: https://eips.ethereum.org/EIPS/eip-1155" + }, + { + "name": "basic_grc1155_token.gno", + "body": "package grc1155\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype basicGRC1155Token struct {\n\turi string\n\tbalances avl.Tree // \"TokenId:Address\" -\u003e uint64\n\toperatorApprovals avl.Tree // \"OwnerAddress:OperatorAddress\" -\u003e bool\n}\n\nvar _ IGRC1155 = (*basicGRC1155Token)(nil)\n\n// Returns new basic GRC1155 token\nfunc NewBasicGRC1155Token(uri string) *basicGRC1155Token {\n\treturn \u0026basicGRC1155Token{\n\t\turi: uri,\n\t\tbalances: avl.Tree{},\n\t\toperatorApprovals: avl.Tree{},\n\t}\n}\n\nfunc (s *basicGRC1155Token) Uri() string { return s.uri }\n\n// BalanceOf returns the input address's balance of the token type requested\nfunc (s *basicGRC1155Token) BalanceOf(addr std.Address, tid TokenID) (uint64, error) {\n\tif !isValidAddress(addr) {\n\t\treturn 0, ErrInvalidAddress\n\t}\n\n\tkey := string(tid) + \":\" + addr.String()\n\tbalance, found := s.balances.Get(key)\n\tif !found {\n\t\treturn 0, nil\n\t}\n\n\treturn balance.(uint64), nil\n}\n\n// BalanceOfBatch returns the balance of multiple account/token pairs\nfunc (s *basicGRC1155Token) BalanceOfBatch(owners []std.Address, batch []TokenID) ([]uint64, error) {\n\tif len(owners) != len(batch) {\n\t\treturn nil, ErrMismatchLength\n\t}\n\n\tbalanceOfBatch := make([]uint64, len(owners))\n\n\tfor i := 0; i \u003c len(owners); i++ {\n\t\tbalanceOfBatch[i], _ = s.BalanceOf(owners[i], batch[i])\n\t}\n\n\treturn balanceOfBatch, nil\n}\n\n// SetApprovalForAll can approve the operator to operate on all tokens\nfunc (s *basicGRC1155Token) SetApprovalForAll(operator std.Address, approved bool) error {\n\tif !isValidAddress(operator) {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tcaller := std.GetOrigCaller()\n\treturn s.setApprovalForAll(caller, operator, approved)\n}\n\n// IsApprovedForAll returns true if operator is the owner or is approved for all by the owner.\n// Otherwise, returns false\nfunc (s *basicGRC1155Token) IsApprovedForAll(owner, operator std.Address) bool {\n\tif operator == owner {\n\t\treturn true\n\t}\n\tkey := owner.String() + \":\" + operator.String()\n\t_, found := s.operatorApprovals.Get(key)\n\tif !found {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// Safely transfers `tokenId` token from `from` to `to`, checking that\n// contract recipients are aware of the GRC1155 protocol to prevent\n// tokens from being forever locked.\nfunc (s *basicGRC1155Token) SafeTransferFrom(from, to std.Address, tid TokenID, amount uint64) error {\n\tcaller := std.GetOrigCaller()\n\tif !s.IsApprovedForAll(caller, from) {\n\t\treturn ErrCallerIsNotOwnerOrApproved\n\t}\n\n\terr := s.safeBatchTransferFrom(from, to, []TokenID{tid}, []uint64{amount})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !s.doSafeTransferAcceptanceCheck(caller, from, to, tid, amount) {\n\t\treturn ErrTransferToRejectedOrNonGRC1155Receiver\n\t}\n\n\temit(\u0026TransferSingleEvent{caller, from, to, tid, amount})\n\n\treturn nil\n}\n\n// Safely transfers a `batch` of tokens from `from` to `to`, checking that\n// contract recipients are aware of the GRC1155 protocol to prevent\n// tokens from being forever locked.\nfunc (s *basicGRC1155Token) SafeBatchTransferFrom(from, to std.Address, batch []TokenID, amounts []uint64) error {\n\tcaller := std.GetOrigCaller()\n\tif !s.IsApprovedForAll(caller, from) {\n\t\treturn ErrCallerIsNotOwnerOrApproved\n\t}\n\n\terr := s.safeBatchTransferFrom(from, to, batch, amounts)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !s.doSafeBatchTransferAcceptanceCheck(caller, from, to, batch, amounts) {\n\t\treturn ErrTransferToRejectedOrNonGRC1155Receiver\n\t}\n\n\temit(\u0026TransferBatchEvent{caller, from, to, batch, amounts})\n\n\treturn nil\n}\n\n// Creates `amount` tokens of token type `id`, and assigns them to `to`. Also checks that\n// contract recipients are using GRC1155 protocol.\nfunc (s *basicGRC1155Token) SafeMint(to std.Address, tid TokenID, amount uint64) error {\n\tcaller := std.GetOrigCaller()\n\n\terr := s.mintBatch(to, []TokenID{tid}, []uint64{amount})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !s.doSafeTransferAcceptanceCheck(caller, zeroAddress, to, tid, amount) {\n\t\treturn ErrTransferToRejectedOrNonGRC1155Receiver\n\t}\n\n\temit(\u0026TransferSingleEvent{caller, zeroAddress, to, tid, amount})\n\n\treturn nil\n}\n\n// Batch version of `SafeMint()`. Also checks that\n// contract recipients are using GRC1155 protocol.\nfunc (s *basicGRC1155Token) SafeBatchMint(to std.Address, batch []TokenID, amounts []uint64) error {\n\tcaller := std.GetOrigCaller()\n\n\terr := s.mintBatch(to, batch, amounts)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !s.doSafeBatchTransferAcceptanceCheck(caller, zeroAddress, to, batch, amounts) {\n\t\treturn ErrTransferToRejectedOrNonGRC1155Receiver\n\t}\n\n\temit(\u0026TransferBatchEvent{caller, zeroAddress, to, batch, amounts})\n\n\treturn nil\n}\n\n// Destroys `amount` tokens of token type `id` from `from`.\nfunc (s *basicGRC1155Token) Burn(from std.Address, tid TokenID, amount uint64) error {\n\tcaller := std.GetOrigCaller()\n\n\terr := s.burnBatch(from, []TokenID{tid}, []uint64{amount})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\temit(\u0026TransferSingleEvent{caller, from, zeroAddress, tid, amount})\n\n\treturn nil\n}\n\n// Batch version of `Burn()`\nfunc (s *basicGRC1155Token) BatchBurn(from std.Address, batch []TokenID, amounts []uint64) error {\n\tcaller := std.GetOrigCaller()\n\n\terr := s.burnBatch(from, batch, amounts)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\temit(\u0026TransferBatchEvent{caller, from, zeroAddress, batch, amounts})\n\n\treturn nil\n}\n\n/* Helper methods */\n\n// Helper for SetApprovalForAll(): approve `operator` to operate on all of `owner` tokens\nfunc (s *basicGRC1155Token) setApprovalForAll(owner, operator std.Address, approved bool) error {\n\tif owner == operator {\n\t\treturn nil\n\t}\n\n\tkey := owner.String() + \":\" + operator.String()\n\tif approved {\n\t\ts.operatorApprovals.Set(key, approved)\n\t} else {\n\t\ts.operatorApprovals.Remove(key)\n\t}\n\n\temit(\u0026ApprovalForAllEvent{owner, operator, approved})\n\n\treturn nil\n}\n\n// Helper for SafeTransferFrom() and SafeBatchTransferFrom()\nfunc (s *basicGRC1155Token) safeBatchTransferFrom(from, to std.Address, batch []TokenID, amounts []uint64) error {\n\tif len(batch) != len(amounts) {\n\t\treturn ErrMismatchLength\n\t}\n\tif !isValidAddress(from) || !isValidAddress(to) {\n\t\treturn ErrInvalidAddress\n\t}\n\tif from == to {\n\t\treturn ErrCannotTransferToSelf\n\t}\n\n\tcaller := std.GetOrigCaller()\n\ts.beforeTokenTransfer(caller, from, to, batch, amounts)\n\n\tfor i := 0; i \u003c len(batch); i++ {\n\t\ttid := batch[i]\n\t\tamount := amounts[i]\n\t\tfromBalance, err := s.BalanceOf(from, tid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif fromBalance \u003c amount {\n\t\t\treturn ErrInsufficientBalance\n\t\t}\n\t\ttoBalance, err := s.BalanceOf(to, tid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfromBalance -= amount\n\t\ttoBalance += amount\n\t\tfromBalanceKey := string(tid) + \":\" + from.String()\n\t\ttoBalanceKey := string(tid) + \":\" + to.String()\n\t\ts.balances.Set(fromBalanceKey, fromBalance)\n\t\ts.balances.Set(toBalanceKey, toBalance)\n\t}\n\n\ts.afterTokenTransfer(caller, from, to, batch, amounts)\n\n\treturn nil\n}\n\n// Helper for SafeMint() and SafeBatchMint()\nfunc (s *basicGRC1155Token) mintBatch(to std.Address, batch []TokenID, amounts []uint64) error {\n\tif len(batch) != len(amounts) {\n\t\treturn ErrMismatchLength\n\t}\n\tif !isValidAddress(to) {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tcaller := std.GetOrigCaller()\n\ts.beforeTokenTransfer(caller, zeroAddress, to, batch, amounts)\n\n\tfor i := 0; i \u003c len(batch); i++ {\n\t\ttid := batch[i]\n\t\tamount := amounts[i]\n\t\ttoBalance, err := s.BalanceOf(to, tid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttoBalance += amount\n\t\ttoBalanceKey := string(tid) + \":\" + to.String()\n\t\ts.balances.Set(toBalanceKey, toBalance)\n\t}\n\n\ts.afterTokenTransfer(caller, zeroAddress, to, batch, amounts)\n\n\treturn nil\n}\n\n// Helper for Burn() and BurnBatch()\nfunc (s *basicGRC1155Token) burnBatch(from std.Address, batch []TokenID, amounts []uint64) error {\n\tif len(batch) != len(amounts) {\n\t\treturn ErrMismatchLength\n\t}\n\tif !isValidAddress(from) {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tcaller := std.GetOrigCaller()\n\ts.beforeTokenTransfer(caller, from, zeroAddress, batch, amounts)\n\n\tfor i := 0; i \u003c len(batch); i++ {\n\t\ttid := batch[i]\n\t\tamount := amounts[i]\n\t\tfromBalance, err := s.BalanceOf(from, tid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif fromBalance \u003c amount {\n\t\t\treturn ErrBurnAmountExceedsBalance\n\t\t}\n\t\tfromBalance -= amount\n\t\tfromBalanceKey := string(tid) + \":\" + from.String()\n\t\ts.balances.Set(fromBalanceKey, fromBalance)\n\t}\n\n\ts.afterTokenTransfer(caller, from, zeroAddress, batch, amounts)\n\n\treturn nil\n}\n\nfunc (s *basicGRC1155Token) setUri(newUri string) {\n\ts.uri = newUri\n\temit(\u0026UpdateURIEvent{newUri})\n}\n\nfunc (s *basicGRC1155Token) beforeTokenTransfer(operator, from, to std.Address, batch []TokenID, amounts []uint64) {\n\t// TODO: Implementation\n}\n\nfunc (s *basicGRC1155Token) afterTokenTransfer(operator, from, to std.Address, batch []TokenID, amounts []uint64) {\n\t// TODO: Implementation\n}\n\nfunc (s *basicGRC1155Token) doSafeTransferAcceptanceCheck(operator, from, to std.Address, tid TokenID, amount uint64) bool {\n\t// TODO: Implementation\n\treturn true\n}\n\nfunc (s *basicGRC1155Token) doSafeBatchTransferAcceptanceCheck(operator, from, to std.Address, batch []TokenID, amounts []uint64) bool {\n\t// TODO: Implementation\n\treturn true\n}\n\nfunc (s *basicGRC1155Token) RenderHome() (str string) {\n\tstr += ufmt.Sprintf(\"# URI:%s\\n\", s.uri)\n\n\treturn\n}\n" + }, + { + "name": "basic_grc1155_token_test.gno", + "body": "package grc1155\n\nimport (\n\t\"std\"\n\t\"testing\"\n)\n\nconst dummyURI = \"ipfs://xyz\"\n\nfunc TestNewBasicGRC1155Token(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\treturn t.Errorf(\"should not be nil\")\n\t}\n}\n\nfunc TestUri(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\turi := dummy.Uri()\n\tif uri != dummyURI {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", dummyURI, uri)\n\t}\n}\n\nfunc TestBalanceOf(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\ttid1 := TokenID(\"1\")\n\ttid2 := TokenID(\"2\")\n\n\tbalanceZeroAddressOfToken1, err := dummy.BalanceOf(zeroAddress, tid1)\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\tbalanceAddr1OfToken1, err := dummy.BalanceOf(addr1, tid1)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif balanceAddr1OfToken1 != 0 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 0, balanceAddr1OfToken1)\n\t}\n\n\tdummy.mintBatch(addr1, []TokenID{tid1, tid2}, []uint64{10, 100})\n\tdummy.mintBatch(addr2, []TokenID{tid1}, []uint64{20})\n\n\tbalanceAddr1OfToken1, err = dummy.BalanceOf(addr1, tid1)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tbalanceAddr1OfToken2, err := dummy.BalanceOf(addr1, tid2)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tbalanceAddr2OfToken1, err := dummy.BalanceOf(addr2, tid1)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tif balanceAddr1OfToken1 != 10 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 10, balanceAddr1OfToken1)\n\t}\n\tif balanceAddr1OfToken2 != 100 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 100, balanceAddr1OfToken2)\n\t}\n\tif balanceAddr2OfToken1 != 20 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 20, balanceAddr2OfToken1)\n\t}\n}\n\nfunc TestBalanceOfBatch(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\ttid1 := TokenID(\"1\")\n\ttid2 := TokenID(\"2\")\n\n\tbalanceBatch, err := dummy.BalanceOfBatch([]std.Address{addr1, addr2}, []TokenID{tid1, tid2})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tif balanceBatch[0] != 0 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 0, balanceBatch[0])\n\t}\n\tif balanceBatch[1] != 0 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 0, balanceBatch[1])\n\t}\n\n\tdummy.mintBatch(addr1, []TokenID{tid1}, []uint64{10})\n\tdummy.mintBatch(addr2, []TokenID{tid2}, []uint64{20})\n\n\tbalanceBatch, err = dummy.BalanceOfBatch([]std.Address{addr1, addr2}, []TokenID{tid1, tid2})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tif balanceBatch[0] != 10 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 10, balanceBatch[0])\n\t}\n\tif balanceBatch[1] != 20 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 20, balanceBatch[1])\n\t}\n}\n\nfunc TestIsApprovedForAll(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\tisApprovedForAll := dummy.IsApprovedForAll(addr1, addr2)\n\tif isApprovedForAll != false {\n\t\tt.Errorf(\"expected: (%v), got: (%v)\", false, isApprovedForAll)\n\t}\n}\n\nfunc TestSetApprovalForAll(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tcaller := std.GetOrigCaller()\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\tisApprovedForAll := dummy.IsApprovedForAll(caller, addr)\n\tif isApprovedForAll != false {\n\t\tt.Errorf(\"expected: (%v), got: (%v)\", false, isApprovedForAll)\n\t}\n\n\terr := dummy.SetApprovalForAll(addr, true)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tisApprovedForAll = dummy.IsApprovedForAll(caller, addr)\n\tif isApprovedForAll != true {\n\t\tt.Errorf(\"expected: (%v), got: (%v)\", true, isApprovedForAll)\n\t}\n\n\terr = dummy.SetApprovalForAll(addr, false)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tisApprovedForAll = dummy.IsApprovedForAll(caller, addr)\n\tif isApprovedForAll != false {\n\t\tt.Errorf(\"expected: (%v), got: (%v)\", false, isApprovedForAll)\n\t}\n}\n\nfunc TestSafeTransferFrom(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tcaller := std.GetOrigCaller()\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\ttid := TokenID(\"1\")\n\n\tdummy.mintBatch(caller, []TokenID{tid}, []uint64{100})\n\n\terr := dummy.SafeTransferFrom(caller, zeroAddress, tid, 10)\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\n\terr = dummy.SafeTransferFrom(caller, addr, tid, 160)\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\n\terr = dummy.SafeTransferFrom(caller, addr, tid, 60)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\t// Check balance of caller after transfer\n\tbalanceOfCaller, err := dummy.BalanceOf(caller, tid)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif balanceOfCaller != 40 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 40, balanceOfCaller)\n\t}\n\n\t// Check balance of addr after transfer\n\tbalanceOfAddr, err := dummy.BalanceOf(addr, tid)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif balanceOfAddr != 60 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 60, balanceOfAddr)\n\t}\n}\n\nfunc TestSafeBatchTransferFrom(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tcaller := std.GetOrigCaller()\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\ttid1 := TokenID(\"1\")\n\ttid2 := TokenID(\"2\")\n\n\tdummy.mintBatch(caller, []TokenID{tid1, tid2}, []uint64{10, 100})\n\n\terr := dummy.SafeBatchTransferFrom(caller, zeroAddress, []TokenID{tid1, tid2}, []uint64{4, 60})\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.SafeBatchTransferFrom(caller, addr, []TokenID{tid1, tid2}, []uint64{40, 60})\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.SafeBatchTransferFrom(caller, addr, []TokenID{tid1}, []uint64{40, 60})\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.SafeBatchTransferFrom(caller, addr, []TokenID{tid1, tid2}, []uint64{4, 60})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tbalanceBatch, err := dummy.BalanceOfBatch([]std.Address{caller, addr, caller, addr}, []TokenID{tid1, tid1, tid2, tid2})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\t// Check token1's balance of caller after batch transfer\n\tif balanceBatch[0] != 6 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 6, balanceBatch[0])\n\t}\n\n\t// Check token1's balance of addr after batch transfer\n\tif balanceBatch[1] != 4 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 4, balanceBatch[1])\n\t}\n\n\t// Check token2's balance of caller after batch transfer\n\tif balanceBatch[2] != 40 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 40, balanceBatch[2])\n\t}\n\n\t// Check token2's balance of addr after batch transfer\n\tif balanceBatch[3] != 60 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 60, balanceBatch[3])\n\t}\n}\n\nfunc TestSafeMint(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\ttid1 := TokenID(\"1\")\n\ttid2 := TokenID(\"2\")\n\n\terr := dummy.SafeMint(zeroAddress, tid1, 100)\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.SafeMint(addr1, tid1, 100)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\terr = dummy.SafeMint(addr1, tid2, 200)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\terr = dummy.SafeMint(addr2, tid1, 50)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tbalanceBatch, err := dummy.BalanceOfBatch([]std.Address{addr1, addr2, addr1}, []TokenID{tid1, tid1, tid2})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\t// Check token1's balance of addr1 after mint\n\tif balanceBatch[0] != 100 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 100, balanceBatch[0])\n\t}\n\t// Check token1's balance of addr2 after mint\n\tif balanceBatch[1] != 50 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 50, balanceBatch[1])\n\t}\n\t// Check token2's balance of addr1 after mint\n\tif balanceBatch[2] != 200 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 200, balanceBatch[2])\n\t}\n}\n\nfunc TestSafeBatchMint(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\ttid1 := TokenID(\"1\")\n\ttid2 := TokenID(\"2\")\n\n\terr := dummy.SafeBatchMint(zeroAddress, []TokenID{tid1, tid2}, []uint64{100, 200})\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.SafeBatchMint(addr1, []TokenID{tid1, tid2}, []uint64{100, 200})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\terr = dummy.SafeBatchMint(addr2, []TokenID{tid1, tid2}, []uint64{300, 400})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tbalanceBatch, err := dummy.BalanceOfBatch([]std.Address{addr1, addr2, addr1, addr2}, []TokenID{tid1, tid1, tid2, tid2})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\t// Check token1's balance of addr1 after batch mint\n\tif balanceBatch[0] != 100 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 100, balanceBatch[0])\n\t}\n\t// Check token1's balance of addr2 after batch mint\n\tif balanceBatch[1] != 300 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 300, balanceBatch[1])\n\t}\n\t// Check token2's balance of addr1 after batch mint\n\tif balanceBatch[2] != 200 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 200, balanceBatch[2])\n\t}\n\t// Check token2's balance of addr2 after batch mint\n\tif balanceBatch[3] != 400 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 400, balanceBatch[3])\n\t}\n}\n\nfunc TestBurn(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\ttid1 := TokenID(\"1\")\n\ttid2 := TokenID(\"2\")\n\n\tdummy.mintBatch(addr, []TokenID{tid1, tid2}, []uint64{100, 200})\n\terr := dummy.Burn(zeroAddress, tid1, 60)\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.Burn(addr, tid1, 160)\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.Burn(addr, tid1, 60)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\terr = dummy.Burn(addr, tid2, 60)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tbalanceBatch, err := dummy.BalanceOfBatch([]std.Address{addr, addr}, []TokenID{tid1, tid2})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\t// Check token1's balance of addr after burn\n\tif balanceBatch[0] != 40 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 40, balanceBatch[0])\n\t}\n\t// Check token2's balance of addr after burn\n\tif balanceBatch[1] != 140 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 140, balanceBatch[1])\n\t}\n}\n\nfunc TestBatchBurn(t *testing.T) {\n\tdummy := NewBasicGRC1155Token(dummyURI)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\ttid1 := TokenID(\"1\")\n\ttid2 := TokenID(\"2\")\n\n\tdummy.mintBatch(addr, []TokenID{tid1, tid2}, []uint64{100, 200})\n\terr := dummy.BatchBurn(zeroAddress, []TokenID{tid1, tid2}, []uint64{60, 60})\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.BatchBurn(addr, []TokenID{tid1, tid2}, []uint64{160, 60})\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\terr = dummy.BatchBurn(addr, []TokenID{tid1, tid2}, []uint64{60, 60})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tbalanceBatch, err := dummy.BalanceOfBatch([]std.Address{addr, addr}, []TokenID{tid1, tid2})\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\t// Check token1's balance of addr after batch burn\n\tif balanceBatch[0] != 40 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 40, balanceBatch[0])\n\t}\n\t// Check token2's balance of addr after batch burn\n\tif balanceBatch[1] != 140 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 140, balanceBatch[1])\n\t}\n}\n" + }, + { + "name": "errors.gno", + "body": "package grc1155\n\nimport \"errors\"\n\nvar (\n\tErrInvalidAddress = errors.New(\"invalid address\")\n\tErrMismatchLength = errors.New(\"accounts and ids length mismatch\")\n\tErrCannotTransferToSelf = errors.New(\"cannot send transfer to self\")\n\tErrTransferToRejectedOrNonGRC1155Receiver = errors.New(\"transfer to rejected or non GRC1155Receiver implementer\")\n\tErrCallerIsNotOwnerOrApproved = errors.New(\"caller is not token owner or approved\")\n\tErrInsufficientBalance = errors.New(\"insufficient balance for transfer\")\n\tErrBurnAmountExceedsBalance = errors.New(\"burn amount exceeds balance\")\n)\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/grc/grc1155\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "igrc1155.gno", + "body": "package grc1155\n\nimport \"std\"\n\ntype IGRC1155 interface {\n\tSafeTransferFrom(from, to std.Address, tid TokenID, amount uint64) error\n\tSafeBatchTransferFrom(from, to std.Address, batch []TokenID, amounts []uint64) error\n\tBalanceOf(owner std.Address, tid TokenID) (uint64, error)\n\tBalanceOfBatch(owners []std.Address, batch []TokenID) ([]uint64, error)\n\tSetApprovalForAll(operator std.Address, approved bool) error\n\tIsApprovedForAll(owner, operator std.Address) bool\n}\n\ntype TokenID string\n\ntype TransferSingleEvent struct {\n\tOperator std.Address\n\tFrom std.Address\n\tTo std.Address\n\tTokenID TokenID\n\tAmount uint64\n}\n\ntype TransferBatchEvent struct {\n\tOperator std.Address\n\tFrom std.Address\n\tTo std.Address\n\tBatch []TokenID\n\tAmounts []uint64\n}\n\ntype ApprovalForAllEvent struct {\n\tOwner std.Address\n\tOperator std.Address\n\tApproved bool\n}\n\ntype UpdateURIEvent struct {\n\tURI string\n}\n" + }, + { + "name": "util.gno", + "body": "package grc1155\n\nimport (\n\t\"std\"\n)\n\nconst zeroAddress std.Address = \"\"\n\nfunc isValidAddress(addr std.Address) bool {\n\tif !addr.IsValid() {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc emit(event interface{}) {\n\t// TODO: setup a pubsub system here?\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "uassert", + "path": "gno.land/p/demo/uassert", + "files": [ + { + "name": "doc.gno", + "body": "package uassert // import \"gno.land/p/demo/uassert\"\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/uassert\n" + }, + { + "name": "helpers.gno", + "body": "package uassert\n\nimport \"strings\"\n\nfunc fail(t TestingT, customMsgs []string, failureMessage string, args ...interface{}) bool {\n\tcustomMsg := \"\"\n\tif len(customMsgs) \u003e 0 {\n\t\tcustomMsg = strings.Join(customMsgs, \" \")\n\t}\n\tif customMsg != \"\" {\n\t\tfailureMessage += \" - \" + customMsg\n\t}\n\tt.Errorf(failureMessage, args...)\n\treturn false\n}\n\nfunc autofail(t TestingT, success bool, customMsgs []string, failureMessage string, args ...interface{}) bool {\n\tif success {\n\t\treturn true\n\t}\n\treturn fail(t, customMsgs, failureMessage, args...)\n}\n\nfunc checkDidPanic(f func()) (didPanic bool, message string) {\n\tdidPanic = true\n\tdefer func() {\n\t\tr := recover()\n\n\t\tif r == nil {\n\t\t\tmessage = \"nil\"\n\t\t\treturn\n\t\t}\n\n\t\terr, ok := r.(error)\n\t\tif ok {\n\t\t\tmessage = err.Error()\n\t\t\treturn\n\t\t}\n\n\t\terrStr, ok := r.(string)\n\t\tif ok {\n\t\t\tmessage = errStr\n\t\t\treturn\n\t\t}\n\n\t\tmessage = \"recover: unsupported type\"\n\t}()\n\tf()\n\tdidPanic = false\n\treturn\n}\n" + }, + { + "name": "mock_test.gno", + "body": "package uassert\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype mockTestingT struct {\n\tfmt string\n\targs []interface{}\n}\n\n// --- interface mock\n\nvar _ TestingT = (*mockTestingT)(nil)\n\nfunc (mockT *mockTestingT) Helper() { /* noop */ }\nfunc (mockT *mockTestingT) Skip(args ...interface{}) { /* not implmented */ }\nfunc (mockT *mockTestingT) Fail() { /* not implmented */ }\nfunc (mockT *mockTestingT) FailNow() { /* not implmented */ }\nfunc (mockT *mockTestingT) Logf(fmt string, args ...interface{}) { /* noop */ }\n\nfunc (mockT *mockTestingT) Fatalf(fmt string, args ...interface{}) {\n\tmockT.fmt = \"fatal: \" + fmt\n\tmockT.args = args\n}\n\nfunc (mockT *mockTestingT) Errorf(fmt string, args ...interface{}) {\n\tmockT.fmt = \"error: \" + fmt\n\tmockT.args = args\n}\n\n// --- helpers\n\nfunc (mockT *mockTestingT) actualString() string {\n\tres := fmt.Sprintf(mockT.fmt, mockT.args...)\n\tmockT.reset()\n\treturn res\n}\n\nfunc (mockT *mockTestingT) reset() {\n\tmockT.fmt = \"\"\n\tmockT.args = nil\n}\n\nfunc (mockT *mockTestingT) equals(t *testing.T, expected string) {\n\tactual := mockT.actualString()\n\n\tif expected != actual {\n\t\tt.Errorf(\"mockT differs:\\n- expected: %s\\n- actual: %s\\n\", expected, actual)\n\t}\n}\n\nfunc (mockT *mockTestingT) empty(t *testing.T) {\n\tif mockT.fmt != \"\" || mockT.args != nil {\n\t\tactual := mockT.actualString()\n\t\tt.Errorf(\"mockT should be empty, got %s\", actual)\n\t}\n}\n" + }, + { + "name": "types.gno", + "body": "package uassert\n\ntype TestingT interface {\n\tHelper()\n\tSkip(args ...interface{})\n\tFatalf(fmt string, args ...interface{})\n\tErrorf(fmt string, args ...interface{})\n\tLogf(fmt string, args ...interface{})\n\tFail()\n\tFailNow()\n}\n" + }, + { + "name": "uassert.gno", + "body": "// uassert is an adapted lighter version of https://github.com/stretchr/testify/assert.\npackage uassert\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// NoError asserts that a function returned no error (i.e. `nil`).\nfunc NoError(t TestingT, err error, msgs ...string) bool {\n\tt.Helper()\n\tif err != nil {\n\t\treturn fail(t, msgs, \"unexpected error: %s\", err.Error())\n\t}\n\treturn true\n}\n\n// Error asserts that a function returned an error (i.e. not `nil`).\nfunc Error(t TestingT, err error, msgs ...string) bool {\n\tt.Helper()\n\tif err == nil {\n\t\treturn fail(t, msgs, \"an error is expected but got nil\")\n\t}\n\treturn true\n}\n\n// ErrorContains asserts that a function returned an error (i.e. not `nil`)\n// and that the error contains the specified substring.\nfunc ErrorContains(t TestingT, err error, contains string, msgs ...string) bool {\n\tt.Helper()\n\n\tif !Error(t, err, msgs...) {\n\t\treturn false\n\t}\n\n\tactual := err.Error()\n\tif !strings.Contains(actual, contains) {\n\t\treturn fail(t, msgs, \"error %q does not contain %q\", actual, contains)\n\t}\n\n\treturn true\n}\n\n// True asserts that the specified value is true.\nfunc True(t TestingT, value bool, msgs ...string) bool {\n\tt.Helper()\n\tif !value {\n\t\treturn fail(t, msgs, \"should be true\")\n\t}\n\treturn true\n}\n\n// False asserts that the specified value is false.\nfunc False(t TestingT, value bool, msgs ...string) bool {\n\tt.Helper()\n\tif value {\n\t\treturn fail(t, msgs, \"should be false\")\n\t}\n\treturn true\n}\n\n// ErrorIs asserts the given error matches the target error\nfunc ErrorIs(t TestingT, err, target error, msgs ...string) bool {\n\tt.Helper()\n\n\tif err == nil || target == nil {\n\t\treturn err == target\n\t}\n\n\t// XXX: if errors.Is(err, target) return true\n\n\tif err.Error() != target.Error() {\n\t\treturn fail(t, msgs, \"error mismatch, expected %s, got %s\", target.Error(), err.Error())\n\t}\n\n\treturn true\n}\n\n// PanicsWithMessage asserts that the code inside the specified func panics,\n// and that the recovered panic value satisfies the given message\nfunc PanicsWithMessage(t TestingT, msg string, f func(), msgs ...string) bool {\n\tt.Helper()\n\n\tdidPanic, panicValue := checkDidPanic(f)\n\tif !didPanic {\n\t\treturn fail(t, msgs, \"func should panic\\n\\tPanic value:\\t%v\", panicValue)\n\t}\n\n\tif panicValue != msg {\n\t\treturn fail(t, msgs, \"func should panic with message:\\t%s\\n\\tPanic value:\\t%s\", msg, panicValue)\n\t}\n\treturn true\n}\n\n// NotPanics asserts that the code inside the specified func does NOT panic.\nfunc NotPanics(t TestingT, f func(), msgs ...string) bool {\n\tt.Helper()\n\n\tdidPanic, panicValue := checkDidPanic(f)\n\n\tif didPanic {\n\t\treturn fail(t, msgs, \"func should not panic\\n\\tPanic value:\\t%s\", panicValue)\n\t}\n\treturn true\n}\n\n// Equal asserts that two objects are equal.\nfunc Equal(t TestingT, expected, actual interface{}, msgs ...string) bool {\n\tt.Helper()\n\n\tif expected == nil || actual == nil {\n\t\treturn expected == actual\n\t}\n\n\t// XXX: errors\n\t// XXX: slices\n\t// XXX: pointers\n\n\tequal := false\n\tok_ := false\n\tes, as := \"unsupported type\", \"unsupported type\"\n\n\tswitch ev := expected.(type) {\n\tcase string:\n\t\tif av, ok := actual.(string); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = ev, av\n\t\t}\n\tcase std.Address:\n\t\tif av, ok := actual.(std.Address); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = string(ev), string(av)\n\t\t}\n\tcase int:\n\t\tif av, ok := actual.(int); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.Itoa(ev), strconv.Itoa(av)\n\t\t}\n\tcase int8:\n\t\tif av, ok := actual.(int8); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.Itoa(int(ev)), strconv.Itoa(int(av))\n\t\t}\n\tcase int16:\n\t\tif av, ok := actual.(int16); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.Itoa(int(ev)), strconv.Itoa(int(av))\n\t\t}\n\tcase int32:\n\t\tif av, ok := actual.(int32); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.Itoa(int(ev)), strconv.Itoa(int(av))\n\t\t}\n\tcase int64:\n\t\tif av, ok := actual.(int64); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.Itoa(int(ev)), strconv.Itoa(int(av))\n\t\t}\n\tcase uint:\n\t\tif av, ok := actual.(uint); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.FormatUint(uint64(ev), 10), strconv.FormatUint(uint64(av), 10)\n\t\t}\n\tcase uint8:\n\t\tif av, ok := actual.(uint8); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.FormatUint(uint64(ev), 10), strconv.FormatUint(uint64(av), 10)\n\t\t}\n\tcase uint16:\n\t\tif av, ok := actual.(uint16); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.FormatUint(uint64(ev), 10), strconv.FormatUint(uint64(av), 10)\n\t\t}\n\tcase uint32:\n\t\tif av, ok := actual.(uint32); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.FormatUint(uint64(ev), 10), strconv.FormatUint(uint64(av), 10)\n\t\t}\n\tcase uint64:\n\t\tif av, ok := actual.(uint64); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tes, as = strconv.FormatUint(ev, 10), strconv.FormatUint(av, 10)\n\t\t}\n\tcase bool:\n\t\tif av, ok := actual.(bool); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t\tif ev {\n\t\t\t\tes, as = \"true\", \"false\"\n\t\t\t} else {\n\t\t\t\tes, as = \"false\", \"true\"\n\t\t\t}\n\t\t}\n\tcase float32:\n\t\tif av, ok := actual.(float32); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t}\n\tcase float64:\n\t\tif av, ok := actual.(float64); ok {\n\t\t\tequal = ev == av\n\t\t\tok_ = true\n\t\t}\n\tdefault:\n\t\treturn fail(t, msgs, \"uassert.Equal: unsupported type\")\n\t}\n\n\t/*\n\t\t// XXX: implement stringer and other well known similar interfaces\n\t\ttype stringer interface{ String() string }\n\t\tif ev, ok := expected.(stringer); ok {\n\t\t\tif av, ok := actual.(stringer); ok {\n\t\t\t\tequal = ev.String() == av.String()\n\t\t\t\tok_ = true\n\t\t\t}\n\t\t}\n\t*/\n\n\tif !ok_ {\n\t\treturn fail(t, msgs, \"uassert.Equal: different types\") // XXX: display the types\n\t}\n\tif !equal {\n\t\treturn fail(t, msgs, \"uassert.Equal: same type but different value\\n\\texpected: %s\\n\\tactual: %s\", es, as)\n\t}\n\n\treturn true\n}\n\nfunc Empty(t TestingT, obj interface{}, msgs ...string) bool {\n\tt.Helper()\n\tswitch val := obj.(type) {\n\tcase string:\n\t\tif val != \"\" {\n\t\t\treturn fail(t, msgs, \"uassert.Empty: not empty string: %s\", val)\n\t\t}\n\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:\n\t\tif val != 0 {\n\t\t\treturn fail(t, msgs, \"uassert.Empty: not empty number: %d\", val)\n\t\t}\n\tcase std.Address:\n\t\tvar zeroAddr std.Address\n\t\tif val != zeroAddr {\n\t\t\treturn fail(t, msgs, \"uassert.Empty: not empty std.Address: %s\", string(val))\n\t\t}\n\tdefault:\n\t\treturn fail(t, msgs, \"uassert.Empty: unsupported type\")\n\t}\n\treturn true\n}\n" + }, + { + "name": "uassert_test.gno", + "body": "package uassert\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"std\"\n\t\"testing\"\n)\n\nvar _ TestingT = (*testing.T)(nil)\n\nfunc TestMock(t *testing.T) {\n\tmockT := new(mockTestingT)\n\tmockT.empty(t)\n\tNoError(mockT, errors.New(\"foo\"))\n\tmockT.equals(t, \"error: unexpected error: foo\")\n\tNoError(mockT, errors.New(\"foo\"), \"custom message\")\n\tmockT.equals(t, \"error: unexpected error: foo - custom message\")\n\tNoError(mockT, errors.New(\"foo\"), \"custom\", \"message\")\n\tmockT.equals(t, \"error: unexpected error: foo - custom message\")\n}\n\nfunc TestNoError(t *testing.T) {\n\tmockT := new(mockTestingT)\n\tTrue(t, NoError(mockT, nil))\n\tmockT.empty(t)\n\tFalse(t, NoError(mockT, errors.New(\"foo bar\")))\n\tmockT.equals(t, \"error: unexpected error: foo bar\")\n}\n\nfunc TestError(t *testing.T) {\n\tmockT := new(mockTestingT)\n\tTrue(t, Error(mockT, errors.New(\"foo bar\")))\n\tmockT.empty(t)\n\tFalse(t, Error(mockT, nil))\n\tmockT.equals(t, \"error: an error is expected but got nil\")\n}\n\nfunc TestErrorContains(t *testing.T) {\n\tmockT := new(mockTestingT)\n\n\t// nil error\n\tvar err error\n\tFalse(t, ErrorContains(mockT, err, \"\"), \"ErrorContains should return false for nil arg\")\n}\n\nfunc TestTrue(t *testing.T) {\n\tmockT := new(mockTestingT)\n\tif !True(mockT, true) {\n\t\tt.Error(\"True should return true\")\n\t}\n\tmockT.empty(t)\n\tif True(mockT, false) {\n\t\tt.Error(\"True should return false\")\n\t}\n\tmockT.equals(t, \"error: should be true\")\n}\n\nfunc TestFalse(t *testing.T) {\n\tmockT := new(mockTestingT)\n\tif !False(mockT, false) {\n\t\tt.Error(\"False should return true\")\n\t}\n\tmockT.empty(t)\n\tif False(mockT, true) {\n\t\tt.Error(\"False should return false\")\n\t}\n\tmockT.equals(t, \"error: should be false\")\n}\n\nfunc TestPanicsWithMessage(t *testing.T) {\n\tmockT := new(mockTestingT)\n\tif !PanicsWithMessage(mockT, \"panic\", func() {\n\t\tpanic(errors.New(\"panic\"))\n\t}) {\n\t\tt.Error(\"PanicsWithMessage should return true\")\n\t}\n\tmockT.empty(t)\n\n\tif PanicsWithMessage(mockT, \"Panic!\", func() {\n\t\t// noop\n\t}) {\n\t\tt.Error(\"PanicsWithMessage should return false\")\n\t}\n\tmockT.equals(t, \"error: func should panic\\n\\tPanic value:\\tnil\")\n\n\tif PanicsWithMessage(mockT, \"at the disco\", func() {\n\t\tpanic(errors.New(\"panic\"))\n\t}) {\n\t\tt.Error(\"PanicsWithMessage should return false\")\n\t}\n\tmockT.equals(t, \"error: func should panic with message:\\tat the disco\\n\\tPanic value:\\tpanic\")\n\n\tif PanicsWithMessage(mockT, \"Panic!\", func() {\n\t\tpanic(\"panic\")\n\t}) {\n\t\tt.Error(\"PanicsWithMessage should return false\")\n\t}\n\tmockT.equals(t, \"error: func should panic with message:\\tPanic!\\n\\tPanic value:\\tpanic\")\n}\n\nfunc TestNotPanics(t *testing.T) {\n\tmockT := new(mockTestingT)\n\n\tif !NotPanics(mockT, func() {\n\t\t// noop\n\t}) {\n\t\tt.Error(\"NotPanics should return true\")\n\t}\n\tmockT.empty(t)\n\n\tif NotPanics(mockT, func() {\n\t\tpanic(\"Panic!\")\n\t}) {\n\t\tt.Error(\"NotPanics should return false\")\n\t}\n}\n\nfunc TestEqual(t *testing.T) {\n\tmockT := new(mockTestingT)\n\n\tcases := []struct {\n\t\texpected interface{}\n\t\tactual interface{}\n\t\tresult bool\n\t\tremark string\n\t}{\n\t\t// expected to be equal\n\t\t{\"Hello World\", \"Hello World\", true, \"\"},\n\t\t{123, 123, true, \"\"},\n\t\t{123.5, 123.5, true, \"\"},\n\t\t{nil, nil, true, \"\"},\n\t\t{int32(123), int32(123), true, \"\"},\n\t\t{uint64(123), uint64(123), true, \"\"},\n\t\t{std.Address(\"g12345\"), std.Address(\"g12345\"), true, \"\"},\n\t\t// XXX: continue\n\n\t\t// not expected to be equal\n\t\t{\"Hello World\", 42, false, \"\"},\n\t\t{41, 42, false, \"\"},\n\t\t{10, uint(10), false, \"\"},\n\t\t// XXX: continue\n\n\t\t// expected to raise errors\n\t\t// XXX: todo\n\t}\n\n\tfor _, c := range cases {\n\t\tname := fmt.Sprintf(\"Equal(%v, %v)\", c.expected, c.actual)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tres := Equal(mockT, c.expected, c.actual)\n\n\t\t\tif res != c.result {\n\t\t\t\tt.Errorf(\"%s should return %v: %s - %s\", name, c.result, c.remark, mockT.actualString())\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype myStruct struct {\n\tS string\n\tI int\n}\n\nfunc TestEmpty(t *testing.T) {\n\tmockT := new(mockTestingT)\n\n\tcases := []struct {\n\t\tobj interface{}\n\t\texpectedEmpty bool\n\t}{\n\t\t// expected to be empty\n\t\t{\"\", true},\n\t\t{0, true},\n\t\t{int(0), true},\n\t\t{int64(0), true},\n\t\t{uint(0), true},\n\t\t// XXX: continue\n\n\t\t// not expected to be empty\n\t\t{\"Hello World\", false},\n\t\t{1, false},\n\t\t{int32(1), false},\n\t\t{uint64(1), false},\n\t\t{std.Address(\"g12345\"), false},\n\n\t\t// unsupported\n\t\t{nil, false},\n\t\t{myStruct{}, false},\n\t\t{\u0026myStruct{}, false},\n\t}\n\n\tfor _, c := range cases {\n\t\tname := fmt.Sprintf(\"Empty(%v)\", c.obj)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tres := Empty(mockT, c.obj)\n\n\t\t\tif res != c.expectedEmpty {\n\t\t\t\tt.Errorf(\"%s should return %v: %s\", name, c.expectedEmpty, mockT.actualString())\n\t\t\t}\n\t\t})\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "urequire", + "path": "gno.land/p/demo/urequire", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/urequire\n\nrequire gno.land/p/demo/uassert v0.0.0-latest\n" + }, + { + "name": "urequire.gno", + "body": "// urequire is a sister package for uassert.\n// XXX: codegen the package.\npackage urequire\n\nimport \"gno.land/p/demo/uassert\"\n\n// type TestingT = uassert.TestingT // XXX: bug, should work\n\nfunc NoError(t uassert.TestingT, err error, msgs ...string) {\n\tt.Helper()\n\tif uassert.NoError(t, err, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc Error(t uassert.TestingT, err error, msgs ...string) {\n\tt.Helper()\n\tif uassert.Error(t, err, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc ErrorContains(t uassert.TestingT, err error, contains string, msgs ...string) {\n\tt.Helper()\n\tif uassert.ErrorContains(t, err, contains, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc True(t uassert.TestingT, value bool, msgs ...string) {\n\tt.Helper()\n\tif uassert.True(t, value, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc False(t uassert.TestingT, value bool, msgs ...string) {\n\tt.Helper()\n\tif uassert.False(t, value, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc ErrorIs(t uassert.TestingT, err, target error, msgs ...string) {\n\tt.Helper()\n\tif uassert.ErrorIs(t, err, target, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc PanicsWithMessage(t uassert.TestingT, msg string, f func(), msgs ...string) {\n\tt.Helper()\n\tif uassert.PanicsWithMessage(t, msg, f, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc NotPanics(t uassert.TestingT, f func(), msgs ...string) {\n\tt.Helper()\n\tif uassert.NotPanics(t, f, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc Equal(t uassert.TestingT, expected, actual interface{}, msgs ...string) {\n\tt.Helper()\n\tif uassert.Equal(t, expected, actual, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc Empty(t uassert.TestingT, obj interface{}, msgs ...string) {\n\tt.Helper()\n\tif uassert.Empty(t, obj, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n" + }, + { + "name": "urequire_test.gno", + "body": "package urequire\n\nimport \"testing\"\n\nfunc TestPackage(t *testing.T) {\n\tEqual(t, 42, 42)\n\t// XXX: find a way to unit test this package\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "grc20", + "path": "gno.land/p/demo/grc/grc20", + "files": [ + { + "name": "banker.gno", + "body": "package grc20\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// Banker implements a token banker with admin privileges.\n//\n// The Banker is intended to be used in two main ways:\n// 1. as a temporary object used to make the initial minting, then deleted.\n// 2. preserved in an unexported variable to support conditional administrative\n// tasks protected by the contract.\ntype Banker struct {\n\tname string\n\tsymbol string\n\tdecimals uint\n\ttotalSupply uint64\n\tbalances avl.Tree // std.Address(owner) -\u003e uint64\n\tallowances avl.Tree // string(owner+\":\"+spender) -\u003e uint64\n\ttoken *token // to share the same pointer\n}\n\nfunc NewBanker(name, symbol string, decimals uint) *Banker {\n\tif name == \"\" {\n\t\tpanic(\"name should not be empty\")\n\t}\n\tif symbol == \"\" {\n\t\tpanic(\"symbol should not be empty\")\n\t}\n\t// XXX additional checks (length, characters, limits, etc)\n\n\tb := Banker{\n\t\tname: name,\n\t\tsymbol: symbol,\n\t\tdecimals: decimals,\n\t}\n\tt := \u0026token{banker: \u0026b}\n\tb.token = t\n\treturn \u0026b\n}\n\nfunc (b Banker) Token() Token { return b.token } // Token returns a grc20 safe-object implementation.\nfunc (b Banker) GetName() string { return b.name }\nfunc (b Banker) GetSymbol() string { return b.symbol }\nfunc (b Banker) GetDecimals() uint { return b.decimals }\nfunc (b Banker) TotalSupply() uint64 { return b.totalSupply }\nfunc (b Banker) KnownAccounts() int { return b.balances.Size() }\n\nfunc (b *Banker) Mint(address std.Address, amount uint64) error {\n\tif !address.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\n\t// TODO: check for overflow\n\n\tb.totalSupply += amount\n\tcurrentBalance := b.BalanceOf(address)\n\tnewBalance := currentBalance + amount\n\n\tb.balances.Set(string(address), newBalance)\n\n\tstd.Emit(\n\t\tTransferEvent,\n\t\t\"from\", \"\",\n\t\t\"to\", string(address),\n\t\t\"value\", strconv.Itoa(int(amount)),\n\t)\n\n\treturn nil\n}\n\nfunc (b *Banker) Burn(address std.Address, amount uint64) error {\n\tif !address.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\t// TODO: check for overflow\n\n\tcurrentBalance := b.BalanceOf(address)\n\tif currentBalance \u003c amount {\n\t\treturn ErrInsufficientBalance\n\t}\n\n\tb.totalSupply -= amount\n\tnewBalance := currentBalance - amount\n\n\tb.balances.Set(string(address), newBalance)\n\n\tstd.Emit(\n\t\tTransferEvent,\n\t\t\"from\", string(address),\n\t\t\"to\", \"\",\n\t\t\"value\", strconv.Itoa(int(amount)),\n\t)\n\n\treturn nil\n}\n\nfunc (b Banker) BalanceOf(address std.Address) uint64 {\n\tbalance, found := b.balances.Get(address.String())\n\tif !found {\n\t\treturn 0\n\t}\n\treturn balance.(uint64)\n}\n\nfunc (b *Banker) SpendAllowance(owner, spender std.Address, amount uint64) error {\n\tif !owner.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\tif !spender.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tcurrentAllowance := b.Allowance(owner, spender)\n\tif currentAllowance \u003c amount {\n\t\treturn ErrInsufficientAllowance\n\t}\n\n\tkey := allowanceKey(owner, spender)\n\tnewAllowance := currentAllowance - amount\n\n\tif newAllowance == 0 {\n\t\tb.allowances.Remove(key)\n\t} else {\n\t\tb.allowances.Set(key, newAllowance)\n\t}\n\n\treturn nil\n}\n\nfunc (b *Banker) Transfer(from, to std.Address, amount uint64) error {\n\tif !from.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\tif !to.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\tif from == to {\n\t\treturn ErrCannotTransferToSelf\n\t}\n\n\ttoBalance := b.BalanceOf(to)\n\tfromBalance := b.BalanceOf(from)\n\n\t// debug.\n\t// println(\"from\", from, \"to\", to, \"amount\", amount, \"fromBalance\", fromBalance, \"toBalance\", toBalance)\n\n\tif fromBalance \u003c amount {\n\t\treturn ErrInsufficientBalance\n\t}\n\n\tnewToBalance := toBalance + amount\n\tnewFromBalance := fromBalance - amount\n\n\tb.balances.Set(string(to), newToBalance)\n\tb.balances.Set(string(from), newFromBalance)\n\n\tstd.Emit(\n\t\tTransferEvent,\n\t\t\"from\", from.String(),\n\t\t\"to\", to.String(),\n\t\t\"value\", strconv.Itoa(int(amount)),\n\t)\n\treturn nil\n}\n\nfunc (b *Banker) TransferFrom(spender, from, to std.Address, amount uint64) error {\n\tif err := b.SpendAllowance(from, spender, amount); err != nil {\n\t\treturn err\n\t}\n\treturn b.Transfer(from, to, amount)\n}\n\nfunc (b *Banker) Allowance(owner, spender std.Address) uint64 {\n\tallowance, found := b.allowances.Get(allowanceKey(owner, spender))\n\tif !found {\n\t\treturn 0\n\t}\n\treturn allowance.(uint64)\n}\n\nfunc (b *Banker) Approve(owner, spender std.Address, amount uint64) error {\n\tif !owner.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\tif !spender.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tb.allowances.Set(allowanceKey(owner, spender), amount)\n\n\tstd.Emit(\n\t\tApprovalEvent,\n\t\t\"owner\", string(owner),\n\t\t\"spender\", string(spender),\n\t\t\"value\", strconv.Itoa(int(amount)),\n\t)\n\n\treturn nil\n}\n\nfunc (b *Banker) RenderHome() string {\n\tstr := \"\"\n\tstr += ufmt.Sprintf(\"# %s ($%s)\\n\\n\", b.name, b.symbol)\n\tstr += ufmt.Sprintf(\"* **Decimals**: %d\\n\", b.decimals)\n\tstr += ufmt.Sprintf(\"* **Total supply**: %d\\n\", b.totalSupply)\n\tstr += ufmt.Sprintf(\"* **Known accounts**: %d\\n\", b.KnownAccounts())\n\treturn str\n}\n\nfunc allowanceKey(owner, spender std.Address) string {\n\treturn owner.String() + \":\" + spender.String()\n}\n" + }, + { + "name": "banker_test.gno", + "body": "package grc20\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/demo/urequire\"\n)\n\nfunc TestBankerImpl(t *testing.T) {\n\tdummy := NewBanker(\"Dummy\", \"DUMMY\", 4)\n\turequire.False(t, dummy == nil, \"dummy should not be nil\")\n}\n\nfunc TestAllowance(t *testing.T) {\n\tvar (\n\t\towner = testutils.TestAddress(\"owner\")\n\t\tspender = testutils.TestAddress(\"spender\")\n\t\tdest = testutils.TestAddress(\"dest\")\n\t)\n\n\tb := NewBanker(\"Dummy\", \"DUMMY\", 6)\n\turequire.NoError(t, b.Mint(owner, 100000000))\n\turequire.NoError(t, b.Approve(owner, spender, 5000000))\n\turequire.Error(t, b.TransferFrom(spender, owner, dest, 10000000), ErrInsufficientAllowance.Error(), \"should not be able to transfer more than approved\")\n\n\ttests := []struct {\n\t\tspend uint64\n\t\texp uint64\n\t}{\n\t\t{3, 4999997},\n\t\t{999997, 4000000},\n\t\t{4000000, 0},\n\t}\n\n\tfor _, tt := range tests {\n\t\tb0 := b.BalanceOf(dest)\n\t\turequire.NoError(t, b.TransferFrom(spender, owner, dest, tt.spend))\n\t\ta := b.Allowance(owner, spender)\n\t\turequire.Equal(t, a, tt.exp, ufmt.Sprintf(\"allowance exp: %d, got %d\", tt.exp, a))\n\t\tb := b.BalanceOf(dest)\n\t\texpB := b0 + tt.spend\n\t\turequire.Equal(t, b, expB, ufmt.Sprintf(\"balance exp: %d, got %d\", expB, b))\n\t}\n\n\turequire.Error(t, b.TransferFrom(spender, owner, dest, 1), \"no allowance\")\n\tkey := allowanceKey(owner, spender)\n\turequire.False(t, b.allowances.Has(key), \"allowance should be removed\")\n\turequire.Equal(t, b.Allowance(owner, spender), uint64(0), \"allowance should be 0\")\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/grc/grc20\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/grc/exts v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n)\n" + }, + { + "name": "token.gno", + "body": "package grc20\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/grc/exts\"\n)\n\n// token implements the Token interface.\n//\n// It is generated with Banker.Token().\n// It can safely be explosed publicly.\ntype token struct {\n\tbanker *Banker\n}\n\n// var _ Token = (*token)(nil)\nfunc (t *token) GetName() string { return t.banker.name }\nfunc (t *token) GetSymbol() string { return t.banker.symbol }\nfunc (t *token) GetDecimals() uint { return t.banker.decimals }\nfunc (t *token) TotalSupply() uint64 { return t.banker.totalSupply }\n\nfunc (t *token) BalanceOf(owner std.Address) uint64 {\n\treturn t.banker.BalanceOf(owner)\n}\n\nfunc (t *token) Transfer(to std.Address, amount uint64) error {\n\tcaller := std.PrevRealm().Addr()\n\treturn t.banker.Transfer(caller, to, amount)\n}\n\nfunc (t *token) Allowance(owner, spender std.Address) uint64 {\n\treturn t.banker.Allowance(owner, spender)\n}\n\nfunc (t *token) Approve(spender std.Address, amount uint64) error {\n\tcaller := std.PrevRealm().Addr()\n\treturn t.banker.Approve(caller, spender, amount)\n}\n\nfunc (t *token) TransferFrom(from, to std.Address, amount uint64) error {\n\tspender := std.PrevRealm().Addr()\n\tif err := t.banker.SpendAllowance(from, spender, amount); err != nil {\n\t\treturn err\n\t}\n\treturn t.banker.Transfer(from, to, amount)\n}\n\ntype Token2 interface {\n\texts.TokenMetadata\n\n\t// Returns the amount of tokens in existence.\n\tTotalSupply() uint64\n\n\t// Returns the amount of tokens owned by `account`.\n\tBalanceOf(account std.Address) uint64\n\n\t// Moves `amount` tokens from the caller's account to `to`.\n\t//\n\t// Returns an error if the operation failed.\n\tTransfer(to std.Address, amount uint64) error\n\n\t// Returns the remaining number of tokens that `spender` will be\n\t// allowed to spend on behalf of `owner` through {transferFrom}. This is\n\t// zero by default.\n\t//\n\t// This value changes when {approve} or {transferFrom} are called.\n\tAllowance(owner, spender std.Address) uint64\n\n\t// Sets `amount` as the allowance of `spender` over the caller's tokens.\n\t//\n\t// Returns an error if the operation failed.\n\t//\n\t// IMPORTANT: Beware that changing an allowance with this method brings the risk\n\t// that someone may use both the old and the new allowance by unfortunate\n\t// transaction ordering. One possible solution to mitigate this race\n\t// condition is to first reduce the spender's allowance to 0 and set the\n\t// desired value afterwards:\n\t// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n\tApprove(spender std.Address, amount uint64) error\n\n\t// Moves `amount` tokens from `from` to `to` using the\n\t// allowance mechanism. `amount` is then deducted from the caller's\n\t// allowance.\n\t//\n\t// Returns an error if the operation failed.\n\tTransferFrom(from, to std.Address, amount uint64) error\n}\n" + }, + { + "name": "token_test.gno", + "body": "package grc20\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/demo/urequire\"\n)\n\nfunc TestUserTokenImpl(t *testing.T) {\n\tbank := NewBanker(\"Dummy\", \"DUMMY\", 4)\n\ttok := bank.Token()\n\t_ = tok\n}\n\nfunc TestUserApprove(t *testing.T) {\n\towner := testutils.TestAddress(\"owner\")\n\tspender := testutils.TestAddress(\"spender\")\n\tdest := testutils.TestAddress(\"dest\")\n\n\tbank := NewBanker(\"Dummy\", \"DUMMY\", 6)\n\ttok := bank.Token()\n\n\t// Set owner as the original caller\n\tstd.TestSetOrigCaller(owner)\n\t// Mint 100000000 tokens for owner\n\turequire.NoError(t, bank.Mint(owner, 100000000))\n\n\t// Approve spender to spend 5000000 tokens\n\turequire.NoError(t, tok.Approve(spender, 5000000))\n\n\t// Set spender as the original caller\n\tstd.TestSetOrigCaller(spender)\n\t// Try to transfer 10000000 tokens from owner to dest, should fail because it exceeds allowance\n\turequire.Error(t,\n\t\ttok.TransferFrom(owner, dest, 10000000),\n\t\tErrInsufficientAllowance.Error(),\n\t\t\"should not be able to transfer more than approved\",\n\t)\n\n\t// Define a set of test data with spend amount and expected remaining allowance\n\ttests := []struct {\n\t\tspend uint64 // Spend amount\n\t\texp uint64 // Remaining allowance\n\t}{\n\t\t{3, 4999997},\n\t\t{999997, 4000000},\n\t\t{4000000, 0},\n\t}\n\n\t// perform transfer operation,and check if allowance and balance are correct\n\tfor _, tt := range tests {\n\t\tb0 := tok.BalanceOf(dest)\n\t\t// Perform transfer from owner to dest\n\t\turequire.NoError(t, tok.TransferFrom(owner, dest, tt.spend))\n\t\ta := tok.Allowance(owner, spender)\n\t\t// Check if allowance equals expected value\n\t\turequire.True(t, a == tt.exp, ufmt.Sprintf(\"allowance exp: %d,got %d\", tt.exp, a))\n\n\t\t// Get dest current balance\n\t\tb := tok.BalanceOf(dest)\n\t\t// Calculate expected balance ,should be initial balance plus transfer amount\n\t\texpB := b0 + tt.spend\n\t\t// Check if balance equals expected value\n\t\turequire.True(t, b == expB, ufmt.Sprintf(\"balance exp: %d,got %d\", expB, b))\n\t}\n\n\t// Try to transfer one token from owner to dest ,should fail because no allowance left\n\turequire.Error(t, tok.TransferFrom(owner, dest, 1), ErrInsufficientAllowance.Error(), \"no allowance\")\n}\n" + }, + { + "name": "types.gno", + "body": "package grc20\n\nimport (\n\t\"errors\"\n\t\"std\"\n\n\t\"gno.land/p/demo/grc/exts\"\n)\n\nvar (\n\tErrInsufficientBalance = errors.New(\"insufficient balance\")\n\tErrInsufficientAllowance = errors.New(\"insufficient allowance\")\n\tErrInvalidAddress = errors.New(\"invalid address\")\n\tErrCannotTransferToSelf = errors.New(\"cannot send transfer to self\")\n)\n\ntype Token interface {\n\texts.TokenMetadata\n\n\t// Returns the amount of tokens in existence.\n\tTotalSupply() uint64\n\n\t// Returns the amount of tokens owned by `account`.\n\tBalanceOf(account std.Address) uint64\n\n\t// Moves `amount` tokens from the caller's account to `to`.\n\t//\n\t// Returns an error if the operation failed.\n\tTransfer(to std.Address, amount uint64) error\n\n\t// Returns the remaining number of tokens that `spender` will be\n\t// allowed to spend on behalf of `owner` through {transferFrom}. This is\n\t// zero by default.\n\t//\n\t// This value changes when {approve} or {transferFrom} are called.\n\tAllowance(owner, spender std.Address) uint64\n\n\t// Sets `amount` as the allowance of `spender` over the caller's tokens.\n\t//\n\t// Returns an error if the operation failed.\n\t//\n\t// IMPORTANT: Beware that changing an allowance with this method brings the risk\n\t// that someone may use both the old and the new allowance by unfortunate\n\t// transaction ordering. One possible solution to mitigate this race\n\t// condition is to first reduce the spender's allowance to 0 and set the\n\t// desired value afterwards:\n\t// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n\tApprove(spender std.Address, amount uint64) error\n\n\t// Moves `amount` tokens from `from` to `to` using the\n\t// allowance mechanism. `amount` is then deducted from the caller's\n\t// allowance.\n\t//\n\t// Returns an error if the operation failed.\n\tTransferFrom(from, to std.Address, amount uint64) error\n}\n\nconst (\n\tTransferEvent = \"Transfer\"\n\tApprovalEvent = \"Approval\"\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "grc721", + "path": "gno.land/p/demo/grc/grc721", + "files": [ + { + "name": "basic_nft.gno", + "body": "package grc721\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype basicNFT struct {\n\tname string\n\tsymbol string\n\towners avl.Tree // tokenId -\u003e OwnerAddress\n\tbalances avl.Tree // OwnerAddress -\u003e TokenCount\n\ttokenApprovals avl.Tree // TokenId -\u003e ApprovedAddress\n\ttokenURIs avl.Tree // TokenId -\u003e URIs\n\toperatorApprovals avl.Tree // \"OwnerAddress:OperatorAddress\" -\u003e bool\n}\n\n// Returns new basic NFT\nfunc NewBasicNFT(name string, symbol string) *basicNFT {\n\treturn \u0026basicNFT{\n\t\tname: name,\n\t\tsymbol: symbol,\n\n\t\towners: avl.Tree{},\n\t\tbalances: avl.Tree{},\n\t\ttokenApprovals: avl.Tree{},\n\t\ttokenURIs: avl.Tree{},\n\t\toperatorApprovals: avl.Tree{},\n\t}\n}\n\nfunc (s *basicNFT) Name() string { return s.name }\nfunc (s *basicNFT) Symbol() string { return s.symbol }\nfunc (s *basicNFT) TokenCount() uint64 { return uint64(s.owners.Size()) }\n\n// BalanceOf returns balance of input address\nfunc (s *basicNFT) BalanceOf(addr std.Address) (uint64, error) {\n\tif err := isValidAddress(addr); err != nil {\n\t\treturn 0, err\n\t}\n\n\tbalance, found := s.balances.Get(addr.String())\n\tif !found {\n\t\treturn 0, nil\n\t}\n\n\treturn balance.(uint64), nil\n}\n\n// OwnerOf returns owner of input token id\nfunc (s *basicNFT) OwnerOf(tid TokenID) (std.Address, error) {\n\towner, found := s.owners.Get(string(tid))\n\tif !found {\n\t\treturn \"\", ErrInvalidTokenId\n\t}\n\n\treturn owner.(std.Address), nil\n}\n\n// TokenURI returns the URI of input token id\nfunc (s *basicNFT) TokenURI(tid TokenID) (string, error) {\n\turi, found := s.tokenURIs.Get(string(tid))\n\tif !found {\n\t\treturn \"\", ErrInvalidTokenId\n\t}\n\n\treturn uri.(string), nil\n}\n\nfunc (s *basicNFT) SetTokenURI(tid TokenID, tURI TokenURI) (bool, error) {\n\t// check for invalid TokenID\n\tif !s.exists(tid) {\n\t\treturn false, ErrInvalidTokenId\n\t}\n\n\t// check for the right owner\n\towner, err := s.OwnerOf(tid)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tcaller := std.PrevRealm().Addr()\n\tif caller != owner {\n\t\treturn false, ErrCallerIsNotOwner\n\t}\n\ts.tokenURIs.Set(string(tid), string(tURI))\n\treturn true, nil\n}\n\n// IsApprovedForAll returns true if operator is approved for all by the owner.\n// Otherwise, returns false\nfunc (s *basicNFT) IsApprovedForAll(owner, operator std.Address) bool {\n\tkey := owner.String() + \":\" + operator.String()\n\t_, found := s.operatorApprovals.Get(key)\n\tif !found {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// Approve approves the input address for particular token\nfunc (s *basicNFT) Approve(to std.Address, tid TokenID) error {\n\tif err := isValidAddress(to); err != nil {\n\t\treturn err\n\t}\n\n\towner, err := s.OwnerOf(tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif owner == to {\n\t\treturn ErrApprovalToCurrentOwner\n\t}\n\n\tcaller := std.PrevRealm().Addr()\n\tif caller != owner \u0026\u0026 !s.IsApprovedForAll(owner, caller) {\n\t\treturn ErrCallerIsNotOwnerOrApproved\n\t}\n\n\ts.tokenApprovals.Set(string(tid), to.String())\n\tevent := ApprovalEvent{owner, to, tid}\n\temit(\u0026event)\n\n\treturn nil\n}\n\n// GetApproved return the approved address for token\nfunc (s *basicNFT) GetApproved(tid TokenID) (std.Address, error) {\n\taddr, found := s.tokenApprovals.Get(string(tid))\n\tif !found {\n\t\treturn zeroAddress, ErrTokenIdNotHasApproved\n\t}\n\n\treturn std.Address(addr.(string)), nil\n}\n\n// SetApprovalForAll can approve the operator to operate on all tokens\nfunc (s *basicNFT) SetApprovalForAll(operator std.Address, approved bool) error {\n\tif err := isValidAddress(operator); err != nil {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tcaller := std.PrevRealm().Addr()\n\treturn s.setApprovalForAll(caller, operator, approved)\n}\n\n// Safely transfers `tokenId` token from `from` to `to`, checking that\n// contract recipients are aware of the GRC721 protocol to prevent\n// tokens from being forever locked.\nfunc (s *basicNFT) SafeTransferFrom(from, to std.Address, tid TokenID) error {\n\tcaller := std.PrevRealm().Addr()\n\tif !s.isApprovedOrOwner(caller, tid) {\n\t\treturn ErrCallerIsNotOwnerOrApproved\n\t}\n\n\terr := s.transfer(from, to, tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !s.checkOnGRC721Received(from, to, tid) {\n\t\treturn ErrTransferToNonGRC721Receiver\n\t}\n\n\treturn nil\n}\n\n// Transfers `tokenId` token from `from` to `to`.\nfunc (s *basicNFT) TransferFrom(from, to std.Address, tid TokenID) error {\n\tcaller := std.PrevRealm().Addr()\n\tif !s.isApprovedOrOwner(caller, tid) {\n\t\treturn ErrCallerIsNotOwnerOrApproved\n\t}\n\n\terr := s.transfer(from, to, tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Mints `tokenId` and transfers it to `to`.\nfunc (s *basicNFT) Mint(to std.Address, tid TokenID) error {\n\treturn s.mint(to, tid)\n}\n\n// Mints `tokenId` and transfers it to `to`. Also checks that\n// contract recipients are using GRC721 protocol\nfunc (s *basicNFT) SafeMint(to std.Address, tid TokenID) error {\n\terr := s.mint(to, tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !s.checkOnGRC721Received(zeroAddress, to, tid) {\n\t\treturn ErrTransferToNonGRC721Receiver\n\t}\n\n\treturn nil\n}\n\nfunc (s *basicNFT) Burn(tid TokenID) error {\n\towner, err := s.OwnerOf(tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ts.beforeTokenTransfer(owner, zeroAddress, tid, 1)\n\n\ts.tokenApprovals.Remove(string(tid))\n\tbalance, err := s.BalanceOf(owner)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbalance -= 1\n\ts.balances.Set(owner.String(), balance)\n\ts.owners.Remove(string(tid))\n\n\tevent := TransferEvent{owner, zeroAddress, tid}\n\temit(\u0026event)\n\n\ts.afterTokenTransfer(owner, zeroAddress, tid, 1)\n\n\treturn nil\n}\n\n/* Helper methods */\n\n// Helper for SetApprovalForAll()\nfunc (s *basicNFT) setApprovalForAll(owner, operator std.Address, approved bool) error {\n\tif owner == operator {\n\t\treturn ErrApprovalToCurrentOwner\n\t}\n\n\tkey := owner.String() + \":\" + operator.String()\n\ts.operatorApprovals.Set(key, approved)\n\n\tevent := ApprovalForAllEvent{owner, operator, approved}\n\temit(\u0026event)\n\n\treturn nil\n}\n\n// Helper for TransferFrom() and SafeTransferFrom()\nfunc (s *basicNFT) transfer(from, to std.Address, tid TokenID) error {\n\tif err := isValidAddress(from); err != nil {\n\t\treturn ErrInvalidAddress\n\t}\n\tif err := isValidAddress(to); err != nil {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tif from == to {\n\t\treturn ErrCannotTransferToSelf\n\t}\n\n\towner, err := s.OwnerOf(tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif owner != from {\n\t\treturn ErrTransferFromIncorrectOwner\n\t}\n\n\ts.beforeTokenTransfer(from, to, tid, 1)\n\n\t// Check that tokenId was not transferred by `beforeTokenTransfer`\n\towner, err = s.OwnerOf(tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif owner != from {\n\t\treturn ErrTransferFromIncorrectOwner\n\t}\n\n\ts.tokenApprovals.Remove(string(tid))\n\tfromBalance, err := s.BalanceOf(from)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttoBalance, err := s.BalanceOf(to)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfromBalance -= 1\n\ttoBalance += 1\n\ts.balances.Set(from.String(), fromBalance)\n\ts.balances.Set(to.String(), toBalance)\n\ts.owners.Set(string(tid), to)\n\n\tevent := TransferEvent{from, to, tid}\n\temit(\u0026event)\n\n\ts.afterTokenTransfer(from, to, tid, 1)\n\n\treturn nil\n}\n\n// Helper for Mint() and SafeMint()\nfunc (s *basicNFT) mint(to std.Address, tid TokenID) error {\n\tif err := isValidAddress(to); err != nil {\n\t\treturn err\n\t}\n\n\tif s.exists(tid) {\n\t\treturn ErrTokenIdAlreadyExists\n\t}\n\n\ts.beforeTokenTransfer(zeroAddress, to, tid, 1)\n\n\t// Check that tokenId was not minted by `beforeTokenTransfer`\n\tif s.exists(tid) {\n\t\treturn ErrTokenIdAlreadyExists\n\t}\n\n\ttoBalance, err := s.BalanceOf(to)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttoBalance += 1\n\ts.balances.Set(to.String(), toBalance)\n\ts.owners.Set(string(tid), to)\n\n\tevent := TransferEvent{zeroAddress, to, tid}\n\temit(\u0026event)\n\n\ts.afterTokenTransfer(zeroAddress, to, tid, 1)\n\n\treturn nil\n}\n\nfunc (s *basicNFT) isApprovedOrOwner(addr std.Address, tid TokenID) bool {\n\towner, found := s.owners.Get(string(tid))\n\tif !found {\n\t\treturn false\n\t}\n\n\tif addr == owner.(std.Address) || s.IsApprovedForAll(owner.(std.Address), addr) {\n\t\treturn true\n\t}\n\n\t_, err := s.GetApproved(tid)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// Checks if token id already exists\nfunc (s *basicNFT) exists(tid TokenID) bool {\n\t_, found := s.owners.Get(string(tid))\n\treturn found\n}\n\nfunc (s *basicNFT) beforeTokenTransfer(from, to std.Address, firstTokenId TokenID, batchSize uint64) {\n\t// TODO: Implementation\n}\n\nfunc (s *basicNFT) afterTokenTransfer(from, to std.Address, firstTokenId TokenID, batchSize uint64) {\n\t// TODO: Implementation\n}\n\nfunc (s *basicNFT) checkOnGRC721Received(from, to std.Address, tid TokenID) bool {\n\t// TODO: Implementation\n\treturn true\n}\n\nfunc (s *basicNFT) RenderHome() (str string) {\n\tstr += ufmt.Sprintf(\"# %s ($%s)\\n\\n\", s.name, s.symbol)\n\tstr += ufmt.Sprintf(\"* **Total supply**: %d\\n\", s.TokenCount())\n\tstr += ufmt.Sprintf(\"* **Known accounts**: %d\\n\", s.balances.Size())\n\n\treturn\n}\n" + }, + { + "name": "basic_nft_test.gno", + "body": "package grc721\n\nimport (\n\t\"std\"\n\t\"testing\"\n)\n\nvar (\n\tdummyNFTName = \"DummyNFT\"\n\tdummyNFTSymbol = \"DNFT\"\n)\n\nfunc TestNewBasicNFT(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n}\n\nfunc TestName(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\tname := dummy.Name()\n\tif name != dummyNFTName {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", dummyNFTName, name)\n\t}\n}\n\nfunc TestSymbol(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\tsymbol := dummy.Symbol()\n\tif symbol != dummyNFTSymbol {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", dummyNFTSymbol, symbol)\n\t}\n}\n\nfunc TestTokenCount(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tcount := dummy.TokenCount()\n\tif count != 0 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 0, count)\n\t}\n\n\tdummy.mint(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\", TokenID(\"1\"))\n\tdummy.mint(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\", TokenID(\"2\"))\n\n\tcount = dummy.TokenCount()\n\tif count != 2 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 2, count)\n\t}\n}\n\nfunc TestBalanceOf(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\tbalanceAddr1, err := dummy.BalanceOf(addr1)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif balanceAddr1 != 0 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 0, balanceAddr1)\n\t}\n\n\tdummy.mint(addr1, TokenID(\"1\"))\n\tdummy.mint(addr1, TokenID(\"2\"))\n\tdummy.mint(addr2, TokenID(\"3\"))\n\n\tbalanceAddr1, err = dummy.BalanceOf(addr1)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tbalanceAddr2, err := dummy.BalanceOf(addr2)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tif balanceAddr1 != 2 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 2, balanceAddr1)\n\t}\n\tif balanceAddr2 != 1 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 1, balanceAddr2)\n\t}\n}\n\nfunc TestOwnerOf(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\towner, err := dummy.OwnerOf(TokenID(\"invalid\"))\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\n\tdummy.mint(addr1, TokenID(\"1\"))\n\tdummy.mint(addr2, TokenID(\"2\"))\n\n\t// Checking for token id \"1\"\n\towner, err = dummy.OwnerOf(TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif owner != addr1 {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", addr1.String(), owner.String())\n\t}\n\n\t// Checking for token id \"2\"\n\towner, err = dummy.OwnerOf(TokenID(\"2\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif owner != addr2 {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", addr2.String(), owner.String())\n\t}\n}\n\nfunc TestIsApprovedForAll(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\tisApprovedForAll := dummy.IsApprovedForAll(addr1, addr2)\n\tif isApprovedForAll != false {\n\t\tt.Errorf(\"expected: (%v), got: (%v)\", false, isApprovedForAll)\n\t}\n}\n\nfunc TestSetApprovalForAll(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tcaller := std.PrevRealm().Addr()\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\tisApprovedForAll := dummy.IsApprovedForAll(caller, addr)\n\tif isApprovedForAll != false {\n\t\tt.Errorf(\"expected: (%v), got: (%v)\", false, isApprovedForAll)\n\t}\n\n\terr := dummy.SetApprovalForAll(addr, true)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tisApprovedForAll = dummy.IsApprovedForAll(caller, addr)\n\tif isApprovedForAll != true {\n\t\tt.Errorf(\"expected: (%v), got: (%v)\", false, isApprovedForAll)\n\t}\n}\n\nfunc TestGetApproved(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tapprovedAddr, err := dummy.GetApproved(TokenID(\"invalid\"))\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n}\n\nfunc TestApprove(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tcaller := std.PrevRealm().Addr()\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\tdummy.mint(caller, TokenID(\"1\"))\n\n\t_, err := dummy.GetApproved(TokenID(\"1\"))\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\n\terr = dummy.Approve(addr, TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\tapprovedAddr, err := dummy.GetApproved(TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif approvedAddr != addr {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", addr.String(), approvedAddr.String())\n\t}\n}\n\nfunc TestTransferFrom(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tcaller := std.PrevRealm().Addr()\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\tdummy.mint(caller, TokenID(\"1\"))\n\tdummy.mint(caller, TokenID(\"2\"))\n\n\terr := dummy.TransferFrom(caller, addr, TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\t// Check balance of caller after transfer\n\tbalanceOfCaller, err := dummy.BalanceOf(caller)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif balanceOfCaller != 1 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 1, balanceOfCaller)\n\t}\n\n\t// Check balance of addr after transfer\n\tbalanceOfAddr, err := dummy.BalanceOf(addr)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif balanceOfAddr != 1 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 1, balanceOfAddr)\n\t}\n\n\t// Check Owner of transferred Token id\n\towner, err := dummy.OwnerOf(TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif owner != addr {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", addr.String(), owner.String())\n\t}\n}\n\nfunc TestSafeTransferFrom(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\tcaller := std.PrevRealm().Addr()\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\tdummy.mint(caller, TokenID(\"1\"))\n\tdummy.mint(caller, TokenID(\"2\"))\n\n\terr := dummy.SafeTransferFrom(caller, addr, TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\t// Check balance of caller after transfer\n\tbalanceOfCaller, err := dummy.BalanceOf(caller)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif balanceOfCaller != 1 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 1, balanceOfCaller)\n\t}\n\n\t// Check balance of addr after transfer\n\tbalanceOfAddr, err := dummy.BalanceOf(addr)\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif balanceOfAddr != 1 {\n\t\tt.Errorf(\"expected: (%d), got: (%d)\", 1, balanceOfAddr)\n\t}\n\n\t// Check Owner of transferred Token id\n\towner, err := dummy.OwnerOf(TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif owner != addr {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", addr.String(), owner.String())\n\t}\n}\n\nfunc TestMint(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\n\terr := dummy.Mint(addr1, TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\terr = dummy.Mint(addr1, TokenID(\"2\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\terr = dummy.Mint(addr2, TokenID(\"3\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\t// Try minting duplicate token id\n\terr = dummy.Mint(addr2, TokenID(\"1\"))\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n\n\t// Check Owner of Token id\n\towner, err := dummy.OwnerOf(TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\tif owner != addr1 {\n\t\tt.Errorf(\"expected: (%s), got: (%s)\", addr1.String(), owner.String())\n\t}\n}\n\nfunc TestBurn(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\tdummy.mint(addr, TokenID(\"1\"))\n\tdummy.mint(addr, TokenID(\"2\"))\n\n\terr := dummy.Burn(TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"should not result in error\")\n\t}\n\n\t// Check Owner of Token id\n\towner, err := dummy.OwnerOf(TokenID(\"1\"))\n\tif err == nil {\n\t\tt.Errorf(\"should result in error\")\n\t}\n}\n\nfunc TestSetTokenURI(t *testing.T) {\n\tdummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := std.Address(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\taddr2 := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\ttokenURI := \"http://example.com/token\"\n\n\tstd.TestSetOrigCaller(std.Address(addr1)) // addr1\n\n\tdummy.mint(addr1, TokenID(\"1\"))\n\t_, derr := dummy.SetTokenURI(TokenID(\"1\"), TokenURI(tokenURI))\n\n\tif derr != nil {\n\t\tt.Errorf(\"Should not result in error \", derr.Error())\n\t}\n\n\t// Test case: Invalid token ID\n\t_, err := dummy.SetTokenURI(TokenID(\"3\"), TokenURI(tokenURI))\n\tif err != ErrInvalidTokenId {\n\t\tt.Errorf(\"Expected error %v, got %v\", ErrInvalidTokenId, err)\n\t}\n\n\tstd.TestSetOrigCaller(std.Address(addr2)) // addr2\n\n\t_, cerr := dummy.SetTokenURI(TokenID(\"1\"), TokenURI(tokenURI)) // addr2 trying to set URI for token 1\n\tif cerr != ErrCallerIsNotOwner {\n\t\tt.Errorf(\"Expected error %v, got %v\", ErrCallerIsNotOwner, err)\n\t}\n\n\t// Test case: Retrieving TokenURI\n\tstd.TestSetOrigCaller(std.Address(addr1)) // addr1\n\n\tdummyTokenURI, err := dummy.TokenURI(TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"TokenURI error: %v, \", err.Error())\n\t}\n\tif dummyTokenURI != tokenURI {\n\t\tt.Errorf(\"Expected URI %v, got %v\", tokenURI, dummyTokenURI)\n\t}\n}\n" + }, + { + "name": "errors.gno", + "body": "package grc721\n\nimport \"errors\"\n\nvar (\n\tErrInvalidTokenId = errors.New(\"invalid token id\")\n\tErrInvalidAddress = errors.New(\"invalid address\")\n\tErrTokenIdNotHasApproved = errors.New(\"token id not approved for anyone\")\n\tErrApprovalToCurrentOwner = errors.New(\"approval to current owner\")\n\tErrCallerIsNotOwner = errors.New(\"caller is not token owner\")\n\tErrCallerNotApprovedForAll = errors.New(\"caller is not approved for all\")\n\tErrCannotTransferToSelf = errors.New(\"cannot send transfer to self\")\n\tErrTransferFromIncorrectOwner = errors.New(\"transfer from incorrect owner\")\n\tErrTransferToNonGRC721Receiver = errors.New(\"transfer to non GRC721Receiver implementer\")\n\tErrCallerIsNotOwnerOrApproved = errors.New(\"caller is not token owner or approved\")\n\tErrTokenIdAlreadyExists = errors.New(\"token id already exists\")\n\n\t// ERC721Royalty\n\tErrInvalidRoyaltyPercentage = errors.New(\"invalid royalty percentage\")\n\tErrInvalidRoyaltyPaymentAddress = errors.New(\"invalid royalty paymentAddress\")\n\tErrCannotCalculateRoyaltyAmount = errors.New(\"cannot calculate royalty amount\")\n)\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/grc/grc721\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "grc721_metadata.gno", + "body": "package grc721\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\n// metadataNFT represents an NFT with metadata extensions.\ntype metadataNFT struct {\n\t*basicNFT // Embedded basicNFT struct for basic NFT functionality\n\textensions *avl.Tree // AVL tree for storing metadata extensions\n}\n\n// Ensure that metadataNFT implements the IGRC721MetadataOnchain interface.\nvar _ IGRC721MetadataOnchain = (*metadataNFT)(nil)\n\n// NewNFTWithMetadata creates a new basic NFT with metadata extensions.\nfunc NewNFTWithMetadata(name string, symbol string) *metadataNFT {\n\t// Create a new basic NFT\n\tnft := NewBasicNFT(name, symbol)\n\n\t// Return a metadataNFT with basicNFT embedded and an empty AVL tree for extensions\n\treturn \u0026metadataNFT{\n\t\tbasicNFT: nft,\n\t\textensions: avl.NewTree(),\n\t}\n}\n\n// SetTokenMetadata sets metadata for a given token ID.\nfunc (s *metadataNFT) SetTokenMetadata(tid TokenID, metadata Metadata) error {\n\t// Check if the caller is the owner of the token\n\towner, err := s.basicNFT.OwnerOf(tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcaller := std.PrevRealm().Addr()\n\tif caller != owner {\n\t\treturn ErrCallerIsNotOwner\n\t}\n\n\t// Set the metadata for the token ID in the extensions AVL tree\n\ts.extensions.Set(string(tid), metadata)\n\treturn nil\n}\n\n// TokenMetadata retrieves metadata for a given token ID.\nfunc (s *metadataNFT) TokenMetadata(tid TokenID) (Metadata, error) {\n\t// Retrieve metadata from the extensions AVL tree\n\tmetadata, found := s.extensions.Get(string(tid))\n\tif !found {\n\t\treturn Metadata{}, ErrInvalidTokenId\n\t}\n\n\treturn metadata.(Metadata), nil\n}\n\n// mint mints a new token and assigns it to the specified address.\nfunc (s *metadataNFT) mint(to std.Address, tid TokenID) error {\n\t// Check if the address is valid\n\tif err := isValidAddress(to); err != nil {\n\t\treturn err\n\t}\n\n\t// Check if the token ID already exists\n\tif s.basicNFT.exists(tid) {\n\t\treturn ErrTokenIdAlreadyExists\n\t}\n\n\ts.basicNFT.beforeTokenTransfer(zeroAddress, to, tid, 1)\n\n\t// Check if the token ID was minted by beforeTokenTransfer\n\tif s.basicNFT.exists(tid) {\n\t\treturn ErrTokenIdAlreadyExists\n\t}\n\n\t// Increment balance of the recipient address\n\ttoBalance, err := s.basicNFT.BalanceOf(to)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttoBalance += 1\n\ts.basicNFT.balances.Set(to.String(), toBalance)\n\n\t// Set owner of the token ID to the recipient address\n\ts.basicNFT.owners.Set(string(tid), to)\n\n\t// Emit transfer event\n\tevent := TransferEvent{zeroAddress, to, tid}\n\temit(\u0026event)\n\n\ts.basicNFT.afterTokenTransfer(zeroAddress, to, tid, 1)\n\n\treturn nil\n}\n" + }, + { + "name": "grc721_metadata_test.gno", + "body": "package grc721\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n)\n\nfunc TestSetMetadata(t *testing.T) {\n\t// Create a new dummy NFT with metadata\n\tdummy := NewNFTWithMetadata(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\t// Define addresses for testing purposes\n\taddr1 := testutils.TestAddress(\"alice\")\n\taddr2 := testutils.TestAddress(\"bob\")\n\n\t// Define metadata attributes\n\tname := \"test\"\n\tdescription := \"test\"\n\timage := \"test\"\n\timageData := \"test\"\n\texternalURL := \"test\"\n\tattributes := []Trait{}\n\tbackgroundColor := \"test\"\n\tanimationURL := \"test\"\n\tyoutubeURL := \"test\"\n\n\t// Set the original caller to addr1\n\tstd.TestSetOrigCaller(addr1) // addr1\n\n\t// Mint a new token for addr1\n\tdummy.mint(addr1, TokenID(\"1\"))\n\n\t// Set metadata for token 1\n\tderr := dummy.SetTokenMetadata(TokenID(\"1\"), Metadata{\n\t\tName: name,\n\t\tDescription: description,\n\t\tImage: image,\n\t\tImageData: imageData,\n\t\tExternalURL: externalURL,\n\t\tAttributes: attributes,\n\t\tBackgroundColor: backgroundColor,\n\t\tAnimationURL: animationURL,\n\t\tYoutubeURL: youtubeURL,\n\t})\n\n\t// Check if there was an error setting metadata\n\tif derr != nil {\n\t\tt.Errorf(\"Should not result in error : %s\", derr.Error())\n\t}\n\n\t// Test case: Invalid token ID\n\terr := dummy.SetTokenMetadata(TokenID(\"3\"), Metadata{\n\t\tName: name,\n\t\tDescription: description,\n\t\tImage: image,\n\t\tImageData: imageData,\n\t\tExternalURL: externalURL,\n\t\tAttributes: attributes,\n\t\tBackgroundColor: backgroundColor,\n\t\tAnimationURL: animationURL,\n\t\tYoutubeURL: youtubeURL,\n\t})\n\n\t// Check if the error returned matches the expected error\n\tif err != ErrInvalidTokenId {\n\t\tt.Errorf(\"Expected error %s, got %s\", ErrInvalidTokenId, err)\n\t}\n\n\t// Set the original caller to addr2\n\tstd.TestSetOrigCaller(addr2) // addr2\n\n\t// Try to set metadata for token 1 from addr2 (should fail)\n\tcerr := dummy.SetTokenMetadata(TokenID(\"1\"), Metadata{\n\t\tName: name,\n\t\tDescription: description,\n\t\tImage: image,\n\t\tImageData: imageData,\n\t\tExternalURL: externalURL,\n\t\tAttributes: attributes,\n\t\tBackgroundColor: backgroundColor,\n\t\tAnimationURL: animationURL,\n\t\tYoutubeURL: youtubeURL,\n\t})\n\n\t// Check if the error returned matches the expected error\n\tif cerr != ErrCallerIsNotOwner {\n\t\tt.Errorf(\"Expected error %s, got %s\", ErrCallerIsNotOwner, cerr)\n\t}\n\n\t// Set the original caller back to addr1\n\tstd.TestSetOrigCaller(addr1) // addr1\n\n\t// Retrieve metadata for token 1\n\tdummyMetadata, err := dummy.TokenMetadata(TokenID(\"1\"))\n\tif err != nil {\n\t\tt.Errorf(\"Metadata error: %s\", err.Error())\n\t}\n\n\t// Check if metadata attributes match expected values\n\tif dummyMetadata.Image != image {\n\t\tt.Errorf(\"Expected Metadata's image %s, got %s\", image, dummyMetadata.Image)\n\t}\n\tif dummyMetadata.ImageData != imageData {\n\t\tt.Errorf(\"Expected Metadata's imageData %s, got %s\", imageData, dummyMetadata.ImageData)\n\t}\n\tif dummyMetadata.ExternalURL != externalURL {\n\t\tt.Errorf(\"Expected Metadata's externalURL %s, got %s\", externalURL, dummyMetadata.ExternalURL)\n\t}\n\tif dummyMetadata.Description != description {\n\t\tt.Errorf(\"Expected Metadata's description %s, got %s\", description, dummyMetadata.Description)\n\t}\n\tif dummyMetadata.Name != name {\n\t\tt.Errorf(\"Expected Metadata's name %s, got %s\", name, dummyMetadata.Name)\n\t}\n\tif len(dummyMetadata.Attributes) != len(attributes) {\n\t\tt.Errorf(\"Expected %d Metadata's attributes, got %d\", len(attributes), len(dummyMetadata.Attributes))\n\t}\n\tif dummyMetadata.BackgroundColor != backgroundColor {\n\t\tt.Errorf(\"Expected Metadata's backgroundColor %s, got %s\", backgroundColor, dummyMetadata.BackgroundColor)\n\t}\n\tif dummyMetadata.AnimationURL != animationURL {\n\t\tt.Errorf(\"Expected Metadata's animationURL %s, got %s\", animationURL, dummyMetadata.AnimationURL)\n\t}\n\tif dummyMetadata.YoutubeURL != youtubeURL {\n\t\tt.Errorf(\"Expected Metadata's youtubeURL %s, got %s\", youtubeURL, dummyMetadata.YoutubeURL)\n\t}\n}\n" + }, + { + "name": "grc721_royalty.gno", + "body": "package grc721\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\n// royaltyNFT represents a non-fungible token (NFT) with royalty functionality.\ntype royaltyNFT struct {\n\t*metadataNFT // Embedding metadataNFT for NFT functionality\n\ttokenRoyaltyInfo *avl.Tree // AVL tree to store royalty information for each token\n\tmaxRoyaltyPercentage uint64 // maxRoyaltyPercentage represents the maximum royalty percentage that can be charged every sale\n}\n\n// Ensure that royaltyNFT implements the IGRC2981 interface.\nvar _ IGRC2981 = (*royaltyNFT)(nil)\n\n// NewNFTWithRoyalty creates a new royalty NFT with the specified name, symbol, and royalty calculator.\nfunc NewNFTWithRoyalty(name string, symbol string) *royaltyNFT {\n\t// Create a new NFT with metadata\n\tnft := NewNFTWithMetadata(name, symbol)\n\n\treturn \u0026royaltyNFT{\n\t\tmetadataNFT: nft,\n\t\ttokenRoyaltyInfo: avl.NewTree(),\n\t\tmaxRoyaltyPercentage: 100,\n\t}\n}\n\n// SetTokenRoyalty sets the royalty information for a specific token ID.\nfunc (r *royaltyNFT) SetTokenRoyalty(tid TokenID, royaltyInfo RoyaltyInfo) error {\n\t// Validate the payment address\n\tif err := isValidAddress(royaltyInfo.PaymentAddress); err != nil {\n\t\treturn ErrInvalidRoyaltyPaymentAddress\n\t}\n\n\t// Check if royalty percentage exceeds maxRoyaltyPercentage\n\tif royaltyInfo.Percentage \u003e r.maxRoyaltyPercentage {\n\t\treturn ErrInvalidRoyaltyPercentage\n\t}\n\n\t// Check if the caller is the owner of the token\n\towner, err := r.metadataNFT.OwnerOf(tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcaller := std.PrevRealm().Addr()\n\tif caller != owner {\n\t\treturn ErrCallerIsNotOwner\n\t}\n\n\t// Set royalty information for the token\n\tr.tokenRoyaltyInfo.Set(string(tid), royaltyInfo)\n\n\treturn nil\n}\n\n// RoyaltyInfo returns the royalty information for the given token ID and sale price.\nfunc (r *royaltyNFT) RoyaltyInfo(tid TokenID, salePrice uint64) (std.Address, uint64, error) {\n\t// Retrieve royalty information for the token\n\tval, found := r.tokenRoyaltyInfo.Get(string(tid))\n\tif !found {\n\t\treturn \"\", 0, ErrInvalidTokenId\n\t}\n\n\troyaltyInfo := val.(RoyaltyInfo)\n\n\t// Calculate royalty amount\n\troyaltyAmount, _ := r.calculateRoyaltyAmount(salePrice, royaltyInfo.Percentage)\n\n\treturn royaltyInfo.PaymentAddress, royaltyAmount, nil\n}\n\nfunc (r *royaltyNFT) calculateRoyaltyAmount(salePrice, percentage uint64) (uint64, error) {\n\troyaltyAmount := (salePrice * percentage) / 100\n\treturn royaltyAmount, nil\n}\n" + }, + { + "name": "grc721_royalty_test.gno", + "body": "package grc721\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n)\n\nfunc TestSetTokenRoyalty(t *testing.T) {\n\tdummy := NewNFTWithRoyalty(dummyNFTName, dummyNFTSymbol)\n\tif dummy == nil {\n\t\tt.Errorf(\"should not be nil\")\n\t}\n\n\taddr1 := testutils.TestAddress(\"alice\")\n\taddr2 := testutils.TestAddress(\"bob\")\n\n\tpaymentAddress := testutils.TestAddress(\"john\")\n\tpercentage := uint64(10) // 10%\n\n\tsalePrice := uint64(1000)\n\texpectRoyaltyAmount := uint64(100)\n\n\tstd.TestSetOrigCaller(addr1) // addr1\n\n\tdummy.mint(addr1, TokenID(\"1\"))\n\n\tderr := dummy.SetTokenRoyalty(TokenID(\"1\"), RoyaltyInfo{\n\t\tPaymentAddress: paymentAddress,\n\t\tPercentage: percentage,\n\t})\n\n\tif derr != nil {\n\t\tt.Errorf(\"Should not result in error : %s\", derr.Error())\n\t}\n\n\t// Test case: Invalid token ID\n\terr := dummy.SetTokenRoyalty(TokenID(\"3\"), RoyaltyInfo{\n\t\tPaymentAddress: paymentAddress,\n\t\tPercentage: percentage,\n\t})\n\tif err != ErrInvalidTokenId {\n\t\tt.Errorf(\"Expected error %s, got %s\", ErrInvalidTokenId, err)\n\t}\n\n\tstd.TestSetOrigCaller(addr2) // addr2\n\n\tcerr := dummy.SetTokenRoyalty(TokenID(\"1\"), RoyaltyInfo{\n\t\tPaymentAddress: paymentAddress,\n\t\tPercentage: percentage,\n\t})\n\tif cerr != ErrCallerIsNotOwner {\n\t\tt.Errorf(\"Expected error %s, got %s\", ErrCallerIsNotOwner, cerr)\n\t}\n\n\t// Test case: Invalid payment address\n\taerr := dummy.SetTokenRoyalty(TokenID(\"4\"), RoyaltyInfo{\n\t\tPaymentAddress: std.Address(\"###\"), // invalid address\n\t\tPercentage: percentage,\n\t})\n\tif aerr != ErrInvalidRoyaltyPaymentAddress {\n\t\tt.Errorf(\"Expected error %s, got %s\", ErrInvalidRoyaltyPaymentAddress, aerr)\n\t}\n\n\t// Test case: Invalid percentage\n\tperr := dummy.SetTokenRoyalty(TokenID(\"5\"), RoyaltyInfo{\n\t\tPaymentAddress: paymentAddress,\n\t\tPercentage: uint64(200), // over maxRoyaltyPercentage\n\t})\n\n\tif perr != ErrInvalidRoyaltyPercentage {\n\t\tt.Errorf(\"Expected error %s, got %s\", ErrInvalidRoyaltyPercentage, perr)\n\t}\n\n\t// Test case: Retrieving Royalty Info\n\tstd.TestSetOrigCaller(addr1) // addr1\n\n\tdummyPaymentAddress, dummyRoyaltyAmount, rerr := dummy.RoyaltyInfo(TokenID(\"1\"), salePrice)\n\tif rerr != nil {\n\t\tt.Errorf(\"RoyaltyInfo error: %s\", rerr.Error())\n\t}\n\n\tif dummyPaymentAddress != paymentAddress {\n\t\tt.Errorf(\"Expected RoyaltyPaymentAddress %s, got %s\", paymentAddress, dummyPaymentAddress)\n\t}\n\n\tif dummyRoyaltyAmount != expectRoyaltyAmount {\n\t\tt.Errorf(\"Expected RoyaltyAmount %d, got %d\", expectRoyaltyAmount, dummyRoyaltyAmount)\n\t}\n}\n" + }, + { + "name": "igrc721.gno", + "body": "package grc721\n\nimport \"std\"\n\ntype IGRC721 interface {\n\tBalanceOf(owner std.Address) (uint64, error)\n\tOwnerOf(tid TokenID) (std.Address, error)\n\tSetTokenURI(tid TokenID, tURI TokenURI) (bool, error)\n\tSafeTransferFrom(from, to std.Address, tid TokenID) error\n\tTransferFrom(from, to std.Address, tid TokenID) error\n\tApprove(approved std.Address, tid TokenID) error\n\tSetApprovalForAll(operator std.Address, approved bool) error\n\tGetApproved(tid TokenID) (std.Address, error)\n\tIsApprovedForAll(owner, operator std.Address) bool\n}\n\ntype (\n\tTokenID string\n\tTokenURI string\n)\n\ntype TransferEvent struct {\n\tFrom std.Address\n\tTo std.Address\n\tTokenID TokenID\n}\n\ntype ApprovalEvent struct {\n\tOwner std.Address\n\tApproved std.Address\n\tTokenID TokenID\n}\n\ntype ApprovalForAllEvent struct {\n\tOwner std.Address\n\tOperator std.Address\n\tApproved bool\n}\n" + }, + { + "name": "igrc721_metadata.gno", + "body": "package grc721\n\n// IGRC721CollectionMetadata describes basic information about an NFT collection.\ntype IGRC721CollectionMetadata interface {\n\tName() string // Name returns the name of the collection.\n\tSymbol() string // Symbol returns the symbol of the collection.\n}\n\n// IGRC721Metadata follows the Ethereum standard\ntype IGRC721Metadata interface {\n\tIGRC721CollectionMetadata\n\tTokenURI(tid TokenID) (string, error) // TokenURI returns the URI of a specific token.\n}\n\n// IGRC721Metadata follows the OpenSea metadata standard\ntype IGRC721MetadataOnchain interface {\n\tIGRC721CollectionMetadata\n\tTokenMetadata(tid TokenID) (Metadata, error)\n}\n\ntype Trait struct {\n\tDisplayType string\n\tTraitType string\n\tValue string\n}\n\n// see: https://docs.opensea.io/docs/metadata-standards\ntype Metadata struct {\n\tImage string // URL to the image of the item. Can be any type of image (including SVGs, which will be cached into PNGs by OpenSea), IPFS or Arweave URLs or paths. We recommend using a minimum 3000 x 3000 image.\n\tImageData string // Raw SVG image data, if you want to generate images on the fly (not recommended). Only use this if you're not including the image parameter.\n\tExternalURL string // URL that will appear below the asset's image on OpenSea and will allow users to leave OpenSea and view the item on your site.\n\tDescription string // Human-readable description of the item. Markdown is supported.\n\tName string // Name of the item.\n\tAttributes []Trait // Attributes for the item, which will show up on the OpenSea page for the item.\n\tBackgroundColor string // Background color of the item on OpenSea. Must be a six-character hexadecimal without a pre-pended #\n\tAnimationURL string // URL to a multimedia attachment for the item. Supported file extensions: GLTF, GLB, WEBM, MP4, M4V, OGV, OGG, MP3, WAV, OGA, HTML (for rich experiences and interactive NFTs using JavaScript canvas, WebGL, etc.). Scripts and relative paths within the HTML page are now supported. Access to browser extensions is not supported.\n\tYoutubeURL string // URL to a YouTube video (only used if animation_url is not provided).\n}\n" + }, + { + "name": "igrc721_royalty.gno", + "body": "package grc721\n\nimport \"std\"\n\n// IGRC2981 follows the Ethereum standard\ntype IGRC2981 interface {\n\t// RoyaltyInfo retrieves royalty information for a tokenID and salePrice.\n\t// It returns the payment address, royalty amount, and an error if any.\n\tRoyaltyInfo(tokenID TokenID, salePrice uint64) (std.Address, uint64, error)\n}\n\n// RoyaltyInfo represents royalty information for a token.\ntype RoyaltyInfo struct {\n\tPaymentAddress std.Address // PaymentAddress is the address where royalty payment should be sent.\n\tPercentage uint64 // Percentage is the royalty percentage. It indicates the percentage of royalty to be paid for each sale. For example : Percentage = 10 =\u003e 10%\n}\n" + }, + { + "name": "util.gno", + "body": "package grc721\n\nimport (\n\t\"std\"\n)\n\nvar zeroAddress = std.Address(\"\")\n\nfunc isValidAddress(addr std.Address) error {\n\tif !addr.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\treturn nil\n}\n\nfunc emit(event interface{}) {\n\t// TODO: setup a pubsub system here?\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "grc777", + "path": "gno.land/p/demo/grc/grc777", + "files": [ + { + "name": "dummy_test.gno", + "body": "package grc777\n\nimport (\n\t\"std\"\n\t\"testing\"\n)\n\ntype dummyImpl struct{}\n\n// FIXME: this should fail.\nvar _ IGRC777 = (*dummyImpl)(nil)\n\nfunc TestInterface(t *testing.T) {\n\tvar dummy IGRC777 = \u0026dummyImpl{}\n}\n\nfunc (impl *dummyImpl) GetName() string { panic(\"not implemented\") }\nfunc (impl *dummyImpl) GetSymbol() string { panic(\"not implemented\") }\nfunc (impl *dummyImpl) GetDecimals() uint { panic(\"not implemented\") }\nfunc (impl *dummyImpl) Granularity() (granularity uint64) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) TotalSupply() (supply uint64) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) BalanceOf(address std.Address) uint64 { panic(\"not implemented\") }\nfunc (impl *dummyImpl) Burn(amount uint64, data []byte) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) AuthorizeOperator(operator std.Address) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) RevokeOperator(operators std.Address) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) DefaultOperators() []std.Address { panic(\"not implemented\") }\nfunc (impl *dummyImpl) Send(recipient std.Address, amount uint64, data []byte) {\n\tpanic(\"not implemented\")\n}\n\nfunc (impl *dummyImpl) IsOperatorFor(operator, tokenHolder std.Address) bool {\n\tpanic(\"not implemented\")\n}\n\nfunc (impl *dummyImpl) OperatorSend(sender, recipient std.Address, amount uint64, data, operatorData []byte) {\n\tpanic(\"not implemented\")\n}\n\nfunc (impl *dummyImpl) OperatorBurn(account std.Address, amount uint64, data, operatorData []byte) {\n\tpanic(\"not implemented\")\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/grc/grc777\n\nrequire gno.land/p/demo/grc/exts v0.0.0-latest\n" + }, + { + "name": "igrc777.gno", + "body": "package grc777\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/grc/exts\"\n)\n\n// TODO: use big.Int or a custom uint64 instead of uint64\n\ntype IGRC777 interface {\n\texts.TokenMetadata\n\n\t// Returns the smallest part of the token that is not divisible. This\n\t// means all token operations (creation, movement and destruction) must\n\t// have amounts that are a multiple of this number.\n\t//\n\t// For most token contracts, this value will equal 1.\n\tGranularity() (granularity uint64)\n\n\t// Returns the amount of tokens in existence.\n\tTotalSupply() (supply uint64)\n\n\t// Returns the amount of tokens owned by an account (`owner`).\n\tBalanceOf(address std.Address) uint64\n\n\t// Moves `amount` tokens from the caller's account to `recipient`.\n\t//\n\t// If send or receive hooks are registered for the caller and `recipient`,\n\t// the corresponding functions will be called with `data` and empty\n\t// `operatorData`. See {IERC777Sender} and {IERC777Recipient}.\n\t//\n\t// Emits a {Sent} event.\n\t//\n\t// Requirements\n\t//\n\t// - the caller must have at least `amount` tokens.\n\t// - `recipient` cannot be the zero address.\n\t// - if `recipient` is a contract, it must implement the {IERC777Recipient}\n\t// interface.\n\tSend(recipient std.Address, amount uint64, data []byte)\n\n\t// Destroys `amount` tokens from the caller's account, reducing the\n\t// total supply.\n\t//\n\t// If a send hook is registered for the caller, the corresponding function\n\t// will be called with `data` and empty `operatorData`. See {IERC777Sender}.\n\t//\n\t// Emits a {Burned} event.\n\t//\n\t// Requirements\n\t//\n\t// - the caller must have at least `amount` tokens.\n\tBurn(amount uint64, data []byte)\n\n\t// Returns true if an account is an operator of `tokenHolder`.\n\t// Operators can send and burn tokens on behalf of their owners. All\n\t// accounts are their own operator.\n\t//\n\t// See {operatorSend} and {operatorBurn}.\n\tIsOperatorFor(operator, tokenHolder std.Address) bool\n\n\t// Make an account an operator of the caller.\n\t//\n\t// See {isOperatorFor}.\n\t//\n\t// Emits an {AuthorizedOperator} event.\n\t//\n\t// Requirements\n\t//\n\t// - `operator` cannot be calling address.\n\tAuthorizeOperator(operator std.Address)\n\n\t// Revoke an account's operator status for the caller.\n\t//\n\t// See {isOperatorFor} and {defaultOperators}.\n\t//\n\t// Emits a {RevokedOperator} event.\n\t//\n\t// Requirements\n\t//\n\t// - `operator` cannot be calling address.\n\tRevokeOperator(operators std.Address)\n\n\t// Returns the list of default operators. These accounts are operators\n\t// for all token holders, even if {authorizeOperator} was never called on\n\t// them.\n\t//\n\t// This list is immutable, but individual holders may revoke these via\n\t// {revokeOperator}, in which case {isOperatorFor} will return false.\n\tDefaultOperators() []std.Address\n\n\t// Moves `amount` tokens from `sender` to `recipient`. The caller must\n\t// be an operator of `sender`.\n\t//\n\t// If send or receive hooks are registered for `sender` and `recipient`,\n\t// the corresponding functions will be called with `data` and\n\t// `operatorData`. See {IERC777Sender} and {IERC777Recipient}.\n\t//\n\t// Emits a {Sent} event.\n\t//\n\t// Requirements\n\t//\n\t// - `sender` cannot be the zero address.\n\t// - `sender` must have at least `amount` tokens.\n\t// - the caller must be an operator for `sender`.\n\t// - `recipient` cannot be the zero address.\n\t// - if `recipient` is a contract, it must implement the {IERC777Recipient}\n\t// interface.\n\tOperatorSend(sender, recipient std.Address, amount uint64, data, operatorData []byte)\n\n\t// Destroys `amount` tokens from `account`, reducing the total supply.\n\t// The caller must be an operator of `account`.\n\t//\n\t// If a send hook is registered for `account`, the corresponding function\n\t// will be called with `data` and `operatorData`. See {IERC777Sender}.\n\t//\n\t// Emits a {Burned} event.\n\t//\n\t// Requirements\n\t//\n\t// - `account` cannot be the zero address.\n\t// - `account` must have at least `amount` tokens.\n\t// - the caller must be an operator for `account`.\n\tOperatorBurn(account std.Address, amount uint64, data, operatorData []byte)\n}\n\n// Emitted when `amount` tokens are created by `operator` and assigned to `to`.\n//\n// Note that some additional user `data` and `operatorData` can be logged in the event.\ntype MintedEvent struct {\n\tOperator std.Address\n\tTo std.Address\n\tAmount uint64\n\tData []byte\n\tOperatorData []byte\n}\n\n// Emitted when `operator` destroys `amount` tokens from `account`.\n//\n// Note that some additional user `data` and `operatorData` can be logged in the event.\ntype BurnedEvent struct {\n\tOperator std.Address\n\tFrom std.Address\n\tAmount uint64\n\tData []byte\n\tOperatorData []byte\n}\n\n// Emitted when `operator` is made operator for `tokenHolder`\ntype AuthorizedOperatorEvent struct {\n\tOperator std.Address\n\tTokenHolder std.Address\n}\n\n// Emitted when `operator` is revoked its operator status for `tokenHolder`.\ntype RevokedOperatorEvent struct {\n\tOperator std.Address\n\tTokenHolder std.Address\n}\n\ntype SentEvent struct {\n\tOperator std.Address\n\tFrom std.Address\n\tTo std.Address\n\tAmount uint64\n\tData []byte\n\tOperatorData []byte\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "rat", + "path": "gno.land/p/demo/rat", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/rat\n" + }, + { + "name": "maths.gno", + "body": "package rat\n\nconst (\n\tintSize = 32 \u003c\u003c (^uint(0) \u003e\u003e 63) // 32 or 64\n\n\tMaxInt = 1\u003c\u003c(intSize-1) - 1\n\tMinInt = -1 \u003c\u003c (intSize - 1)\n\tMaxInt8 = 1\u003c\u003c7 - 1\n\tMinInt8 = -1 \u003c\u003c 7\n\tMaxInt16 = 1\u003c\u003c15 - 1\n\tMinInt16 = -1 \u003c\u003c 15\n\tMaxInt32 = 1\u003c\u003c31 - 1\n\tMinInt32 = -1 \u003c\u003c 31\n\tMaxInt64 = 1\u003c\u003c63 - 1\n\tMinInt64 = -1 \u003c\u003c 63\n\tMaxUint = 1\u003c\u003cintSize - 1\n\tMaxUint8 = 1\u003c\u003c8 - 1\n\tMaxUint16 = 1\u003c\u003c16 - 1\n\tMaxUint32 = 1\u003c\u003c32 - 1\n\tMaxUint64 = 1\u003c\u003c64 - 1\n)\n" + }, + { + "name": "rat.gno", + "body": "package rat\n\n//----------------------------------------\n// Rat fractions\n\n// represents a fraction.\ntype Rat struct {\n\tX int32\n\tY int32 // must be positive\n}\n\nfunc NewRat(x, y int32) Rat {\n\tif y \u003c= 0 {\n\t\tpanic(\"invalid std.Rat denominator\")\n\t}\n\treturn Rat{X: x, Y: y}\n}\n\nfunc (r1 Rat) IsValid() bool {\n\tif r1.Y \u003c= 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (r1 Rat) Cmp(r2 Rat) int {\n\tif !r1.IsValid() {\n\t\tpanic(\"invalid std.Rat left operand\")\n\t}\n\tif !r2.IsValid() {\n\t\tpanic(\"invalid std.Rat right operand\")\n\t}\n\tvar p1, p2 int64\n\tp1 = int64(r1.X) * int64(r2.Y)\n\tp2 = int64(r1.Y) * int64(r2.X)\n\tif p1 \u003c p2 {\n\t\treturn -1\n\t} else if p1 == p2 {\n\t\treturn 0\n\t} else {\n\t\treturn 1\n\t}\n}\n\n//func (r1 Rat) Plus(r2 Rat) Rat {\n// XXX\n//}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "users", + "path": "gno.land/p/demo/users", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/users\n" + }, + { + "name": "types.gno", + "body": "package users\n\ntype AddressOrName string\n\nfunc (aon AddressOrName) IsName() bool {\n\treturn aon != \"\" \u0026\u0026 aon[0] == '@'\n}\n\nfunc (aon AddressOrName) GetName() (string, bool) {\n\tif len(aon) \u003e= 2 \u0026\u0026 aon[0] == '@' {\n\t\treturn string(aon[1:]), true\n\t}\n\treturn \"\", false\n}\n" + }, + { + "name": "users.gno", + "body": "package users\n\nimport (\n\t\"std\"\n\t\"strconv\"\n)\n\n//----------------------------------------\n// Types\n\ntype User struct {\n\tAddress std.Address\n\tName string\n\tProfile string\n\tNumber int\n\tInvites int\n\tInviter std.Address\n}\n\nfunc (u *User) Render() string {\n\tstr := \"## user \" + u.Name + \"\\n\" +\n\t\t\"\\n\" +\n\t\t\" * address = \" + string(u.Address) + \"\\n\" +\n\t\t\" * \" + strconv.Itoa(u.Invites) + \" invites\\n\"\n\tif u.Inviter != \"\" {\n\t\tstr = str + \" * invited by \" + string(u.Inviter) + \"\\n\"\n\t}\n\tstr = str + \"\\n\" +\n\t\tu.Profile + \"\\n\"\n\treturn str\n}\n" + }, + { + "name": "users_test.gno", + "body": "package users\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "users", + "path": "gno.land/r/demo/users", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/users\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n)\n" + }, + { + "name": "preregister.gno", + "body": "package users\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/users\"\n)\n\n// pre-restricted names\nvar preRestrictedNames = []string{\n\t\"bitcoin\", \"cosmos\", \"newtendermint\", \"ethereum\",\n}\n\n// pre-registered users\nvar preRegisteredUsers = []struct {\n\tName string\n\tAddress std.Address\n}{\n\t// system name\n\t{\"archives\", \"g1xlnyjrnf03ju82v0f98ruhpgnquk28knmjfe5k\"}, // -\u003e @r_archives\n\t{\"demo\", \"g13ek2zz9qurzynzvssyc4sthwppnruhnp0gdz8n\"}, // -\u003e @r_demo\n\t{\"gno\", \"g19602kd9tfxrfd60sgreadt9zvdyyuudcyxsz8a\"}, // -\u003e @r_gno\n\t{\"gnoland\", \"g1g3lsfxhvaqgdv4ccemwpnms4fv6t3aq3p5z6u7\"}, // -\u003e @r_gnoland\n\t{\"gnolang\", \"g1yjlnm3z2630gg5mryjd79907e0zx658wxs9hnd\"}, // -\u003e @r_gnolang\n\t{\"gov\", \"g1g73v2anukg4ej7axwqpthsatzrxjsh0wk797da\"}, // -\u003e @r_gov\n\t{\"nt\", \"g15ge0ae9077eh40erwrn2eq0xw6wupwqthpv34l\"}, // -\u003e @r_nt\n\t{\"sys\", \"g1r929wt2qplfawe4lvqv9zuwfdcz4vxdun7qh8l\"}, // -\u003e @r_sys\n\t{\"x\", \"g164sdpew3c2t3rvxj3kmfv7c7ujlvcw2punzzuz\"}, // -\u003e @r_x\n\n\t// Onbloc\n\t{\"gnoswap\", \"g1lmvrrrr4er2us84h2732sru76c9zl2nvknha8c\"}, // -\u003e @r_gnoswap\n\t{\"onbloc\", \"g12vx7dn3dqq89mz550zwunvg4qw6epq73d9csay\"}, // -\u003e @r_onbloc\n}\n\nfunc init() {\n\t// add pre-registered users\n\tfor _, res := range preRegisteredUsers {\n\t\t// assert not already registered.\n\t\t_, ok := name2User.Get(res.Name)\n\t\tif ok {\n\t\t\tpanic(\"name already registered\")\n\t\t}\n\n\t\t_, ok = addr2User.Get(res.Address.String())\n\t\tif ok {\n\t\t\tpanic(\"address already registered\")\n\t\t}\n\n\t\tcounter++\n\t\tuser := \u0026users.User{\n\t\t\tAddress: res.Address,\n\t\t\tName: res.Name,\n\t\t\tProfile: \"\",\n\t\t\tNumber: counter,\n\t\t\tInvites: int(0),\n\t\t\tInviter: admin,\n\t\t}\n\t\tname2User.Set(res.Name, user)\n\t\taddr2User.Set(res.Address.String(), user)\n\t}\n\n\t// add pre-restricted names\n\tfor _, name := range preRestrictedNames {\n\t\tif _, ok := name2User.Get(name); ok {\n\t\t\tpanic(\"name already registered\")\n\t\t}\n\n\t\trestricted.Set(name, true)\n\t}\n}\n" + }, + { + "name": "users.gno", + "body": "package users\n\nimport (\n\t\"regexp\"\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/users\"\n)\n\n//----------------------------------------\n// State\n\nvar (\n\tadmin std.Address = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\" // @moul\n\n\trestricted avl.Tree // Name -\u003e true - restricted name\n\tname2User avl.Tree // Name -\u003e *users.User\n\taddr2User avl.Tree // std.Address -\u003e *users.User\n\tinvites avl.Tree // string(inviter+\":\"+invited) -\u003e true\n\tcounter int // user id counter\n\tminFee int64 = 20 * 1_000_000 // minimum gnot must be paid to register.\n\tmaxFeeMult int64 = 10 // maximum multiples of minFee accepted.\n)\n\n//----------------------------------------\n// Top-level functions\n\nfunc Register(inviter std.Address, name string, profile string) {\n\t// assert CallTx call.\n\tstd.AssertOriginCall()\n\t// assert invited or paid.\n\tcaller := std.GetCallerAt(2)\n\tif caller != std.GetOrigCaller() {\n\t\tpanic(\"should not happen\") // because std.AssertOrigCall().\n\t}\n\n\tsentCoins := std.GetOrigSend()\n\tminCoin := std.NewCoin(\"ugnot\", minFee)\n\n\tif inviter == \"\" {\n\t\t// banker := std.GetBanker(std.BankerTypeOrigSend)\n\t\tif len(sentCoins) == 1 \u0026\u0026 sentCoins[0].IsGTE(minCoin) {\n\t\t\tif sentCoins[0].Amount \u003e minFee*maxFeeMult {\n\t\t\t\tpanic(\"payment must not be greater than \" + strconv.Itoa(int(minFee*maxFeeMult)))\n\t\t\t} else {\n\t\t\t\t// ok\n\t\t\t}\n\t\t} else {\n\t\t\tpanic(\"payment must not be less than \" + strconv.Itoa(int(minFee)))\n\t\t}\n\t} else {\n\t\tinvitekey := inviter.String() + \":\" + caller.String()\n\t\t_, ok := invites.Get(invitekey)\n\t\tif !ok {\n\t\t\tpanic(\"invalid invitation\")\n\t\t}\n\t\tinvites.Remove(invitekey)\n\t}\n\n\t// assert not already registered.\n\t_, ok := name2User.Get(name)\n\tif ok {\n\t\tpanic(\"name already registered: \" + name)\n\t}\n\t_, ok = addr2User.Get(caller.String())\n\tif ok {\n\t\tpanic(\"address already registered: \" + caller.String())\n\t}\n\n\tisInviterAdmin := inviter == admin\n\n\t// check for restricted name\n\tif _, isRestricted := restricted.Get(name); isRestricted {\n\t\t// only address invite by the admin can register restricted name\n\t\tif !isInviterAdmin {\n\t\t\tpanic(\"restricted name: \" + name)\n\t\t}\n\n\t\trestricted.Remove(name)\n\t}\n\n\t// assert name is valid.\n\t// admin inviter can bypass name restriction\n\tif !isInviterAdmin \u0026\u0026 !reName.MatchString(name) {\n\t\tpanic(\"invalid name: \" + name + \" (must be at least 6 characters, lowercase alphanumeric with underscore)\")\n\t}\n\n\t// remainder of fees go toward invites.\n\tinvites := int(0)\n\tif len(sentCoins) == 1 {\n\t\tif sentCoins[0].Denom == \"ugnot\" \u0026\u0026 sentCoins[0].Amount \u003e= minFee {\n\t\t\tinvites = int(sentCoins[0].Amount / minFee)\n\t\t\tif inviter == \"\" \u0026\u0026 invites \u003e 0 {\n\t\t\t\tinvites -= 1\n\t\t\t}\n\t\t}\n\t}\n\t// register.\n\tcounter++\n\tuser := \u0026users.User{\n\t\tAddress: caller,\n\t\tName: name,\n\t\tProfile: profile,\n\t\tNumber: counter,\n\t\tInvites: invites,\n\t\tInviter: inviter,\n\t}\n\tname2User.Set(name, user)\n\taddr2User.Set(caller.String(), user)\n}\n\nfunc Invite(invitee string) {\n\t// assert CallTx call.\n\tstd.AssertOriginCall()\n\t// get caller/inviter.\n\tcaller := std.GetCallerAt(2)\n\tif caller != std.GetOrigCaller() {\n\t\tpanic(\"should not happen\") // because std.AssertOrigCall().\n\t}\n\tlines := strings.Split(invitee, \"\\n\")\n\tif caller == admin {\n\t\t// nothing to do, all good\n\t} else {\n\t\t// ensure has invites.\n\t\tuserI, ok := addr2User.Get(caller.String())\n\t\tif !ok {\n\t\t\tpanic(\"user unknown\")\n\t\t}\n\t\tuser := userI.(*users.User)\n\t\tif user.Invites \u003c= 0 {\n\t\t\tpanic(\"user has no invite tokens\")\n\t\t}\n\t\tuser.Invites -= len(lines)\n\t\tif user.Invites \u003c 0 {\n\t\t\tpanic(\"user has insufficient invite tokens\")\n\t\t}\n\t}\n\t// for each line...\n\tfor _, line := range lines {\n\t\tif line == \"\" {\n\t\t\tcontinue // file bodies have a trailing newline.\n\t\t} else if strings.HasPrefix(line, `//`) {\n\t\t\tcontinue // comment\n\t\t}\n\t\t// record invite.\n\t\tinvitekey := string(caller) + \":\" + string(line)\n\t\tinvites.Set(invitekey, true)\n\t}\n}\n\nfunc GrantInvites(invites string) {\n\t// assert CallTx call.\n\tstd.AssertOriginCall()\n\t// assert admin.\n\tcaller := std.GetCallerAt(2)\n\tif caller != std.GetOrigCaller() {\n\t\tpanic(\"should not happen\") // because std.AssertOrigCall().\n\t}\n\tif caller != admin {\n\t\tpanic(\"unauthorized\")\n\t}\n\t// for each line...\n\tlines := strings.Split(invites, \"\\n\")\n\tfor _, line := range lines {\n\t\tif line == \"\" {\n\t\t\tcontinue // file bodies have a trailing newline.\n\t\t} else if strings.HasPrefix(line, `//`) {\n\t\t\tcontinue // comment\n\t\t}\n\t\t// parse name and invites.\n\t\tvar name string\n\t\tvar invites int\n\t\tparts := strings.Split(line, \":\")\n\t\tif len(parts) == 1 { // short for :1.\n\t\t\tname = parts[0]\n\t\t\tinvites = 1\n\t\t} else if len(parts) == 2 {\n\t\t\tname = parts[0]\n\t\t\tinvites_, err := strconv.Atoi(parts[1])\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tinvites = int(invites_)\n\t\t} else {\n\t\t\tpanic(\"should not happen\")\n\t\t}\n\t\t// give invites.\n\t\tuserI, ok := name2User.Get(name)\n\t\tif !ok {\n\t\t\t// maybe address.\n\t\t\tuserI, ok = addr2User.Get(name)\n\t\t\tif !ok {\n\t\t\t\tpanic(\"invalid user \" + name)\n\t\t\t}\n\t\t}\n\t\tuser := userI.(*users.User)\n\t\tuser.Invites += invites\n\t}\n}\n\n// Any leftover fees go toward invitations.\nfunc SetMinFee(newMinFee int64) {\n\t// assert CallTx call.\n\tstd.AssertOriginCall()\n\t// assert admin caller.\n\tcaller := std.GetCallerAt(2)\n\tif caller != admin {\n\t\tpanic(\"unauthorized\")\n\t}\n\t// update global variables.\n\tminFee = newMinFee\n}\n\n// This helps prevent fat finger accidents.\nfunc SetMaxFeeMultiple(newMaxFeeMult int64) {\n\t// assert CallTx call.\n\tstd.AssertOriginCall()\n\t// assert admin caller.\n\tcaller := std.GetCallerAt(2)\n\tif caller != admin {\n\t\tpanic(\"unauthorized\")\n\t}\n\t// update global variables.\n\tmaxFeeMult = newMaxFeeMult\n}\n\n//----------------------------------------\n// Exposed public functions\n\nfunc GetUserByName(name string) *users.User {\n\tuserI, ok := name2User.Get(name)\n\tif !ok {\n\t\treturn nil\n\t}\n\treturn userI.(*users.User)\n}\n\nfunc GetUserByAddress(addr std.Address) *users.User {\n\tuserI, ok := addr2User.Get(addr.String())\n\tif !ok {\n\t\treturn nil\n\t}\n\treturn userI.(*users.User)\n}\n\n// unlike GetUserByName, input must be \"@\" prefixed for names.\nfunc GetUserByAddressOrName(input users.AddressOrName) *users.User {\n\tname, isName := input.GetName()\n\tif isName {\n\t\treturn GetUserByName(name)\n\t}\n\treturn GetUserByAddress(std.Address(input))\n}\n\nfunc Resolve(input users.AddressOrName) std.Address {\n\tname, isName := input.GetName()\n\tif !isName {\n\t\treturn std.Address(input) // TODO check validity\n\t}\n\n\tuser := GetUserByName(name)\n\treturn user.Address\n}\n\n// Add restricted name to the list\nfunc AdminAddRestrictedName(name string) {\n\t// assert CallTx call.\n\tstd.AssertOriginCall()\n\t// get caller\n\tcaller := std.GetOrigCaller()\n\t// assert admin\n\tif caller != admin {\n\t\tpanic(\"unauthorized\")\n\t}\n\n\tif user := GetUserByName(name); user != nil {\n\t\tpanic(\"already registered name\")\n\t}\n\n\t// register restricted name\n\n\trestricted.Set(name, true)\n}\n\n//----------------------------------------\n// Constants\n\n// NOTE: name length must be clearly distinguishable from a bech32 address.\nvar reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{5,16}$`)\n\n//----------------------------------------\n// Render main page\n\nfunc Render(path string) string {\n\tif path == \"\" {\n\t\treturn renderHome()\n\t} else if len(path) \u003e= 38 { // 39? 40?\n\t\tif path[:2] != \"g1\" {\n\t\t\treturn \"invalid address \" + path\n\t\t}\n\t\tuser := GetUserByAddress(std.Address(path))\n\t\tif user == nil {\n\t\t\t// TODO: display basic information about account.\n\t\t\treturn \"unknown address \" + path\n\t\t}\n\t\treturn user.Render()\n\t} else {\n\t\tuser := GetUserByName(path)\n\t\tif user == nil {\n\t\t\treturn \"unknown username \" + path\n\t\t}\n\t\treturn user.Render()\n\t}\n}\n\nfunc renderHome() string {\n\tdoc := \"\"\n\tname2User.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tuser := value.(*users.User)\n\t\tdoc += \" * [\" + user.Name + \"](/r/demo/users:\" + user.Name + \")\\n\"\n\t\treturn false\n\t})\n\treturn doc\n}\n" + }, + { + "name": "z_0_b_filetest.gno", + "body": "package main\n\n// SEND: 19900000ugnot\n\nimport (\n\t\"gno.land/r/demo/users\"\n)\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tprintln(\"done\")\n}\n\n// Error:\n// payment must not be less than 20000000\n" + }, + { + "name": "z_0_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/users\"\n)\n\nfunc main() {\n\tstd.TestSetOrigSend(std.Coins{std.NewCoin(\"dontcare\", 1)}, nil)\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tprintln(\"done\")\n}\n\n// Error:\n// incompatible coin denominations: dontcare, ugnot\n" + }, + { + "name": "z_10_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/users_test\npackage users_test\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc init() {\n\tcaller := std.GetOrigCaller() // main\n\ttest2 := testutils.TestAddress(\"test2\")\n\t// as admin, invite gnouser and test2\n\tstd.TestSetOrigCaller(admin)\n\tusers.Invite(caller.String() + \"\\n\" + test2.String())\n\t// register as caller\n\tstd.TestSetOrigCaller(caller)\n\tusers.Register(admin, \"gnouser\", \"my profile\")\n}\n\nfunc main() {\n\t// register as test2\n\ttest2 := testutils.TestAddress(\"test2\")\n\tstd.TestSetOrigCaller(test2)\n\tusers.Register(admin, \"test222\", \"my profile 2\")\n\tprintln(\"done\")\n}\n\n// Output:\n// done\n" + }, + { + "name": "z_11_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tstd.TestSetOrigCaller(admin)\n\tusers.AdminAddRestrictedName(\"superrestricted\")\n\n\t// test restricted name\n\tstd.TestSetOrigCaller(caller)\n\tusers.Register(\"\", \"superrestricted\", \"my profile\")\n\tprintln(\"done\")\n}\n\n// Error:\n// restricted name: superrestricted\n" + }, + { + "name": "z_11b_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tstd.TestSetOrigCaller(admin)\n\t// add restricted name\n\tusers.AdminAddRestrictedName(\"superrestricted\")\n\t// grant invite to caller\n\tusers.Invite(caller.String())\n\t// set back caller\n\tstd.TestSetOrigCaller(caller)\n\t// register restricted name with admin invite\n\tusers.Register(admin, \"superrestricted\", \"my profile\")\n\tprintln(\"done\")\n}\n\n// Output:\n// done\n" + }, + { + "name": "z_1_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/users\"\n)\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tprintln(\"done\")\n}\n\n// Output:\n// done\n" + }, + { + "name": "z_2_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// as admin, grant invites to gnouser\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"test1\")\n\tusers.Invite(test1.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test1)\n\tusers.Register(caller, \"satoshi\", \"my other profile\")\n\tprintln(\"done\")\n}\n\n// Output:\n// done\n" + }, + { + "name": "z_3_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// as admin, grant invites to gnouser\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"test1\")\n\tusers.Invite(test1.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test1)\n\tstd.TestSetOrigSend(std.Coins{{\"dontcare\", 1}}, nil)\n\tusers.Register(caller, \"satoshi\", \"my other profile\")\n\tprintln(\"done\")\n}\n\n// Output:\n// done\n" + }, + { + "name": "z_4_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// as admin, grant invites to gnouser\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"test1\")\n\ttest2 := testutils.TestAddress(\"test2\")\n\tusers.Invite(test1.String())\n\t// switch to test2 (not test1)\n\tstd.TestSetOrigCaller(test2)\n\tstd.TestSetOrigSend(std.Coins{{\"dontcare\", 1}}, nil)\n\tusers.Register(caller, \"satoshi\", \"my other profile\")\n\tprintln(\"done\")\n}\n\n// Error:\n// invalid invitation\n" + }, + { + "name": "z_5_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// as admin, grant invites to gnouser\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"test1\")\n\tusers.Invite(test1.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test1)\n\tstd.TestSetOrigSend(std.Coins{{\"dontcare\", 1}}, nil)\n\tusers.Register(caller, \"satoshi\", \"my other profile\")\n\tprintln(users.Render(\"\"))\n\tprintln(\"========================================\")\n\tprintln(users.Render(\"gnouser\"))\n\tprintln(\"========================================\")\n\tprintln(users.Render(\"satoshi\"))\n\tprintln(\"========================================\")\n\tprintln(users.Render(\"badname\"))\n}\n\n// Output:\n// * [archives](/r/demo/users:archives)\n// * [demo](/r/demo/users:demo)\n// * [gno](/r/demo/users:gno)\n// * [gnoland](/r/demo/users:gnoland)\n// * [gnolang](/r/demo/users:gnolang)\n// * [gnouser](/r/demo/users:gnouser)\n// * [gov](/r/demo/users:gov)\n// * [nt](/r/demo/users:nt)\n// * [satoshi](/r/demo/users:satoshi)\n// * [sys](/r/demo/users:sys)\n// * [x](/r/demo/users:x)\n//\n// ========================================\n// ## user gnouser\n//\n// * address = g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n// * 9 invites\n//\n// my profile\n//\n// ========================================\n// ## user satoshi\n//\n// * address = g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7\n// * 0 invites\n// * invited by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n//\n// my other profile\n//\n// ========================================\n// unknown username badname\n" + }, + { + "name": "z_6_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller()\n\t// as admin, grant invites to unregistered user.\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\tprintln(\"done\")\n}\n\n// Error:\n// invalid user g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n" + }, + { + "name": "z_7_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// as admin, grant invites to gnouser\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"test1\")\n\tusers.Invite(test1.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test1)\n\tstd.TestSetOrigSend(std.Coins{{\"dontcare\", 1}}, nil)\n\tusers.Register(caller, \"satoshi\", \"my other profile\")\n\t// as admin, grant invites to gnouser(again) and satoshi.\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\\n\" + test1.String() + \":1\")\n\tprintln(\"done\")\n}\n\n// Output:\n// done\n" + }, + { + "name": "z_7b_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// as admin, grant invites to gnouser\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\\n\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"test1\")\n\tusers.Invite(test1.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test1)\n\tstd.TestSetOrigSend(std.Coins{{\"dontcare\", 1}}, nil)\n\tusers.Register(caller, \"satoshi\", \"my other profile\")\n\t// as admin, grant invites to gnouser(again) and satoshi.\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\\n\" + test1.String() + \":1\")\n\tprintln(\"done\")\n}\n\n// Output:\n// done\n" + }, + { + "name": "z_8_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// as admin, grant invites to gnouser\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"test1\")\n\tusers.Invite(test1.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test1)\n\tstd.TestSetOrigSend(std.Coins{{\"dontcare\", 1}}, nil)\n\tusers.Register(caller, \"satoshi\", \"my other profile\")\n\t// as admin, grant invites to gnouser(again) and nonexistent user.\n\tstd.TestSetOrigCaller(admin)\n\ttest2 := testutils.TestAddress(\"test2\")\n\tusers.GrantInvites(caller.String() + \":1\\n\" + test2.String() + \":1\")\n\tprintln(\"done\")\n}\n\n// Error:\n// invalid user g1w3jhxapjta047h6lta047h6lta047h6laqcyu4\n" + }, + { + "name": "z_9_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\ttest2 := testutils.TestAddress(\"test2\")\n\t// as admin, invite gnouser and test2\n\tstd.TestSetOrigCaller(admin)\n\tusers.Invite(caller.String() + \"\\n\" + test2.String())\n\t// register as caller\n\tstd.TestSetOrigCaller(caller)\n\tusers.Register(admin, \"gnouser\", \"my profile\")\n\t// register as test2\n\tstd.TestSetOrigCaller(test2)\n\tusers.Register(admin, \"test222\", \"my profile 2\")\n\tprintln(\"done\")\n}\n\n// Output:\n// done\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "boards", + "path": "gno.land/r/demo/boards", + "files": [ + { + "name": "README.md", + "body": "This is a demo of Gno smart contract programming. This document was\nconstructed by Gno onto a smart contract hosted on the data Realm\nname [\"gno.land/r/demo/boards\"](https://gno.land/r/demo/boards/)\n([github](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/boards)).\n\n\n\n## Build `gnokey`, create your account, and interact with Gno.\n\nNOTE: Where you see `-remote localhost:26657` here, that flag can be replaced\nwith `-remote test3.gno.land:26657` if you have $GNOT on the testnet.\n(To use the testnet, also replace `-chainid dev` with `-chainid test3` .)\n\n### Build `gnokey` (and other tools).\n\n```bash\ngit clone git@github.com:gnolang/gno.git\ncd gno/gno.land\nmake build\n```\n\n### Generate a seed/mnemonic code.\n\n```bash\n./build/gnokey generate\n```\n\nNOTE: You can generate 24 words with any good bip39 generator.\n\n### Create a new account using your mnemonic.\n\n```bash\n./build/gnokey add -recover KEYNAME\n```\n\nNOTE: `KEYNAME` is your key identifier, and should be changed.\n\n### Verify that you can see your account locally.\n\n```bash\n./build/gnokey list\n```\n\nTake note of your `addr` which looks something like `g17sphqax3kasjptdkmuqvn740u8dhtx4kxl6ljf` .\nYou will use this as your `ACCOUNT_ADDR`.\n\n## Interact with the blockchain.\n\n### Add $GNOT for your account.\n\nBefore starting the `gnoland` node for the first time, your new account can be given $GNOT in the node genesis.\nEdit the file `gno.land/genesis/genesis_balances.txt` and add the following line (simlar to the others), using\nyour `ACCOUNT_ADDR` and `KEYNAME`\n\n`ACCOUNT_ADDR=10000000000ugnot # @KEYNAME`\n\n### Alternative: Run a faucet to add $GNOT.\n\nInstead of editing `gno.land/genesis/genesis_balances.txt`, a more general solution (with more steps)\nis to run a local \"faucet\" and use the web browser to add $GNOT. (This can be done at any time.)\nSee this page: https://github.com/gnolang/gno/blob/master/gno.land/cmd/gnofaucet/README.md\n\n### Start the `gnoland` node.\n\n```bash\n./build/gnoland start\n```\n\nNOTE: The node already has the \"boards\" realm.\n\nLeave this running in the terminal. In a new terminal, cd to the same folder `gno/gno.land` .\n\n### Get your current balance, account number, and sequence number.\n\n```bash\n./build/gnokey query auth/accounts/ACCOUNT_ADDR -remote localhost:26657\n```\n\n### Register a board username with a smart contract call.\n\nThe `USERNAME` for posting can different than your `KEYNAME`. It is internally linked to your `ACCOUNT_ADDR`. It must be at least 6 characters, lowercase alphanumeric with underscore.\n\n```bash\n./build/gnokey maketx call -pkgpath \"gno.land/r/demo/users\" -func \"Register\" -args \"\" -args \"USERNAME\" -args \"Profile description\" -gas-fee \"10000000ugnot\" -gas-wanted \"2000000\" -send \"200000000ugnot\" -broadcast -chainid dev -remote 127.0.0.1:26657 KEYNAME\n```\n\nInteractive documentation: https://test3.gno.land/r/demo/users?help\u0026__func=Register\n\n### Create a board with a smart contract call.\n\n```bash\n./build/gnokey maketx call -pkgpath \"gno.land/r/demo/boards\" -func \"CreateBoard\" -args \"BOARDNAME\" -gas-fee \"1000000ugnot\" -gas-wanted \"10000000\" -broadcast -chainid dev -remote localhost:26657 KEYNAME\n```\n\nInteractive documentation: https://test3.gno.land/r/demo/boards?help\u0026__func=CreateBoard\n\nNext, query for the permanent board ID by querying (you need this to create a new post):\n\n```bash\n./build/gnokey query \"vm/qeval\" -data 'gno.land/r/demo/boards.GetBoardIDFromName(\"BOARDNAME\")' -remote localhost:26657\n```\n\n### Create a post of a board with a smart contract call.\n\nNOTE: If a board was created successfully, your SEQUENCE_NUMBER would have increased.\n\n```bash\n./build/gnokey maketx call -pkgpath \"gno.land/r/demo/boards\" -func \"CreateThread\" -args BOARD_ID -args \"Hello gno.land\" -args \"Text of the post\" -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid dev -remote localhost:26657 KEYNAME\n```\n\nInteractive documentation: https://test3.gno.land/r/demo/boards?help\u0026__func=CreateThread\n\n### Create a comment to a post.\n\n```bash\n./build/gnokey maketx call -pkgpath \"gno.land/r/demo/boards\" -func \"CreateReply\" -args BOARD_ID -args \"1\" -args \"1\" -args \"Nice to meet you too.\" -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid dev -remote localhost:26657 KEYNAME\n```\n\nInteractive documentation: https://test3.gno.land/r/demo/boards?help\u0026__func=CreateReply\n\n```bash\n./build/gnokey query \"vm/qrender\" -data \"gno.land/r/demo/boards:BOARDNAME/1\" -remote localhost:26657\n```\n\n### Render page with optional path expression.\n\nThe contents of `https://gno.land/r/demo/boards:` and `https://gno.land/r/demo/boards:gnolang` are rendered by calling\nthe `Render(path string)` function like so:\n\n```bash\n./build/gnokey query \"vm/qrender\" -data \"gno.land/r/demo/boards:gnolang\"\n```\n## View the board in the browser.\n\n### Start the web server.\n\n```bash\n./build/gnoweb\n```\n\nThis should print something like `Running on http://127.0.0.1:8888` . Leave this running in the terminal.\n\n### View in the browser\n\nIn your browser, navigate to the printed address http://127.0.0.1:8888 .\nTo see you post, click on the package `/r/demo/boards` .\n" + }, + { + "name": "board.gno", + "body": "package boards\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\n//----------------------------------------\n// Board\n\ntype BoardID uint64\n\nfunc (bid BoardID) String() string {\n\treturn strconv.Itoa(int(bid))\n}\n\ntype Board struct {\n\tid BoardID // only set for public boards.\n\turl string\n\tname string\n\tcreator std.Address\n\tthreads avl.Tree // Post.id -\u003e *Post\n\tpostsCtr uint64 // increments Post.id\n\tcreatedAt time.Time\n\tdeleted avl.Tree // TODO reserved for fast-delete.\n}\n\nfunc newBoard(id BoardID, url string, name string, creator std.Address) *Board {\n\tif !reName.MatchString(name) {\n\t\tpanic(\"invalid name: \" + name)\n\t}\n\texists := gBoardsByName.Has(name)\n\tif exists {\n\t\tpanic(\"board already exists\")\n\t}\n\treturn \u0026Board{\n\t\tid: id,\n\t\turl: url,\n\t\tname: name,\n\t\tcreator: creator,\n\t\tthreads: avl.Tree{},\n\t\tcreatedAt: time.Now(),\n\t\tdeleted: avl.Tree{},\n\t}\n}\n\n/* TODO support this once we figure out how to ensure URL correctness.\n// A private board is not tracked by gBoards*,\n// but must be persisted by the caller's realm.\n// Private boards have 0 id and does not ping\n// back the remote board on reposts.\nfunc NewPrivateBoard(url string, name string, creator std.Address) *Board {\n\treturn newBoard(0, url, name, creator)\n}\n*/\n\nfunc (board *Board) IsPrivate() bool {\n\treturn board.id == 0\n}\n\nfunc (board *Board) GetThread(pid PostID) *Post {\n\tpidkey := postIDKey(pid)\n\tpostI, exists := board.threads.Get(pidkey)\n\tif !exists {\n\t\treturn nil\n\t}\n\treturn postI.(*Post)\n}\n\nfunc (board *Board) AddThread(creator std.Address, title string, body string) *Post {\n\tpid := board.incGetPostID()\n\tpidkey := postIDKey(pid)\n\tthread := newPost(board, pid, creator, title, body, pid, 0, 0)\n\tboard.threads.Set(pidkey, thread)\n\treturn thread\n}\n\n// NOTE: this can be potentially very expensive for threads with many replies.\n// TODO: implement optional fast-delete where thread is simply moved.\nfunc (board *Board) DeleteThread(pid PostID) {\n\tpidkey := postIDKey(pid)\n\t_, removed := board.threads.Remove(pidkey)\n\tif !removed {\n\t\tpanic(\"thread does not exist with id \" + pid.String())\n\t}\n}\n\nfunc (board *Board) HasPermission(addr std.Address, perm Permission) bool {\n\tif board.creator == addr {\n\t\tswitch perm {\n\t\tcase EditPermission:\n\t\t\treturn true\n\t\tcase DeletePermission:\n\t\t\treturn true\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\treturn false\n}\n\n// Renders the board for display suitable as plaintext in\n// console. This is suitable for demonstration or tests,\n// but not for prod.\nfunc (board *Board) RenderBoard() string {\n\tstr := \"\"\n\tstr += \"\\\\[[post](\" + board.GetPostFormURL() + \")]\\n\\n\"\n\tif board.threads.Size() \u003e 0 {\n\t\tboard.threads.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tif str != \"\" {\n\t\t\t\tstr += \"----------------------------------------\\n\"\n\t\t\t}\n\t\t\tstr += value.(*Post).RenderSummary() + \"\\n\"\n\t\t\treturn false\n\t\t})\n\t}\n\treturn str\n}\n\nfunc (board *Board) incGetPostID() PostID {\n\tboard.postsCtr++\n\treturn PostID(board.postsCtr)\n}\n\nfunc (board *Board) GetURLFromThreadAndReplyID(threadID, replyID PostID) string {\n\tif replyID == 0 {\n\t\treturn board.url + \"/\" + threadID.String()\n\t} else {\n\t\treturn board.url + \"/\" + threadID.String() + \"/\" + replyID.String()\n\t}\n}\n\nfunc (board *Board) GetPostFormURL() string {\n\treturn \"/r/demo/boards?help\u0026__func=CreateThread\" +\n\t\t\"\u0026bid=\" + board.id.String() +\n\t\t\"\u0026body.type=textarea\"\n}\n" + }, + { + "name": "boards.gno", + "body": "package boards\n\nimport (\n\t\"regexp\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\n//----------------------------------------\n// Realm (package) state\n\nvar (\n\tgBoards avl.Tree // id -\u003e *Board\n\tgBoardsCtr int // increments Board.id\n\tgBoardsByName avl.Tree // name -\u003e *Board\n\tgDefaultAnonFee = 100000000 // minimum fee required if anonymous\n)\n\n//----------------------------------------\n// Constants\n\nvar reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{2,29}$`)\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/boards\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" + }, + { + "name": "misc.gno", + "body": "package boards\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/r/demo/users\"\n)\n\n//----------------------------------------\n// private utility methods\n// XXX ensure these cannot be called from public.\n\nfunc getBoard(bid BoardID) *Board {\n\tbidkey := boardIDKey(bid)\n\tboard_, exists := gBoards.Get(bidkey)\n\tif !exists {\n\t\treturn nil\n\t}\n\tboard := board_.(*Board)\n\treturn board\n}\n\nfunc incGetBoardID() BoardID {\n\tgBoardsCtr++\n\treturn BoardID(gBoardsCtr)\n}\n\nfunc padLeft(str string, length int) string {\n\tif len(str) \u003e= length {\n\t\treturn str\n\t} else {\n\t\treturn strings.Repeat(\" \", length-len(str)) + str\n\t}\n}\n\nfunc padZero(u64 uint64, length int) string {\n\tstr := strconv.Itoa(int(u64))\n\tif len(str) \u003e= length {\n\t\treturn str\n\t} else {\n\t\treturn strings.Repeat(\"0\", length-len(str)) + str\n\t}\n}\n\nfunc boardIDKey(bid BoardID) string {\n\treturn padZero(uint64(bid), 10)\n}\n\nfunc postIDKey(pid PostID) string {\n\treturn padZero(uint64(pid), 10)\n}\n\nfunc indentBody(indent string, body string) string {\n\tlines := strings.Split(body, \"\\n\")\n\tres := \"\"\n\tfor i, line := range lines {\n\t\tif i \u003e 0 {\n\t\t\tres += \"\\n\"\n\t\t}\n\t\tres += indent + line\n\t}\n\treturn res\n}\n\n// NOTE: length must be greater than 3.\nfunc summaryOf(str string, length int) string {\n\tlines := strings.SplitN(str, \"\\n\", 2)\n\tline := lines[0]\n\tif len(line) \u003e length {\n\t\tline = line[:(length-3)] + \"...\"\n\t} else if len(lines) \u003e 1 {\n\t\t// len(line) \u003c= 80\n\t\tline = line + \"...\"\n\t}\n\treturn line\n}\n\nfunc displayAddressMD(addr std.Address) string {\n\tuser := users.GetUserByAddress(addr)\n\tif user == nil {\n\t\treturn \"[\" + addr.String() + \"](/r/demo/users:\" + addr.String() + \")\"\n\t} else {\n\t\treturn \"[@\" + user.Name + \"](/r/demo/users:\" + user.Name + \")\"\n\t}\n}\n\nfunc usernameOf(addr std.Address) string {\n\tuser := users.GetUserByAddress(addr)\n\tif user == nil {\n\t\treturn \"\"\n\t}\n\treturn user.Name\n}\n" + }, + { + "name": "post.gno", + "body": "package boards\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\n//----------------------------------------\n// Post\n\n// NOTE: a PostID is relative to the board.\ntype PostID uint64\n\nfunc (pid PostID) String() string {\n\treturn strconv.Itoa(int(pid))\n}\n\n// A Post is a \"thread\" or a \"reply\" depending on context.\n// A thread is a Post of a Board that holds other replies.\ntype Post struct {\n\tboard *Board\n\tid PostID\n\tcreator std.Address\n\ttitle string // optional\n\tbody string\n\treplies avl.Tree // Post.id -\u003e *Post\n\trepliesAll avl.Tree // Post.id -\u003e *Post (all replies, for top-level posts)\n\treposts avl.Tree // Board.id -\u003e Post.id\n\tthreadID PostID // original Post.id\n\tparentID PostID // parent Post.id (if reply or repost)\n\trepostBoard BoardID // original Board.id (if repost)\n\tcreatedAt time.Time\n\tupdatedAt time.Time\n}\n\nfunc newPost(board *Board, id PostID, creator std.Address, title, body string, threadID, parentID PostID, repostBoard BoardID) *Post {\n\treturn \u0026Post{\n\t\tboard: board,\n\t\tid: id,\n\t\tcreator: creator,\n\t\ttitle: title,\n\t\tbody: body,\n\t\treplies: avl.Tree{},\n\t\trepliesAll: avl.Tree{},\n\t\treposts: avl.Tree{},\n\t\tthreadID: threadID,\n\t\tparentID: parentID,\n\t\trepostBoard: repostBoard,\n\t\tcreatedAt: time.Now(),\n\t}\n}\n\nfunc (post *Post) IsThread() bool {\n\treturn post.parentID == 0\n}\n\nfunc (post *Post) GetPostID() PostID {\n\treturn post.id\n}\n\nfunc (post *Post) AddReply(creator std.Address, body string) *Post {\n\tboard := post.board\n\tpid := board.incGetPostID()\n\tpidkey := postIDKey(pid)\n\treply := newPost(board, pid, creator, \"\", body, post.threadID, post.id, 0)\n\tpost.replies.Set(pidkey, reply)\n\tif post.threadID == post.id {\n\t\tpost.repliesAll.Set(pidkey, reply)\n\t} else {\n\t\tthread := board.GetThread(post.threadID)\n\t\tthread.repliesAll.Set(pidkey, reply)\n\t}\n\treturn reply\n}\n\nfunc (post *Post) Update(title string, body string) {\n\tpost.title = title\n\tpost.body = body\n\tpost.updatedAt = time.Now()\n}\n\nfunc (thread *Post) GetReply(pid PostID) *Post {\n\tpidkey := postIDKey(pid)\n\treplyI, ok := thread.repliesAll.Get(pidkey)\n\tif !ok {\n\t\treturn nil\n\t} else {\n\t\treturn replyI.(*Post)\n\t}\n}\n\nfunc (post *Post) AddRepostTo(creator std.Address, title, body string, dst *Board) *Post {\n\tif !post.IsThread() {\n\t\tpanic(\"cannot repost non-thread post\")\n\t}\n\tpid := dst.incGetPostID()\n\tpidkey := postIDKey(pid)\n\trepost := newPost(dst, pid, creator, title, body, pid, post.id, post.board.id)\n\tdst.threads.Set(pidkey, repost)\n\tif !dst.IsPrivate() {\n\t\tbidkey := boardIDKey(dst.id)\n\t\tpost.reposts.Set(bidkey, pid)\n\t}\n\treturn repost\n}\n\nfunc (thread *Post) DeletePost(pid PostID) {\n\tif thread.id == pid {\n\t\tpanic(\"should not happen\")\n\t}\n\tpidkey := postIDKey(pid)\n\tpostI, removed := thread.repliesAll.Remove(pidkey)\n\tif !removed {\n\t\tpanic(\"post not found in thread\")\n\t}\n\tpost := postI.(*Post)\n\tif post.parentID != thread.id {\n\t\tparent := thread.GetReply(post.parentID)\n\t\tparent.replies.Remove(pidkey)\n\t} else {\n\t\tthread.replies.Remove(pidkey)\n\t}\n}\n\nfunc (post *Post) HasPermission(addr std.Address, perm Permission) bool {\n\tif post.creator == addr {\n\t\tswitch perm {\n\t\tcase EditPermission:\n\t\t\treturn true\n\t\tcase DeletePermission:\n\t\t\treturn true\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\t// post notes inherit permissions of the board.\n\treturn post.board.HasPermission(addr, perm)\n}\n\nfunc (post *Post) GetSummary() string {\n\treturn summaryOf(post.body, 80)\n}\n\nfunc (post *Post) GetURL() string {\n\tif post.IsThread() {\n\t\treturn post.board.GetURLFromThreadAndReplyID(\n\t\t\tpost.id, 0)\n\t} else {\n\t\treturn post.board.GetURLFromThreadAndReplyID(\n\t\t\tpost.threadID, post.id)\n\t}\n}\n\nfunc (post *Post) GetReplyFormURL() string {\n\treturn \"/r/demo/boards?help\u0026__func=CreateReply\" +\n\t\t\"\u0026bid=\" + post.board.id.String() +\n\t\t\"\u0026threadid=\" + post.threadID.String() +\n\t\t\"\u0026postid=\" + post.id.String() +\n\t\t\"\u0026body.type=textarea\"\n}\n\nfunc (post *Post) GetRepostFormURL() string {\n\treturn \"/r/demo/boards?help\u0026__func=CreateRepost\" +\n\t\t\"\u0026bid=\" + post.board.id.String() +\n\t\t\"\u0026postid=\" + post.id.String() +\n\t\t\"\u0026title.type=textarea\" +\n\t\t\"\u0026body.type=textarea\" +\n\t\t\"\u0026dstBoardID.type=textarea\"\n}\n\nfunc (post *Post) GetDeleteFormURL() string {\n\treturn \"/r/demo/boards?help\u0026__func=DeletePost\" +\n\t\t\"\u0026bid=\" + post.board.id.String() +\n\t\t\"\u0026threadid=\" + post.threadID.String() +\n\t\t\"\u0026postid=\" + post.id.String()\n}\n\nfunc (post *Post) RenderSummary() string {\n\tif post.repostBoard != 0 {\n\t\tdstBoard := getBoard(post.repostBoard)\n\t\tif dstBoard == nil {\n\t\t\tpanic(\"repostBoard does not exist\")\n\t\t}\n\t\tthread := dstBoard.GetThread(PostID(post.parentID))\n\t\tif thread == nil {\n\t\t\treturn \"reposted post does not exist\"\n\t\t}\n\t\treturn \"Repost: \" + post.GetSummary() + \"\\n\" + thread.RenderSummary()\n\t}\n\tstr := \"\"\n\tif post.title != \"\" {\n\t\tstr += \"## [\" + summaryOf(post.title, 80) + \"](\" + post.GetURL() + \")\\n\"\n\t\tstr += \"\\n\"\n\t}\n\tstr += post.GetSummary() + \"\\n\"\n\tstr += \"\\\\- \" + displayAddressMD(post.creator) + \",\"\n\tstr += \" [\" + post.createdAt.Format(\"2006-01-02 3:04pm MST\") + \"](\" + post.GetURL() + \")\"\n\tstr += \" \\\\[[x](\" + post.GetDeleteFormURL() + \")]\"\n\tstr += \" (\" + strconv.Itoa(post.replies.Size()) + \" replies)\"\n\tstr += \" (\" + strconv.Itoa(post.reposts.Size()) + \" reposts)\" + \"\\n\"\n\treturn str\n}\n\nfunc (post *Post) RenderPost(indent string, levels int) string {\n\tif post == nil {\n\t\treturn \"nil post\"\n\t}\n\tstr := \"\"\n\tif post.title != \"\" {\n\t\tstr += indent + \"# \" + post.title + \"\\n\"\n\t\tstr += indent + \"\\n\"\n\t}\n\tstr += indentBody(indent, post.body) + \"\\n\" // TODO: indent body lines.\n\tstr += indent + \"\\\\- \" + displayAddressMD(post.creator) + \", \"\n\tstr += \"[\" + post.createdAt.Format(\"2006-01-02 3:04pm (MST)\") + \"](\" + post.GetURL() + \")\"\n\tstr += \" \\\\[[reply](\" + post.GetReplyFormURL() + \")]\"\n\tif post.IsThread() {\n\t\tstr += \" \\\\[[repost](\" + post.GetRepostFormURL() + \")]\"\n\t}\n\tstr += \" \\\\[[x](\" + post.GetDeleteFormURL() + \")]\\n\"\n\tif levels \u003e 0 {\n\t\tif post.replies.Size() \u003e 0 {\n\t\t\tpost.replies.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\t\tstr += indent + \"\\n\"\n\t\t\t\tstr += value.(*Post).RenderPost(indent+\"\u003e \", levels-1)\n\t\t\t\treturn false\n\t\t\t})\n\t\t}\n\t} else {\n\t\tif post.replies.Size() \u003e 0 {\n\t\t\tstr += indent + \"\\n\"\n\t\t\tstr += indent + \"_[see all \" + strconv.Itoa(post.replies.Size()) + \" replies](\" + post.GetURL() + \")_\\n\"\n\t\t}\n\t}\n\treturn str\n}\n\n// render reply and link to context thread\nfunc (post *Post) RenderInner() string {\n\tif post.IsThread() {\n\t\tpanic(\"unexpected thread\")\n\t}\n\tthreadID := post.threadID\n\t// replyID := post.id\n\tparentID := post.parentID\n\tstr := \"\"\n\tstr += \"_[see thread](\" + post.board.GetURLFromThreadAndReplyID(\n\t\tthreadID, 0) + \")_\\n\\n\"\n\tthread := post.board.GetThread(post.threadID)\n\tvar parent *Post\n\tif thread.id == parentID {\n\t\tparent = thread\n\t} else {\n\t\tparent = thread.GetReply(parentID)\n\t}\n\tstr += parent.RenderPost(\"\", 0)\n\tstr += \"\\n\"\n\tstr += post.RenderPost(\"\u003e \", 5)\n\treturn str\n}\n" + }, + { + "name": "public.gno", + "body": "package boards\n\nimport (\n\t\"std\"\n\t\"strconv\"\n)\n\n//----------------------------------------\n// Public facing functions\n\nfunc GetBoardIDFromName(name string) (BoardID, bool) {\n\tboardI, exists := gBoardsByName.Get(name)\n\tif !exists {\n\t\treturn 0, false\n\t}\n\treturn boardI.(*Board).id, true\n}\n\nfunc CreateBoard(name string) BoardID {\n\tif !(std.IsOriginCall() || std.PrevRealm().IsUser()) {\n\t\tpanic(\"invalid non-user call\")\n\t}\n\tbid := incGetBoardID()\n\tcaller := std.GetOrigCaller()\n\tif usernameOf(caller) == \"\" {\n\t\tpanic(\"unauthorized\")\n\t}\n\turl := \"/r/demo/boards:\" + name\n\tboard := newBoard(bid, url, name, caller)\n\tbidkey := boardIDKey(bid)\n\tgBoards.Set(bidkey, board)\n\tgBoardsByName.Set(name, board)\n\treturn board.id\n}\n\nfunc checkAnonFee() bool {\n\tsent := std.GetOrigSend()\n\tanonFeeCoin := std.NewCoin(\"ugnot\", int64(gDefaultAnonFee))\n\tif len(sent) == 1 \u0026\u0026 sent[0].IsGTE(anonFeeCoin) {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc CreateThread(bid BoardID, title string, body string) PostID {\n\tif !(std.IsOriginCall() || std.PrevRealm().IsUser()) {\n\t\tpanic(\"invalid non-user call\")\n\t}\n\tcaller := std.GetOrigCaller()\n\tif usernameOf(caller) == \"\" {\n\t\tif !checkAnonFee() {\n\t\t\tpanic(\"please register, otherwise minimum fee \" + strconv.Itoa(gDefaultAnonFee) + \" is required if anonymous\")\n\t\t}\n\t}\n\tboard := getBoard(bid)\n\tif board == nil {\n\t\tpanic(\"board not exist\")\n\t}\n\tthread := board.AddThread(caller, title, body)\n\treturn thread.id\n}\n\nfunc CreateReply(bid BoardID, threadid, postid PostID, body string) PostID {\n\tif !(std.IsOriginCall() || std.PrevRealm().IsUser()) {\n\t\tpanic(\"invalid non-user call\")\n\t}\n\tcaller := std.GetOrigCaller()\n\tif usernameOf(caller) == \"\" {\n\t\tif !checkAnonFee() {\n\t\t\tpanic(\"please register, otherwise minimum fee \" + strconv.Itoa(gDefaultAnonFee) + \" is required if anonymous\")\n\t\t}\n\t}\n\tboard := getBoard(bid)\n\tif board == nil {\n\t\tpanic(\"board not exist\")\n\t}\n\tthread := board.GetThread(threadid)\n\tif thread == nil {\n\t\tpanic(\"thread not exist\")\n\t}\n\tif postid == threadid {\n\t\treply := thread.AddReply(caller, body)\n\t\treturn reply.id\n\t} else {\n\t\tpost := thread.GetReply(postid)\n\t\treply := post.AddReply(caller, body)\n\t\treturn reply.id\n\t}\n}\n\n// If dstBoard is private, does not ping back.\n// If board specified by bid is private, panics.\nfunc CreateRepost(bid BoardID, postid PostID, title string, body string, dstBoardID BoardID) PostID {\n\tif !(std.IsOriginCall() || std.PrevRealm().IsUser()) {\n\t\tpanic(\"invalid non-user call\")\n\t}\n\tcaller := std.GetOrigCaller()\n\tif usernameOf(caller) == \"\" {\n\t\t// TODO: allow with gDefaultAnonFee payment.\n\t\tif !checkAnonFee() {\n\t\t\tpanic(\"please register, otherwise minimum fee \" + strconv.Itoa(gDefaultAnonFee) + \" is required if anonymous\")\n\t\t}\n\t}\n\tboard := getBoard(bid)\n\tif board == nil {\n\t\tpanic(\"src board not exist\")\n\t}\n\tif board.IsPrivate() {\n\t\tpanic(\"cannot repost from a private board\")\n\t}\n\tdst := getBoard(dstBoardID)\n\tif dst == nil {\n\t\tpanic(\"dst board not exist\")\n\t}\n\tthread := board.GetThread(postid)\n\tif thread == nil {\n\t\tpanic(\"thread not exist\")\n\t}\n\trepost := thread.AddRepostTo(caller, title, body, dst)\n\treturn repost.id\n}\n\nfunc DeletePost(bid BoardID, threadid, postid PostID, reason string) {\n\tif !(std.IsOriginCall() || std.PrevRealm().IsUser()) {\n\t\tpanic(\"invalid non-user call\")\n\t}\n\tcaller := std.GetOrigCaller()\n\tboard := getBoard(bid)\n\tif board == nil {\n\t\tpanic(\"board not exist\")\n\t}\n\tthread := board.GetThread(threadid)\n\tif thread == nil {\n\t\tpanic(\"thread not exist\")\n\t}\n\tif postid == threadid {\n\t\t// delete thread\n\t\tif !thread.HasPermission(caller, DeletePermission) {\n\t\t\tpanic(\"unauthorized\")\n\t\t}\n\t\tboard.DeleteThread(threadid)\n\t} else {\n\t\t// delete thread's post\n\t\tpost := thread.GetReply(postid)\n\t\tif post == nil {\n\t\t\tpanic(\"post not exist\")\n\t\t}\n\t\tif !post.HasPermission(caller, DeletePermission) {\n\t\t\tpanic(\"unauthorized\")\n\t\t}\n\t\tthread.DeletePost(postid)\n\t}\n}\n\nfunc EditPost(bid BoardID, threadid, postid PostID, title, body string) {\n\tif !(std.IsOriginCall() || std.PrevRealm().IsUser()) {\n\t\tpanic(\"invalid non-user call\")\n\t}\n\tcaller := std.GetOrigCaller()\n\tboard := getBoard(bid)\n\tif board == nil {\n\t\tpanic(\"board not exist\")\n\t}\n\tthread := board.GetThread(threadid)\n\tif thread == nil {\n\t\tpanic(\"thread not exist\")\n\t}\n\tif postid == threadid {\n\t\t// edit thread\n\t\tif !thread.HasPermission(caller, EditPermission) {\n\t\t\tpanic(\"unauthorized\")\n\t\t}\n\t\tthread.Update(title, body)\n\t} else {\n\t\t// edit thread's post\n\t\tpost := thread.GetReply(postid)\n\t\tif post == nil {\n\t\t\tpanic(\"post not exist\")\n\t\t}\n\t\tif !post.HasPermission(caller, EditPermission) {\n\t\t\tpanic(\"unauthorized\")\n\t\t}\n\t\tpost.Update(title, body)\n\t}\n}\n" + }, + { + "name": "render.gno", + "body": "package boards\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\n//----------------------------------------\n// Render functions\n\nfunc RenderBoard(bid BoardID) string {\n\tboard := getBoard(bid)\n\tif board == nil {\n\t\treturn \"missing board\"\n\t}\n\treturn board.RenderBoard()\n}\n\nfunc Render(path string) string {\n\tif path == \"\" {\n\t\tstr := \"These are all the boards of this realm:\\n\\n\"\n\t\tgBoards.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tboard := value.(*Board)\n\t\t\tstr += \" * [\" + board.url + \"](\" + board.url + \")\\n\"\n\t\t\treturn false\n\t\t})\n\t\treturn str\n\t}\n\tparts := strings.Split(path, \"/\")\n\tif len(parts) == 1 {\n\t\t// /r/demo/boards:BOARD_NAME\n\t\tname := parts[0]\n\t\tboardI, exists := gBoardsByName.Get(name)\n\t\tif !exists {\n\t\t\treturn \"board does not exist: \" + name\n\t\t}\n\t\treturn boardI.(*Board).RenderBoard()\n\t} else if len(parts) == 2 {\n\t\t// /r/demo/boards:BOARD_NAME/THREAD_ID\n\t\tname := parts[0]\n\t\tboardI, exists := gBoardsByName.Get(name)\n\t\tif !exists {\n\t\t\treturn \"board does not exist: \" + name\n\t\t}\n\t\tpid, err := strconv.Atoi(parts[1])\n\t\tif err != nil {\n\t\t\treturn \"invalid thread id: \" + parts[1]\n\t\t}\n\t\tboard := boardI.(*Board)\n\t\tthread := board.GetThread(PostID(pid))\n\t\tif thread == nil {\n\t\t\treturn \"thread does not exist with id: \" + parts[1]\n\t\t}\n\t\treturn thread.RenderPost(\"\", 5)\n\t} else if len(parts) == 3 {\n\t\t// /r/demo/boards:BOARD_NAME/THREAD_ID/REPLY_ID\n\t\tname := parts[0]\n\t\tboardI, exists := gBoardsByName.Get(name)\n\t\tif !exists {\n\t\t\treturn \"board does not exist: \" + name\n\t\t}\n\t\tpid, err := strconv.Atoi(parts[1])\n\t\tif err != nil {\n\t\t\treturn \"invalid thread id: \" + parts[1]\n\t\t}\n\t\tboard := boardI.(*Board)\n\t\tthread := board.GetThread(PostID(pid))\n\t\tif thread == nil {\n\t\t\treturn \"thread does not exist with id: \" + parts[1]\n\t\t}\n\t\trid, err := strconv.Atoi(parts[2])\n\t\tif err != nil {\n\t\t\treturn \"invalid reply id: \" + parts[2]\n\t\t}\n\t\treply := thread.GetReply(PostID(rid))\n\t\tif reply == nil {\n\t\t\treturn \"reply does not exist with id: \" + parts[2]\n\t\t}\n\t\treturn reply.RenderInner()\n\t} else {\n\t\treturn \"unrecognized path \" + path\n\t}\n}\n" + }, + { + "name": "role.gno", + "body": "package boards\n\ntype Permission string\n\nconst (\n\tDeletePermission Permission = \"role:delete\"\n\tEditPermission Permission = \"role:edit\"\n)\n" + }, + { + "name": "z_0_a_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\nimport (\n\t\"gno.land/r/demo/boards\"\n)\n\nvar bid boards.BoardID\n\nfunc init() {\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tpid := boards.CreateThread(bid, \"Second Post (title)\", \"Body of the second post. (body)\")\n\tboards.CreateReply(bid, pid, pid, \"Reply of the second post\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board\"))\n}\n\n// Error:\n// unauthorized\n" + }, + { + "name": "z_0_b_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 19900000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar bid boards.BoardID\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tbid = boards.CreateBoard(\"test_board\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board\"))\n}\n\n// Error:\n// payment must not be less than 20000000\n" + }, + { + "name": "z_0_c_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar bid boards.BoardID\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tboards.CreateThread(1, \"First Post (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board\"))\n}\n\n// Error:\n// board not exist\n" + }, + { + "name": "z_0_d_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar bid boards.BoardID\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateReply(bid, 0, 0, \"Reply of the second post\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board\"))\n}\n\n// Error:\n// thread not exist\n" + }, + { + "name": "z_0_e_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar bid boards.BoardID\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tboards.CreateReply(bid, 0, 0, \"Reply of the second post\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board\"))\n}\n\n// Error:\n// board not exist\n" + }, + { + "name": "z_0_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 20000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar bid boards.BoardID\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tpid := boards.CreateThread(bid, \"Second Post (title)\", \"Body of the second post. (body)\")\n\tboards.CreateReply(bid, pid, pid, \"Reply of the second post\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board\"))\n}\n\n// Output:\n// \\[[post](/r/demo/boards?help\u0026__func=CreateThread\u0026bid=1\u0026body.type=textarea)]\n//\n// ----------------------------------------\n// ## [First Post (title)](/r/demo/boards:test_board/1)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/1) \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)] (0 replies) (0 reposts)\n//\n// ----------------------------------------\n// ## [Second Post (title)](/r/demo/boards:test_board/2)\n//\n// Body of the second post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/2) \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=2)] (1 replies) (0 reposts)\n" + }, + { + "name": "z_10_a_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\t// boardId 2 not exist\n\tboards.DeletePost(2, pid, pid, \"\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Error:\n// board not exist\n" + }, + { + "name": "z_10_b_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\t// pid of 2 not exist\n\tboards.DeletePost(bid, 2, 2, \"\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Error:\n// thread not exist\n" + }, + { + "name": "z_10_c_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n\trid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n\trid = boards.CreateReply(bid, pid, pid, \"First reply of the First post\\n\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\tboards.DeletePost(bid, pid, rid, \"\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # First Post in (title)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=1\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)]\n//\n// \u003e First reply of the First post\n// \u003e\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=2\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=2)]\n//\n// ----------------------------------------------------\n// # First Post in (title)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=1\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)]\n" + }, + { + "name": "z_10_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\tboards.DeletePost(bid, pid, pid, \"\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # First Post in (title)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=1\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)]\n//\n// ----------------------------------------------------\n// thread does not exist with id: 1\n" + }, + { + "name": "z_11_a_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\t// board 2 not exist\n\tboards.EditPost(2, pid, pid, \"Edited: First Post in (title)\", \"Edited: Body of the first post. (body)\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Error:\n// board not exist\n" + }, + { + "name": "z_11_b_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\t// thread 2 not exist\n\tboards.EditPost(bid, 2, pid, \"Edited: First Post in (title)\", \"Edited: Body of the first post. (body)\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Error:\n// thread not exist\n" + }, + { + "name": "z_11_c_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\t// post 2 not exist\n\tboards.EditPost(bid, pid, 2, \"Edited: First Post in (title)\", \"Edited: Body of the first post. (body)\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Error:\n// post not exist\n" + }, + { + "name": "z_11_d_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n\trid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n\trid = boards.CreateReply(bid, pid, pid, \"First reply of the First post\\n\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\tboards.EditPost(bid, pid, rid, \"\", \"Edited: First reply of the First post\\n\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # First Post in (title)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=1\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)]\n//\n// \u003e First reply of the First post\n// \u003e\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=2\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=2)]\n//\n// ----------------------------------------------------\n// # First Post in (title)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=1\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)]\n//\n// \u003e Edited: First reply of the First post\n// \u003e\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=2\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=2)]\n" + }, + { + "name": "z_11_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tpid = boards.CreateThread(bid, \"First Post in (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n\tboards.EditPost(bid, pid, pid, \"Edited: First Post in (title)\", \"Edited: Body of the first post. (body)\")\n\tprintln(\"----------------------------------------------------\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # First Post in (title)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=1\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)]\n//\n// ----------------------------------------------------\n// # Edited: First Post in (title)\n//\n// Edited: Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=1\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)]\n" + }, + { + "name": "z_12_a_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// create a post via registered user\n\tbid1 := boards.CreateBoard(\"test_board1\")\n\tpid := boards.CreateThread(bid1, \"First Post (title)\", \"Body of the first post. (body)\")\n\tbid2 := boards.CreateBoard(\"test_board2\")\n\n\t// create a repost via anon user\n\ttest2 := testutils.TestAddress(\"test2\")\n\tstd.TestSetOrigCaller(test2)\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 9000000}}, nil)\n\n\trid := boards.CreateRepost(bid1, pid, \"\", \"Check this out\", bid2)\n\tprintln(rid)\n\tprintln(boards.Render(\"test_board1\"))\n}\n\n// Error:\n// please register, otherwise minimum fee 100000000 is required if anonymous\n" + }, + { + "name": "z_12_b_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tbid1 := boards.CreateBoard(\"test_board1\")\n\tpid := boards.CreateThread(bid1, \"First Post (title)\", \"Body of the first post. (body)\")\n\tbid2 := boards.CreateBoard(\"test_board2\")\n\n\t// create a repost to a non-existing board\n\trid := boards.CreateRepost(5, pid, \"\", \"Check this out\", bid2)\n\tprintln(rid)\n\tprintln(boards.Render(\"test_board1\"))\n}\n\n// Error:\n// src board not exist\n" + }, + { + "name": "z_12_c_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tbid1 := boards.CreateBoard(\"test_board1\")\n\tboards.CreateThread(bid1, \"First Post (title)\", \"Body of the first post. (body)\")\n\tbid2 := boards.CreateBoard(\"test_board2\")\n\n\t// create a repost to a non-existing thread\n\trid := boards.CreateRepost(bid1, 5, \"\", \"Check this out\", bid2)\n\tprintln(rid)\n\tprintln(boards.Render(\"test_board1\"))\n}\n\n// Error:\n// thread not exist\n" + }, + { + "name": "z_12_d_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tbid1 := boards.CreateBoard(\"test_board1\")\n\tpid := boards.CreateThread(bid1, \"First Post (title)\", \"Body of the first post. (body)\")\n\tboards.CreateBoard(\"test_board2\")\n\n\t// create a repost to a non-existing destination board\n\trid := boards.CreateRepost(bid1, pid, \"\", \"Check this out\", 5)\n\tprintln(rid)\n\tprintln(boards.Render(\"test_board1\"))\n}\n\n// Error:\n// dst board not exist\n" + }, + { + "name": "z_12_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid1 boards.BoardID\n\tbid2 boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid1 = boards.CreateBoard(\"test_board1\")\n\tpid = boards.CreateThread(bid1, \"First Post (title)\", \"Body of the first post. (body)\")\n\tbid2 = boards.CreateBoard(\"test_board2\")\n}\n\nfunc main() {\n\trid := boards.CreateRepost(bid1, pid, \"\", \"Check this out\", bid2)\n\tprintln(rid)\n\tprintln(boards.Render(\"test_board2\"))\n}\n\n// Output:\n// 1\n// \\[[post](/r/demo/boards?help\u0026__func=CreateThread\u0026bid=2\u0026body.type=textarea)]\n//\n// ----------------------------------------\n// Repost: Check this out\n// ## [First Post (title)](/r/demo/boards:test_board1/1)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board1/1) \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)] (0 replies) (1 reposts)\n" + }, + { + "name": "z_1_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar board *boards.Board\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\t_ = boards.CreateBoard(\"test_board_1\")\n\t_ = boards.CreateBoard(\"test_board_2\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"\"))\n}\n\n// Output:\n// These are all the boards of this realm:\n//\n// * [/r/demo/boards:test_board_1](/r/demo/boards:test_board_1)\n// * [/r/demo/boards:test_board_2](/r/demo/boards:test_board_2)\n" + }, + { + "name": "z_2_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tpid = boards.CreateThread(bid, \"Second Post (title)\", \"Body of the second post. (body)\")\n\tboards.CreateReply(bid, pid, pid, \"Reply of the second post\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # Second Post (title)\n//\n// Body of the second post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=2\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=2\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=2)]\n//\n// \u003e Reply of the second post\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=3\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=3)]\n" + }, + { + "name": "z_3_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tpid = boards.CreateThread(bid, \"Second Post (title)\", \"Body of the second post. (body)\")\n}\n\nfunc main() {\n\trid := boards.CreateReply(bid, pid, pid, \"Reply of the second post\")\n\tprintln(rid)\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// 3\n// # Second Post (title)\n//\n// Body of the second post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=2\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=2\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=2)]\n//\n// \u003e Reply of the second post\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=3\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=3)]\n" + }, + { + "name": "z_4_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tpid = boards.CreateThread(bid, \"Second Post (title)\", \"Body of the second post. (body)\")\n\trid := boards.CreateReply(bid, pid, pid, \"Reply of the second post\")\n\tprintln(rid)\n}\n\nfunc main() {\n\trid2 := boards.CreateReply(bid, pid, pid, \"Second reply of the second post\")\n\tprintln(rid2)\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// 3\n// 4\n// # Second Post (title)\n//\n// Body of the second post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=2\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=2\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=2)]\n//\n// \u003e Reply of the second post\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=3\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=3)]\n//\n// \u003e Second reply of the second post\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=4\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=4)]\n\n// Realm:\n// switchrealm[\"gno.land/r/demo/users\"]\n// switchrealm[\"gno.land/r/demo/boards\"]\n// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111]={\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111\",\n// \"ModTime\": \"123\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"68663c8895d37d479e417c11e21badfe21345c61\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:112\"\n// }\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:125]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"0000000004\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.Post\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Escaped\": true,\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:125\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:124\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:124]={\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:124\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"3f34ac77289aa1d5f9a2f8b6d083138325816fb0\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:125\"\n// }\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"0000000004\"\n// }\n// },\n// {},\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"94a6665a44bac6ede7f3e3b87173e537b12f9532\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:111\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"bc8e5b4e782a0bbc4ac9689681f119beb7b34d59\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:124\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:122\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:122]={\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:122\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:106\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"9957eadbc91dd32f33b0d815e041a32dbdea0671\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:123\"\n// }\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:128]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:128\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:129]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:129\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:130]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:130\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:131]={\n// \"Fields\": [\n// {\n// \"N\": \"AAAAgJSeXbo=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"65536\"\n// }\n// },\n// {\n// \"N\": \"AbSNdvQQIhE=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"1024\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"time.Location\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Escaped\": true,\n// \"ObjectID\": \"336074805fc853987abe6f7fe3ad97a6a6f3077a:2\"\n// },\n// \"Index\": \"182\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:131\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:132]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"65536\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"1024\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"time.Location\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:132\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.Board\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Escaped\": true,\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:84\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"N\": \"BAAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.PostID\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"std.Address\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"Second reply of the second post\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Tree\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"f91e355bd19240f0f3350a7fa0e6a82b72225916\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:128\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Tree\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"9ee9c4117be283fc51ffcc5ecd65b75ecef5a9dd\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:129\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Tree\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"eb768b0140a5fe95f9c58747f0960d647dacfd42\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:130\"\n// }\n// },\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.PostID\"\n// }\n// },\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.PostID\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.BoardID\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"time.Time\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"0fd3352422af0a56a77ef2c9e88f479054e3d51f\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:131\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"time.Time\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"bed4afa8ffdbbf775451c947fc68b27a345ce32a\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:132\"\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126]={\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126\",\n// \"IsEscaped\": true,\n// \"ModTime\": \"0\",\n// \"RefCount\": \"2\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.Post\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"c45bbd47a46681a63af973db0ec2180922e4a8ae\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127\"\n// }\n// }\n// }\n// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:120]={\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:120\",\n// \"ModTime\": \"134\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"dc1f011553dc53e7a846049e08cc77fa35ea6a51\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:121\"\n// }\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:136]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"0000000004\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.Post\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Escaped\": true,\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:126\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:136\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:135\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:135]={\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:135\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"96b86b4585c7f1075d7794180a5581f72733a7ab\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:136\"\n// }\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"0000000004\"\n// }\n// },\n// {},\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AgAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"32274e1f28fb2b97d67a1262afd362d370de7faa\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:120\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"c2cfd6aec36a462f35bf02e5bf4a127aa1bb7ac2\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:135\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:133\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:133]={\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:133\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:107\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"5cb875179e86d32c517322af7a323b2a5f3e6cc5\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:134\"\n// }\n// }\n// }\n// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:85]={\n// \"Fields\": [\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/boards.BoardID\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"/r/demo/boards:test_board\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"test_board\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"std.Address\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Tree\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"a416a751c3a45a1e5cba11e737c51340b081e372\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:86\"\n// }\n// },\n// {\n// \"N\": \"BAAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"65536\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"time.Time\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"36299fccbc13f2a84c4629fad4cb940f0bd4b1c6\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:87\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Tree\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"af6ed0268f99b7f369329094eb6dfaea7812708b\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:88\"\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:85\",\n// \"ModTime\": \"121\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:84\",\n// \"RefCount\": \"1\"\n// }\n// }\n// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:106]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"9809329dc1ddc5d3556f7a8fa3c2cebcbf65560b\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:122\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:106\",\n// \"ModTime\": \"121\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:105\",\n// \"RefCount\": \"1\"\n// }\n// }\n// u[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:107]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"ceae9a1c4ed28bb51062e6ccdccfad0caafd1c4f\",\n// \"ObjectID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:133\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:107\",\n// \"ModTime\": \"121\",\n// \"OwnerID\": \"f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:105\",\n// \"RefCount\": \"1\"\n// }\n// }\n// switchrealm[\"gno.land/r/demo/boards\"]\n// switchrealm[\"gno.land/r/demo/users\"]\n// switchrealm[\"gno.land/r/demo/users\"]\n// switchrealm[\"gno.land/r/demo/users\"]\n// switchrealm[\"gno.land/r/demo/boards\"]\n// switchrealm[\"gno.land/r/demo/boards_test\"]\n" + }, + { + "name": "z_5_b_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// create board via registered user\n\tbid := boards.CreateBoard(\"test_board\")\n\n\t// create post via anon user\n\ttest2 := testutils.TestAddress(\"test2\")\n\tstd.TestSetOrigCaller(test2)\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 9000000}}, nil)\n\n\tpid := boards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Error:\n// please register, otherwise minimum fee 100000000 is required if anonymous\n" + }, + { + "name": "z_5_c_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// create board via registered user\n\tbid := boards.CreateBoard(\"test_board\")\n\n\t// create post via anon user\n\ttest2 := testutils.TestAddress(\"test2\")\n\tstd.TestSetOrigCaller(test2)\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 101000000}}, nil)\n\n\tpid := boards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tboards.CreateReply(bid, pid, pid, \"Reply of the first post\")\n\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # First Post (title)\n//\n// Body of the first post. (body)\n// \\- [g1w3jhxapjta047h6lta047h6lta047h6laqcyu4](/r/demo/users:g1w3jhxapjta047h6lta047h6lta047h6laqcyu4), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=1\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)]\n//\n// \u003e Reply of the first post\n// \u003e \\- [g1w3jhxapjta047h6lta047h6lta047h6laqcyu4](/r/demo/users:g1w3jhxapjta047h6lta047h6lta047h6laqcyu4), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=1\u0026postid=2\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=2)]\n" + }, + { + "name": "z_5_d_filetest.gno", + "body": "package main\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\t// create board via registered user\n\tbid := boards.CreateBoard(\"test_board\")\n\tpid := boards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\n\t// create reply via anon user\n\ttest2 := testutils.TestAddress(\"test2\")\n\tstd.TestSetOrigCaller(test2)\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 9000000}}, nil)\n\tboards.CreateReply(bid, pid, pid, \"Reply of the first post\")\n\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Error:\n// please register, otherwise minimum fee 100000000 is required if anonymous\n" + }, + { + "name": "z_5_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tpid = boards.CreateThread(bid, \"Second Post (title)\", \"Body of the second post. (body)\")\n\trid := boards.CreateReply(bid, pid, pid, \"Reply of the second post\")\n}\n\nfunc main() {\n\trid2 := boards.CreateReply(bid, pid, pid, \"Second reply of the second post\\n\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # Second Post (title)\n//\n// Body of the second post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=2\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=2\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=2)]\n//\n// \u003e Reply of the second post\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=3\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=3)]\n//\n// \u003e Second reply of the second post\n// \u003e\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=4\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=4)]\n" + }, + { + "name": "z_6_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n\trid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tpid = boards.CreateThread(bid, \"Second Post (title)\", \"Body of the second post. (body)\")\n\trid = boards.CreateReply(bid, pid, pid, \"Reply of the second post\")\n}\n\nfunc main() {\n\tboards.CreateReply(bid, pid, pid, \"Second reply of the second post\\n\")\n\tboards.CreateReply(bid, pid, rid, \"First reply of the first reply\\n\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # Second Post (title)\n//\n// Body of the second post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=2\u0026body.type=textarea)] \\[[repost](/r/demo/boards?help\u0026__func=CreateRepost\u0026bid=1\u0026postid=2\u0026title.type=textarea\u0026body.type=textarea\u0026dstBoardID.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=2)]\n//\n// \u003e Reply of the second post\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=3\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=3)]\n// \u003e\n// \u003e \u003e First reply of the first reply\n// \u003e \u003e\n// \u003e \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/5) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=5\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=5)]\n//\n// \u003e Second reply of the second post\n// \u003e\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=4\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=4)]\n" + }, + { + "name": "z_7_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nfunc init() {\n\t// register\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\t// create board and post\n\tbid := boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n}\n\nfunc main() {\n\tprintln(boards.Render(\"test_board\"))\n}\n\n// Output:\n// \\[[post](/r/demo/boards?help\u0026__func=CreateThread\u0026bid=1\u0026body.type=textarea)]\n//\n// ----------------------------------------\n// ## [First Post (title)](/r/demo/boards:test_board/1)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/1) \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=1\u0026postid=1)] (0 replies) (0 reposts)\n" + }, + { + "name": "z_8_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbid boards.BoardID\n\tpid boards.PostID\n\trid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tbid = boards.CreateBoard(\"test_board\")\n\tboards.CreateThread(bid, \"First Post (title)\", \"Body of the first post. (body)\")\n\tpid = boards.CreateThread(bid, \"Second Post (title)\", \"Body of the second post. (body)\")\n\trid = boards.CreateReply(bid, pid, pid, \"Reply of the second post\")\n}\n\nfunc main() {\n\tboards.CreateReply(bid, pid, pid, \"Second reply of the second post\\n\")\n\trid2 := boards.CreateReply(bid, pid, rid, \"First reply of the first reply\\n\")\n\tprintln(boards.Render(\"test_board/\" + strconv.Itoa(int(pid)) + \"/\" + strconv.Itoa(int(rid2))))\n}\n\n// Output:\n// _[see thread](/r/demo/boards:test_board/2)_\n//\n// Reply of the second post\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=3\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=3)]\n//\n// _[see all 1 replies](/r/demo/boards:test_board/2/3)_\n//\n// \u003e First reply of the first reply\n// \u003e\n// \u003e \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/5) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=1\u0026threadid=2\u0026postid=5\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=1\u0026threadid=2\u0026postid=5)]\n" + }, + { + "name": "z_9_a_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar dstBoard boards.BoardID\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tdstBoard = boards.CreateBoard(\"dst_board\")\n\n\tboards.CreateRepost(0, 0, \"First Post in (title)\", \"Body of the first post. (body)\", dstBoard)\n}\n\nfunc main() {\n}\n\n// Error:\n// src board not exist\n" + }, + { + "name": "z_9_b_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tsrcBoard boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tsrcBoard = boards.CreateBoard(\"first_board\")\n\tpid = boards.CreateThread(srcBoard, \"First Post in (title)\", \"Body of the first post. (body)\")\n\n\tboards.CreateRepost(srcBoard, pid, \"First Post in (title)\", \"Body of the first post. (body)\", 0)\n}\n\nfunc main() {\n}\n\n// Error:\n// dst board not exist\n" + }, + { + "name": "z_9_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/boards_test\npackage boards_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/r/demo/boards\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tfirstBoard boards.BoardID\n\tsecondBoard boards.BoardID\n\tpid boards.PostID\n)\n\nfunc init() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\n\tfirstBoard = boards.CreateBoard(\"first_board\")\n\tsecondBoard = boards.CreateBoard(\"second_board\")\n\tpid = boards.CreateThread(firstBoard, \"First Post in (title)\", \"Body of the first post. (body)\")\n\n\tboards.CreateRepost(firstBoard, pid, \"First Post in (title)\", \"Body of the first post. (body)\", secondBoard)\n}\n\nfunc main() {\n\tprintln(boards.Render(\"second_board/\" + strconv.Itoa(int(pid))))\n}\n\n// Output:\n// # First Post in (title)\n//\n// Body of the first post. (body)\n// \\- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:second_board/1/1) \\[[reply](/r/demo/boards?help\u0026__func=CreateReply\u0026bid=2\u0026threadid=1\u0026postid=1\u0026body.type=textarea)] \\[[x](/r/demo/boards?help\u0026__func=DeletePost\u0026bid=2\u0026threadid=1\u0026postid=1)]\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "groups", + "path": "gno.land/p/demo/groups", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/groups\n\nrequire (\n\tgno.land/p/demo/rat v0.0.0-latest\n\tgno.land/r/demo/boards v0.0.0-latest\n)\n" + }, + { + "name": "groups.gno", + "body": "package groups\n\nimport \"gno.land/r/demo/boards\"\n\n// TODO implement something and test.\ntype Group struct {\n\tBoard *boards.Board\n}\n" + }, + { + "name": "vote_set.gno", + "body": "package groups\n\nimport (\n\t\"std\"\n\t\"time\"\n\n\t\"gno.land/p/demo/rat\"\n)\n\n//----------------------------------------\n// VoteSet\n\ntype VoteSet interface {\n\t// number of present votes in set.\n\tSize() int\n\t// add or update vote for voter.\n\tSetVote(voter std.Address, value string) error\n\t// count the number of votes for value.\n\tCountVotes(value string) int\n}\n\n//----------------------------------------\n// VoteList\n\ntype Vote struct {\n\tVoter std.Address\n\tValue string\n}\n\ntype VoteList []Vote\n\nfunc NewVoteList() *VoteList {\n\treturn \u0026VoteList{}\n}\n\nfunc (vlist *VoteList) Size() int {\n\treturn len(*vlist)\n}\n\nfunc (vlist *VoteList) SetVote(voter std.Address, value string) error {\n\t// TODO optimize with binary algorithm\n\tfor i, vote := range *vlist {\n\t\tif vote.Voter == voter {\n\t\t\t// update vote\n\t\t\t(*vlist)[i] = Vote{\n\t\t\t\tVoter: voter,\n\t\t\t\tValue: value,\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\t*vlist = append(*vlist, Vote{\n\t\tVoter: voter,\n\t\tValue: value,\n\t})\n\treturn nil\n}\n\nfunc (vlist *VoteList) CountVotes(target string) int {\n\t// TODO optimize with binary algorithm\n\tvar count int\n\tfor _, vote := range *vlist {\n\t\tif vote.Value == target {\n\t\t\tcount++\n\t\t}\n\t}\n\treturn count\n}\n\n//----------------------------------------\n// Committee\n\ntype Committee struct {\n\tQuorum rat.Rat\n\tThreshold rat.Rat\n\tAddresses std.AddressSet\n}\n\n//----------------------------------------\n// VoteSession\n// NOTE: this seems a bit too formal and\n// complicated vs what might be possible;\n// something simpler, more informal.\n\ntype SessionStatus int\n\nconst (\n\tSessionNew SessionStatus = iota\n\tSessionStarted\n\tSessionCompleted\n\tSessionCanceled\n)\n\ntype VoteSession struct {\n\tName string\n\tCreator std.Address\n\tBody string\n\tStart time.Time\n\tDeadline time.Time\n\tStatus SessionStatus\n\tCommittee *Committee\n\tVotes VoteSet\n\tChoices []string\n\tResult string\n}\n" + }, + { + "name": "z_1_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\nimport (\n\t\"gno.land/p/demo/groups\"\n\t\"gno.land/p/demo/testutils\"\n)\n\nvar vset groups.VoteSet\n\nfunc init() {\n\taddr1 := testutils.TestAddress(\"test1\")\n\taddr2 := testutils.TestAddress(\"test2\")\n\tvset = groups.NewVoteList()\n\tvset.SetVote(addr1, \"yes\")\n\tvset.SetVote(addr2, \"yes\")\n}\n\nfunc main() {\n\tprintln(vset.Size())\n\tprintln(\"yes:\", vset.CountVotes(\"yes\"))\n\tprintln(\"no:\", vset.CountVotes(\"no\"))\n}\n\n// Output:\n// 2\n// yes: 2\n// no: 0\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "uint256", + "path": "gno.land/p/demo/uint256", + "files": [ + { + "name": "LICENSE", + "body": "BSD 3-Clause License\n\nCopyright 2020 uint256 Authors\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, + { + "name": "README.md", + "body": "# Fixed size 256-bit math library\n\nThis is a library specialized at replacing the `big.Int` library for math based on 256-bit types.\n\noriginal repository: [uint256](\u003chttps://github.com/holiman/uint256/tree/master\u003e)\n" + }, + { + "name": "arithmetic.gno", + "body": "// arithmetic provides arithmetic operations for Uint objects.\n// This includes basic binary operations such as addition, subtraction, multiplication, division, and modulo operations\n// as well as overflow checks, and negation. These functions are essential for numeric\n// calculations using 256-bit unsigned integers.\npackage uint256\n\nimport (\n\t\"math/bits\"\n)\n\n// Add sets z to the sum x+y\nfunc (z *Uint) Add(x, y *Uint) *Uint {\n\tvar carry uint64\n\tz.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0)\n\tz.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry)\n\tz.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry)\n\tz.arr[3], _ = bits.Add64(x.arr[3], y.arr[3], carry)\n\treturn z\n}\n\n// AddOverflow sets z to the sum x+y, and returns z and whether overflow occurred\nfunc (z *Uint) AddOverflow(x, y *Uint) (*Uint, bool) {\n\tvar carry uint64\n\tz.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0)\n\tz.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry)\n\tz.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry)\n\tz.arr[3], carry = bits.Add64(x.arr[3], y.arr[3], carry)\n\treturn z, carry != 0\n}\n\n// Sub sets z to the difference x-y\nfunc (z *Uint) Sub(x, y *Uint) *Uint {\n\tvar carry uint64\n\tz.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0)\n\tz.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry)\n\tz.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry)\n\tz.arr[3], _ = bits.Sub64(x.arr[3], y.arr[3], carry)\n\treturn z\n}\n\n// SubOverflow sets z to the difference x-y and returns z and true if the operation underflowed\nfunc (z *Uint) SubOverflow(x, y *Uint) (*Uint, bool) {\n\tvar carry uint64\n\tz.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0)\n\tz.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry)\n\tz.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry)\n\tz.arr[3], carry = bits.Sub64(x.arr[3], y.arr[3], carry)\n\treturn z, carry != 0\n}\n\n// Neg returns -x mod 2^256.\nfunc (z *Uint) Neg(x *Uint) *Uint {\n\treturn z.Sub(new(Uint), x)\n}\n\n// commented out for possible overflow\n// Mul sets z to the product x*y\nfunc (z *Uint) Mul(x, y *Uint) *Uint {\n\tvar (\n\t\tres Uint\n\t\tcarry uint64\n\t\tres1, res2, res3 uint64\n\t)\n\n\tcarry, res.arr[0] = bits.Mul64(x.arr[0], y.arr[0])\n\tcarry, res1 = umulHop(carry, x.arr[1], y.arr[0])\n\tcarry, res2 = umulHop(carry, x.arr[2], y.arr[0])\n\tres3 = x.arr[3]*y.arr[0] + carry\n\n\tcarry, res.arr[1] = umulHop(res1, x.arr[0], y.arr[1])\n\tcarry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry)\n\tres3 = res3 + x.arr[2]*y.arr[1] + carry\n\n\tcarry, res.arr[2] = umulHop(res2, x.arr[0], y.arr[2])\n\tres3 = res3 + x.arr[1]*y.arr[2] + carry\n\n\tres.arr[3] = res3 + x.arr[0]*y.arr[3]\n\n\treturn z.Set(\u0026res)\n}\n\n// MulOverflow sets z to the product x*y, and returns z and whether overflow occurred\nfunc (z *Uint) MulOverflow(x, y *Uint) (*Uint, bool) {\n\tp := umul(x, y)\n\tcopy(z.arr[:], p[:4])\n\treturn z, (p[4] | p[5] | p[6] | p[7]) != 0\n}\n\n// commented out for possible overflow\n// Div sets z to the quotient x/y for returns z.\n// If y == 0, z is set to 0\nfunc (z *Uint) Div(x, y *Uint) *Uint {\n\tif y.IsZero() || y.Gt(x) {\n\t\treturn z.Clear()\n\t}\n\tif x.Eq(y) {\n\t\treturn z.SetOne()\n\t}\n\t// Shortcut some cases\n\tif x.IsUint64() {\n\t\treturn z.SetUint64(x.Uint64() / y.Uint64())\n\t}\n\n\t// At this point, we know\n\t// x/y ; x \u003e y \u003e 0\n\n\tvar quot Uint\n\tudivrem(quot.arr[:], x.arr[:], y)\n\treturn z.Set(\u0026quot)\n}\n\n// MulMod calculates the modulo-m multiplication of x and y and\n// returns z.\n// If m == 0, z is set to 0 (OBS: differs from the big.Int)\nfunc (z *Uint) MulMod(x, y, m *Uint) *Uint {\n\tif x.IsZero() || y.IsZero() || m.IsZero() {\n\t\treturn z.Clear()\n\t}\n\tp := umul(x, y)\n\n\tif m.arr[3] != 0 {\n\t\tmu := Reciprocal(m)\n\t\tr := reduce4(p, m, mu)\n\t\treturn z.Set(\u0026r)\n\t}\n\n\tvar (\n\t\tpl Uint\n\t\tph Uint\n\t)\n\n\tpl = Uint{arr: [4]uint64{p[0], p[1], p[2], p[3]}}\n\tph = Uint{arr: [4]uint64{p[4], p[5], p[6], p[7]}}\n\n\t// If the multiplication is within 256 bits use Mod().\n\tif ph.IsZero() {\n\t\treturn z.Mod(\u0026pl, m)\n\t}\n\n\tvar quot [8]uint64\n\trem := udivrem(quot[:], p[:], m)\n\treturn z.Set(\u0026rem)\n}\n\n// Mod sets z to the modulus x%y for y != 0 and returns z.\n// If y == 0, z is set to 0 (OBS: differs from the big.Uint)\nfunc (z *Uint) Mod(x, y *Uint) *Uint {\n\tif x.IsZero() || y.IsZero() {\n\t\treturn z.Clear()\n\t}\n\tswitch x.Cmp(y) {\n\tcase -1:\n\t\t// x \u003c y\n\t\tcopy(z.arr[:], x.arr[:])\n\t\treturn z\n\tcase 0:\n\t\t// x == y\n\t\treturn z.Clear() // They are equal\n\t}\n\n\t// At this point:\n\t// x != 0\n\t// y != 0\n\t// x \u003e y\n\n\t// Shortcut trivial case\n\tif x.IsUint64() {\n\t\treturn z.SetUint64(x.Uint64() % y.Uint64())\n\t}\n\n\tvar quot Uint\n\t*z = udivrem(quot.arr[:], x.arr[:], y)\n\treturn z\n}\n\n// DivMod sets z to the quotient x div y and m to the modulus x mod y and returns the pair (z, m) for y != 0.\n// If y == 0, both z and m are set to 0 (OBS: differs from the big.Int)\nfunc (z *Uint) DivMod(x, y, m *Uint) (*Uint, *Uint) {\n\tif y.IsZero() {\n\t\treturn z.Clear(), m.Clear()\n\t}\n\tvar quot Uint\n\t*m = udivrem(quot.arr[:], x.arr[:], y)\n\t*z = quot\n\treturn z, m\n}\n\n// Exp sets z = base**exponent mod 2**256, and returns z.\nfunc (z *Uint) Exp(base, exponent *Uint) *Uint {\n\tres := Uint{arr: [4]uint64{1, 0, 0, 0}}\n\tmultiplier := *base\n\texpBitLen := exponent.BitLen()\n\n\tcurBit := 0\n\tword := exponent.arr[0]\n\tfor ; curBit \u003c expBitLen \u0026\u0026 curBit \u003c 64; curBit++ {\n\t\tif word\u00261 == 1 {\n\t\t\tres.Mul(\u0026res, \u0026multiplier)\n\t\t}\n\t\tmultiplier.squared()\n\t\tword \u003e\u003e= 1\n\t}\n\n\tword = exponent.arr[1]\n\tfor ; curBit \u003c expBitLen \u0026\u0026 curBit \u003c 128; curBit++ {\n\t\tif word\u00261 == 1 {\n\t\t\tres.Mul(\u0026res, \u0026multiplier)\n\t\t}\n\t\tmultiplier.squared()\n\t\tword \u003e\u003e= 1\n\t}\n\n\tword = exponent.arr[2]\n\tfor ; curBit \u003c expBitLen \u0026\u0026 curBit \u003c 192; curBit++ {\n\t\tif word\u00261 == 1 {\n\t\t\tres.Mul(\u0026res, \u0026multiplier)\n\t\t}\n\t\tmultiplier.squared()\n\t\tword \u003e\u003e= 1\n\t}\n\n\tword = exponent.arr[3]\n\tfor ; curBit \u003c expBitLen \u0026\u0026 curBit \u003c 256; curBit++ {\n\t\tif word\u00261 == 1 {\n\t\t\tres.Mul(\u0026res, \u0026multiplier)\n\t\t}\n\t\tmultiplier.squared()\n\t\tword \u003e\u003e= 1\n\t}\n\treturn z.Set(\u0026res)\n}\n\nfunc (z *Uint) squared() {\n\tvar (\n\t\tres Uint\n\t\tcarry0, carry1, carry2 uint64\n\t\tres1, res2 uint64\n\t)\n\n\tcarry0, res.arr[0] = bits.Mul64(z.arr[0], z.arr[0])\n\tcarry0, res1 = umulHop(carry0, z.arr[0], z.arr[1])\n\tcarry0, res2 = umulHop(carry0, z.arr[0], z.arr[2])\n\n\tcarry1, res.arr[1] = umulHop(res1, z.arr[0], z.arr[1])\n\tcarry1, res2 = umulStep(res2, z.arr[1], z.arr[1], carry1)\n\n\tcarry2, res.arr[2] = umulHop(res2, z.arr[0], z.arr[2])\n\n\tres.arr[3] = 2*(z.arr[0]*z.arr[3]+z.arr[1]*z.arr[2]) + carry0 + carry1 + carry2\n\n\tz.Set(\u0026res)\n}\n\n// udivrem divides u by d and produces both quotient and remainder.\n// The quotient is stored in provided quot - len(u)-len(d)+1 words.\n// It loosely follows the Knuth's division algorithm (sometimes referenced as \"schoolbook\" division) using 64-bit words.\n// See Knuth, Volume 2, section 4.3.1, Algorithm D.\nfunc udivrem(quot, u []uint64, d *Uint) (rem Uint) {\n\tvar dLen int\n\tfor i := len(d.arr) - 1; i \u003e= 0; i-- {\n\t\tif d.arr[i] != 0 {\n\t\t\tdLen = i + 1\n\t\t\tbreak\n\t\t}\n\t}\n\n\tshift := uint(bits.LeadingZeros64(d.arr[dLen-1]))\n\n\tvar dnStorage Uint\n\tdn := dnStorage.arr[:dLen]\n\tfor i := dLen - 1; i \u003e 0; i-- {\n\t\tdn[i] = (d.arr[i] \u003c\u003c shift) | (d.arr[i-1] \u003e\u003e (64 - shift))\n\t}\n\tdn[0] = d.arr[0] \u003c\u003c shift\n\n\tvar uLen int\n\tfor i := len(u) - 1; i \u003e= 0; i-- {\n\t\tif u[i] != 0 {\n\t\t\tuLen = i + 1\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif uLen \u003c dLen {\n\t\tcopy(rem.arr[:], u)\n\t\treturn rem\n\t}\n\n\tvar unStorage [9]uint64\n\tun := unStorage[:uLen+1]\n\tun[uLen] = u[uLen-1] \u003e\u003e (64 - shift)\n\tfor i := uLen - 1; i \u003e 0; i-- {\n\t\tun[i] = (u[i] \u003c\u003c shift) | (u[i-1] \u003e\u003e (64 - shift))\n\t}\n\tun[0] = u[0] \u003c\u003c shift\n\n\t// TODO: Skip the highest word of numerator if not significant.\n\n\tif dLen == 1 {\n\t\tr := udivremBy1(quot, un, dn[0])\n\t\trem.SetUint64(r \u003e\u003e shift)\n\t\treturn rem\n\t}\n\n\tudivremKnuth(quot, un, dn)\n\n\tfor i := 0; i \u003c dLen-1; i++ {\n\t\trem.arr[i] = (un[i] \u003e\u003e shift) | (un[i+1] \u003c\u003c (64 - shift))\n\t}\n\trem.arr[dLen-1] = un[dLen-1] \u003e\u003e shift\n\n\treturn rem\n}\n\n// umul computes full 256 x 256 -\u003e 512 multiplication.\nfunc umul(x, y *Uint) [8]uint64 {\n\tvar (\n\t\tres [8]uint64\n\t\tcarry, carry4, carry5, carry6 uint64\n\t\tres1, res2, res3, res4, res5 uint64\n\t)\n\n\tcarry, res[0] = bits.Mul64(x.arr[0], y.arr[0])\n\tcarry, res1 = umulHop(carry, x.arr[1], y.arr[0])\n\tcarry, res2 = umulHop(carry, x.arr[2], y.arr[0])\n\tcarry4, res3 = umulHop(carry, x.arr[3], y.arr[0])\n\n\tcarry, res[1] = umulHop(res1, x.arr[0], y.arr[1])\n\tcarry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry)\n\tcarry, res3 = umulStep(res3, x.arr[2], y.arr[1], carry)\n\tcarry5, res4 = umulStep(carry4, x.arr[3], y.arr[1], carry)\n\n\tcarry, res[2] = umulHop(res2, x.arr[0], y.arr[2])\n\tcarry, res3 = umulStep(res3, x.arr[1], y.arr[2], carry)\n\tcarry, res4 = umulStep(res4, x.arr[2], y.arr[2], carry)\n\tcarry6, res5 = umulStep(carry5, x.arr[3], y.arr[2], carry)\n\n\tcarry, res[3] = umulHop(res3, x.arr[0], y.arr[3])\n\tcarry, res[4] = umulStep(res4, x.arr[1], y.arr[3], carry)\n\tcarry, res[5] = umulStep(res5, x.arr[2], y.arr[3], carry)\n\tres[7], res[6] = umulStep(carry6, x.arr[3], y.arr[3], carry)\n\n\treturn res\n}\n\n// umulStep computes (hi * 2^64 + lo) = z + (x * y) + carry.\nfunc umulStep(z, x, y, carry uint64) (hi, lo uint64) {\n\thi, lo = bits.Mul64(x, y)\n\tlo, carry = bits.Add64(lo, carry, 0)\n\thi, _ = bits.Add64(hi, 0, carry)\n\tlo, carry = bits.Add64(lo, z, 0)\n\thi, _ = bits.Add64(hi, 0, carry)\n\treturn hi, lo\n}\n\n// umulHop computes (hi * 2^64 + lo) = z + (x * y)\nfunc umulHop(z, x, y uint64) (hi, lo uint64) {\n\thi, lo = bits.Mul64(x, y)\n\tlo, carry := bits.Add64(lo, z, 0)\n\thi, _ = bits.Add64(hi, 0, carry)\n\treturn hi, lo\n}\n\n// udivremBy1 divides u by single normalized word d and produces both quotient and remainder.\n// The quotient is stored in provided quot.\nfunc udivremBy1(quot, u []uint64, d uint64) (rem uint64) {\n\treciprocal := reciprocal2by1(d)\n\trem = u[len(u)-1] // Set the top word as remainder.\n\tfor j := len(u) - 2; j \u003e= 0; j-- {\n\t\tquot[j], rem = udivrem2by1(rem, u[j], d, reciprocal)\n\t}\n\treturn rem\n}\n\n// udivremKnuth implements the division of u by normalized multiple word d from the Knuth's division algorithm.\n// The quotient is stored in provided quot - len(u)-len(d) words.\n// Updates u to contain the remainder - len(d) words.\nfunc udivremKnuth(quot, u, d []uint64) {\n\tdh := d[len(d)-1]\n\tdl := d[len(d)-2]\n\treciprocal := reciprocal2by1(dh)\n\n\tfor j := len(u) - len(d) - 1; j \u003e= 0; j-- {\n\t\tu2 := u[j+len(d)]\n\t\tu1 := u[j+len(d)-1]\n\t\tu0 := u[j+len(d)-2]\n\n\t\tvar qhat, rhat uint64\n\t\tif u2 \u003e= dh { // Division overflows.\n\t\t\tqhat = ^uint64(0)\n\t\t\t// TODO: Add \"qhat one to big\" adjustment (not needed for correctness, but helps avoiding \"add back\" case).\n\t\t} else {\n\t\t\tqhat, rhat = udivrem2by1(u2, u1, dh, reciprocal)\n\t\t\tph, pl := bits.Mul64(qhat, dl)\n\t\t\tif ph \u003e rhat || (ph == rhat \u0026\u0026 pl \u003e u0) {\n\t\t\t\tqhat--\n\t\t\t\t// TODO: Add \"qhat one to big\" adjustment (not needed for correctness, but helps avoiding \"add back\" case).\n\t\t\t}\n\t\t}\n\n\t\t// Multiply and subtract.\n\t\tborrow := subMulTo(u[j:], d, qhat)\n\t\tu[j+len(d)] = u2 - borrow\n\t\tif u2 \u003c borrow { // Too much subtracted, add back.\n\t\t\tqhat--\n\t\t\tu[j+len(d)] += addTo(u[j:], d)\n\t\t}\n\n\t\tquot[j] = qhat // Store quotient digit.\n\t}\n}\n\n// isBitSet returns true if bit n-th is set, where n = 0 is LSB.\n// The n must be \u003c= 255.\nfunc (z *Uint) isBitSet(n uint) bool {\n\treturn (z.arr[n/64] \u0026 (1 \u003c\u003c (n % 64))) != 0\n}\n\n// addTo computes x += y.\n// Requires len(x) \u003e= len(y).\nfunc addTo(x, y []uint64) uint64 {\n\tvar carry uint64\n\tfor i := 0; i \u003c len(y); i++ {\n\t\tx[i], carry = bits.Add64(x[i], y[i], carry)\n\t}\n\treturn carry\n}\n\n// subMulTo computes x -= y * multiplier.\n// Requires len(x) \u003e= len(y).\nfunc subMulTo(x, y []uint64, multiplier uint64) uint64 {\n\tvar borrow uint64\n\tfor i := 0; i \u003c len(y); i++ {\n\t\ts, carry1 := bits.Sub64(x[i], borrow, 0)\n\t\tph, pl := bits.Mul64(y[i], multiplier)\n\t\tt, carry2 := bits.Sub64(s, pl, 0)\n\t\tx[i] = t\n\t\tborrow = ph + carry1 + carry2\n\t}\n\treturn borrow\n}\n\n// reciprocal2by1 computes \u003c^d, ^0\u003e / d.\nfunc reciprocal2by1(d uint64) uint64 {\n\treciprocal, _ := bits.Div64(^d, ^uint64(0), d)\n\treturn reciprocal\n}\n\n// udivrem2by1 divides \u003cuh, ul\u003e / d and produces both quotient and remainder.\n// It uses the provided d's reciprocal.\n// Implementation ported from https://github.com/chfast/intx and is based on\n// \"Improved division by invariant integers\", Algorithm 4.\nfunc udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) {\n\tqh, ql := bits.Mul64(reciprocal, uh)\n\tql, carry := bits.Add64(ql, ul, 0)\n\tqh, _ = bits.Add64(qh, uh, carry)\n\tqh++\n\n\tr := ul - qh*d\n\n\tif r \u003e ql {\n\t\tqh--\n\t\tr += d\n\t}\n\n\tif r \u003e= d {\n\t\tqh++\n\t\tr -= d\n\t}\n\n\treturn qh, r\n}\n" + }, + { + "name": "arithmetic_test.gno", + "body": "package uint256\n\nimport \"testing\"\n\ntype binOp2Test struct {\n\tx, y, want string\n}\n\nfunc TestAdd(t *testing.T) {\n\ttests := []binOp2Test{\n\t\t{\"0\", \"1\", \"1\"},\n\t\t{\"1\", \"0\", \"1\"},\n\t\t{\"1\", \"1\", \"2\"},\n\t\t{\"1\", \"3\", \"4\"},\n\t\t{\"10\", \"10\", \"20\"},\n\t\t{\"18446744073709551615\", \"18446744073709551615\", \"36893488147419103230\"}, // uint64 overflow\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Add(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Add(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestSub(t *testing.T) {\n\ttests := []binOp2Test{\n\t\t{\"1\", \"0\", \"1\"},\n\t\t{\"1\", \"1\", \"0\"},\n\t\t{\"10\", \"10\", \"0\"},\n\t\t{\"31337\", \"1337\", \"30000\"},\n\t\t{\"2\", \"3\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\"}, // underflow\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Sub(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Sub(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestMul(t *testing.T) {\n\ttests := []binOp2Test{\n\t\t{\"1\", \"0\", \"0\"},\n\t\t{\"1\", \"1\", \"1\"},\n\t\t{\"10\", \"10\", \"100\"},\n\t\t{\"18446744073709551615\", \"2\", \"36893488147419103230\"}, // uint64 overflow\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Mul(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Mul(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestDiv(t *testing.T) {\n\ttests := []binOp2Test{\n\t\t{\"31337\", \"3\", \"10445\"},\n\t\t{\"31337\", \"0\", \"0\"},\n\t\t{\"0\", \"31337\", \"0\"},\n\t\t{\"1\", \"1\", \"1\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Div(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Div(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestMod(t *testing.T) {\n\ttests := []binOp2Test{\n\t\t{\"31337\", \"3\", \"2\"},\n\t\t{\"31337\", \"0\", \"0\"},\n\t\t{\"0\", \"31337\", \"0\"},\n\t\t{\"2\", \"31337\", \"2\"},\n\t\t{\"1\", \"1\", \"0\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Mod(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Mod(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestDivMod(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\ty string\n\t\twantDiv string\n\t\twantMod string\n\t}{\n\t\t{\"1\", \"1\", \"1\", \"0\"},\n\t\t{\"10\", \"10\", \"1\", \"0\"},\n\t\t{\"100\", \"10\", \"10\", \"0\"},\n\t\t{\"31337\", \"3\", \"10445\", \"2\"},\n\t\t{\"31337\", \"0\", \"0\", \"0\"},\n\t\t{\"0\", \"31337\", \"0\", \"0\"},\n\t\t{\"2\", \"31337\", \"0\", \"2\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twantDiv, err := FromDecimal(tc.wantDiv)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twantMod, err := FromDecimal(tc.wantMod)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgotDiv := new(Uint)\n\t\tgotMod := new(Uint)\n\t\tgotDiv.DivMod(x, y, gotMod)\n\n\t\tfor i := range gotDiv.arr {\n\t\t\tif gotDiv.arr[i] != wantDiv.arr[i] {\n\t\t\t\tt.Errorf(\"DivMod(%s, %s) got Div %v, want Div %v\", tc.x, tc.y, gotDiv, wantDiv)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tfor i := range gotMod.arr {\n\t\t\tif gotMod.arr[i] != wantMod.arr[i] {\n\t\t\t\tt.Errorf(\"DivMod(%s, %s) got Mod %v, want Mod %v\", tc.x, tc.y, gotMod, wantMod)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestNeg(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant string\n\t}{\n\t\t{\"31337\", \"115792089237316195423570985008687907853269984665640564039457584007913129608599\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129608599\", \"31337\"},\n\t\t{\"0\", \"0\"},\n\t\t{\"2\", \"115792089237316195423570985008687907853269984665640564039457584007913129639934\"},\n\t\t{\"1\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Neg(x)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Neg(%s) = %v, want %v\", tc.x, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestExp(t *testing.T) {\n\ttests := []binOp2Test{\n\t\t{\"31337\", \"3\", \"30773171189753\"},\n\t\t{\"31337\", \"0\", \"1\"},\n\t\t{\"0\", \"31337\", \"0\"},\n\t\t{\"1\", \"1\", \"1\"},\n\t\t{\"2\", \"3\", \"8\"},\n\t\t{\"2\", \"64\", \"18446744073709551616\"},\n\t\t{\"2\", \"128\", \"340282366920938463463374607431768211456\"},\n\t\t{\"2\", \"255\", \"57896044618658097711785492504343953926634992332820282019728792003956564819968\"},\n\t\t{\"2\", \"256\", \"0\"}, // overflow\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Exp(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Exp(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n" + }, + { + "name": "bits_table.gno", + "body": "// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Code generated by go run make_tables.go. DO NOT EDIT.\n\npackage uint256\n\nconst ntz8tab = \"\" +\n\t\"\\x08\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x04\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x05\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x04\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x06\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x04\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x05\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x04\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x07\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x04\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x05\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x04\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x06\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x04\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x05\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\" +\n\t\"\\x04\\x00\\x01\\x00\\x02\\x00\\x01\\x00\\x03\\x00\\x01\\x00\\x02\\x00\\x01\\x00\"\n\nconst pop8tab = \"\" +\n\t\"\\x00\\x01\\x01\\x02\\x01\\x02\\x02\\x03\\x01\\x02\\x02\\x03\\x02\\x03\\x03\\x04\" +\n\t\"\\x01\\x02\\x02\\x03\\x02\\x03\\x03\\x04\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\" +\n\t\"\\x01\\x02\\x02\\x03\\x02\\x03\\x03\\x04\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\" +\n\t\"\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\" +\n\t\"\\x01\\x02\\x02\\x03\\x02\\x03\\x03\\x04\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\" +\n\t\"\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\" +\n\t\"\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\" +\n\t\"\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\\x04\\x05\\x05\\x06\\x05\\x06\\x06\\x07\" +\n\t\"\\x01\\x02\\x02\\x03\\x02\\x03\\x03\\x04\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\" +\n\t\"\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\" +\n\t\"\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\" +\n\t\"\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\\x04\\x05\\x05\\x06\\x05\\x06\\x06\\x07\" +\n\t\"\\x02\\x03\\x03\\x04\\x03\\x04\\x04\\x05\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\" +\n\t\"\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\\x04\\x05\\x05\\x06\\x05\\x06\\x06\\x07\" +\n\t\"\\x03\\x04\\x04\\x05\\x04\\x05\\x05\\x06\\x04\\x05\\x05\\x06\\x05\\x06\\x06\\x07\" +\n\t\"\\x04\\x05\\x05\\x06\\x05\\x06\\x06\\x07\\x05\\x06\\x06\\x07\\x06\\x07\\x07\\x08\"\n\nconst rev8tab = \"\" +\n\t\"\\x00\\x80\\x40\\xc0\\x20\\xa0\\x60\\xe0\\x10\\x90\\x50\\xd0\\x30\\xb0\\x70\\xf0\" +\n\t\"\\x08\\x88\\x48\\xc8\\x28\\xa8\\x68\\xe8\\x18\\x98\\x58\\xd8\\x38\\xb8\\x78\\xf8\" +\n\t\"\\x04\\x84\\x44\\xc4\\x24\\xa4\\x64\\xe4\\x14\\x94\\x54\\xd4\\x34\\xb4\\x74\\xf4\" +\n\t\"\\x0c\\x8c\\x4c\\xcc\\x2c\\xac\\x6c\\xec\\x1c\\x9c\\x5c\\xdc\\x3c\\xbc\\x7c\\xfc\" +\n\t\"\\x02\\x82\\x42\\xc2\\x22\\xa2\\x62\\xe2\\x12\\x92\\x52\\xd2\\x32\\xb2\\x72\\xf2\" +\n\t\"\\x0a\\x8a\\x4a\\xca\\x2a\\xaa\\x6a\\xea\\x1a\\x9a\\x5a\\xda\\x3a\\xba\\x7a\\xfa\" +\n\t\"\\x06\\x86\\x46\\xc6\\x26\\xa6\\x66\\xe6\\x16\\x96\\x56\\xd6\\x36\\xb6\\x76\\xf6\" +\n\t\"\\x0e\\x8e\\x4e\\xce\\x2e\\xae\\x6e\\xee\\x1e\\x9e\\x5e\\xde\\x3e\\xbe\\x7e\\xfe\" +\n\t\"\\x01\\x81\\x41\\xc1\\x21\\xa1\\x61\\xe1\\x11\\x91\\x51\\xd1\\x31\\xb1\\x71\\xf1\" +\n\t\"\\x09\\x89\\x49\\xc9\\x29\\xa9\\x69\\xe9\\x19\\x99\\x59\\xd9\\x39\\xb9\\x79\\xf9\" +\n\t\"\\x05\\x85\\x45\\xc5\\x25\\xa5\\x65\\xe5\\x15\\x95\\x55\\xd5\\x35\\xb5\\x75\\xf5\" +\n\t\"\\x0d\\x8d\\x4d\\xcd\\x2d\\xad\\x6d\\xed\\x1d\\x9d\\x5d\\xdd\\x3d\\xbd\\x7d\\xfd\" +\n\t\"\\x03\\x83\\x43\\xc3\\x23\\xa3\\x63\\xe3\\x13\\x93\\x53\\xd3\\x33\\xb3\\x73\\xf3\" +\n\t\"\\x0b\\x8b\\x4b\\xcb\\x2b\\xab\\x6b\\xeb\\x1b\\x9b\\x5b\\xdb\\x3b\\xbb\\x7b\\xfb\" +\n\t\"\\x07\\x87\\x47\\xc7\\x27\\xa7\\x67\\xe7\\x17\\x97\\x57\\xd7\\x37\\xb7\\x77\\xf7\" +\n\t\"\\x0f\\x8f\\x4f\\xcf\\x2f\\xaf\\x6f\\xef\\x1f\\x9f\\x5f\\xdf\\x3f\\xbf\\x7f\\xff\"\n\nconst len8tab = \"\" +\n\t\"\\x00\\x01\\x02\\x02\\x03\\x03\\x03\\x03\\x04\\x04\\x04\\x04\\x04\\x04\\x04\\x04\" +\n\t\"\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\\x05\" +\n\t\"\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\" +\n\t\"\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\\x06\" +\n\t\"\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\" +\n\t\"\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\" +\n\t\"\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\" +\n\t\"\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\\x07\" +\n\t\"\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\" +\n\t\"\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\" +\n\t\"\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\" +\n\t\"\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\" +\n\t\"\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\" +\n\t\"\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\" +\n\t\"\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\" +\n\t\"\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\"\n" + }, + { + "name": "bitwise.gno", + "body": "// bitwise contains bitwise operations for Uint instances.\n// This file includes functions to perform bitwise AND, OR, XOR, and NOT operations, as well as bit shifting.\n// These operations are crucial for manipulating individual bits within a 256-bit unsigned integer.\npackage uint256\n\n// Or sets z = x | y and returns z.\nfunc (z *Uint) Or(x, y *Uint) *Uint {\n\tz.arr[0] = x.arr[0] | y.arr[0]\n\tz.arr[1] = x.arr[1] | y.arr[1]\n\tz.arr[2] = x.arr[2] | y.arr[2]\n\tz.arr[3] = x.arr[3] | y.arr[3]\n\treturn z\n}\n\n// And sets z = x \u0026 y and returns z.\nfunc (z *Uint) And(x, y *Uint) *Uint {\n\tz.arr[0] = x.arr[0] \u0026 y.arr[0]\n\tz.arr[1] = x.arr[1] \u0026 y.arr[1]\n\tz.arr[2] = x.arr[2] \u0026 y.arr[2]\n\tz.arr[3] = x.arr[3] \u0026 y.arr[3]\n\treturn z\n}\n\n// Not sets z = ^x and returns z.\nfunc (z *Uint) Not(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = ^x.arr[3], ^x.arr[2], ^x.arr[1], ^x.arr[0]\n\treturn z\n}\n\n// AndNot sets z = x \u0026^ y and returns z.\nfunc (z *Uint) AndNot(x, y *Uint) *Uint {\n\tz.arr[0] = x.arr[0] \u0026^ y.arr[0]\n\tz.arr[1] = x.arr[1] \u0026^ y.arr[1]\n\tz.arr[2] = x.arr[2] \u0026^ y.arr[2]\n\tz.arr[3] = x.arr[3] \u0026^ y.arr[3]\n\treturn z\n}\n\n// Xor sets z = x ^ y and returns z.\nfunc (z *Uint) Xor(x, y *Uint) *Uint {\n\tz.arr[0] = x.arr[0] ^ y.arr[0]\n\tz.arr[1] = x.arr[1] ^ y.arr[1]\n\tz.arr[2] = x.arr[2] ^ y.arr[2]\n\tz.arr[3] = x.arr[3] ^ y.arr[3]\n\treturn z\n}\n\n// Lsh sets z = x \u003c\u003c n and returns z.\nfunc (z *Uint) Lsh(x *Uint, n uint) *Uint {\n\t// n % 64 == 0\n\tif n\u00260x3f == 0 {\n\t\tswitch n {\n\t\tcase 0:\n\t\t\treturn z.Set(x)\n\t\tcase 64:\n\t\t\treturn z.lsh64(x)\n\t\tcase 128:\n\t\t\treturn z.lsh128(x)\n\t\tcase 192:\n\t\t\treturn z.lsh192(x)\n\t\tdefault:\n\t\t\treturn z.Clear()\n\t\t}\n\t}\n\tvar a, b uint64\n\t// Big swaps first\n\tswitch {\n\tcase n \u003e 192:\n\t\tif n \u003e 256 {\n\t\t\treturn z.Clear()\n\t\t}\n\t\tz.lsh192(x)\n\t\tn -= 192\n\t\tgoto sh192\n\tcase n \u003e 128:\n\t\tz.lsh128(x)\n\t\tn -= 128\n\t\tgoto sh128\n\tcase n \u003e 64:\n\t\tz.lsh64(x)\n\t\tn -= 64\n\t\tgoto sh64\n\tdefault:\n\t\tz.Set(x)\n\t}\n\n\t// remaining shifts\n\ta = z.arr[0] \u003e\u003e (64 - n)\n\tz.arr[0] = z.arr[0] \u003c\u003c n\n\nsh64:\n\tb = z.arr[1] \u003e\u003e (64 - n)\n\tz.arr[1] = (z.arr[1] \u003c\u003c n) | a\n\nsh128:\n\ta = z.arr[2] \u003e\u003e (64 - n)\n\tz.arr[2] = (z.arr[2] \u003c\u003c n) | b\n\nsh192:\n\tz.arr[3] = (z.arr[3] \u003c\u003c n) | a\n\n\treturn z\n}\n\n// Rsh sets z = x \u003e\u003e n and returns z.\nfunc (z *Uint) Rsh(x *Uint, n uint) *Uint {\n\t// n % 64 == 0\n\tif n\u00260x3f == 0 {\n\t\tswitch n {\n\t\tcase 0:\n\t\t\treturn z.Set(x)\n\t\tcase 64:\n\t\t\treturn z.rsh64(x)\n\t\tcase 128:\n\t\t\treturn z.rsh128(x)\n\t\tcase 192:\n\t\t\treturn z.rsh192(x)\n\t\tdefault:\n\t\t\treturn z.Clear()\n\t\t}\n\t}\n\tvar a, b uint64\n\t// Big swaps first\n\tswitch {\n\tcase n \u003e 192:\n\t\tif n \u003e 256 {\n\t\t\treturn z.Clear()\n\t\t}\n\t\tz.rsh192(x)\n\t\tn -= 192\n\t\tgoto sh192\n\tcase n \u003e 128:\n\t\tz.rsh128(x)\n\t\tn -= 128\n\t\tgoto sh128\n\tcase n \u003e 64:\n\t\tz.rsh64(x)\n\t\tn -= 64\n\t\tgoto sh64\n\tdefault:\n\t\tz.Set(x)\n\t}\n\n\t// remaining shifts\n\ta = z.arr[3] \u003c\u003c (64 - n)\n\tz.arr[3] = z.arr[3] \u003e\u003e n\n\nsh64:\n\tb = z.arr[2] \u003c\u003c (64 - n)\n\tz.arr[2] = (z.arr[2] \u003e\u003e n) | a\n\nsh128:\n\ta = z.arr[1] \u003c\u003c (64 - n)\n\tz.arr[1] = (z.arr[1] \u003e\u003e n) | b\n\nsh192:\n\tz.arr[0] = (z.arr[0] \u003e\u003e n) | a\n\n\treturn z\n}\n\n// SRsh (Signed/Arithmetic right shift)\n// considers z to be a signed integer, during right-shift\n// and sets z = x \u003e\u003e n and returns z.\nfunc (z *Uint) SRsh(x *Uint, n uint) *Uint {\n\t// If the MSB is 0, SRsh is same as Rsh.\n\tif !x.isBitSet(255) {\n\t\treturn z.Rsh(x, n)\n\t}\n\tif n%64 == 0 {\n\t\tswitch n {\n\t\tcase 0:\n\t\t\treturn z.Set(x)\n\t\tcase 64:\n\t\t\treturn z.srsh64(x)\n\t\tcase 128:\n\t\t\treturn z.srsh128(x)\n\t\tcase 192:\n\t\t\treturn z.srsh192(x)\n\t\tdefault:\n\t\t\treturn z.SetAllOne()\n\t\t}\n\t}\n\tvar a uint64 = MaxUint64 \u003c\u003c (64 - n%64)\n\t// Big swaps first\n\tswitch {\n\tcase n \u003e 192:\n\t\tif n \u003e 256 {\n\t\t\treturn z.SetAllOne()\n\t\t}\n\t\tz.srsh192(x)\n\t\tn -= 192\n\t\tgoto sh192\n\tcase n \u003e 128:\n\t\tz.srsh128(x)\n\t\tn -= 128\n\t\tgoto sh128\n\tcase n \u003e 64:\n\t\tz.srsh64(x)\n\t\tn -= 64\n\t\tgoto sh64\n\tdefault:\n\t\tz.Set(x)\n\t}\n\n\t// remaining shifts\n\tz.arr[3], a = (z.arr[3]\u003e\u003en)|a, z.arr[3]\u003c\u003c(64-n)\n\nsh64:\n\tz.arr[2], a = (z.arr[2]\u003e\u003en)|a, z.arr[2]\u003c\u003c(64-n)\n\nsh128:\n\tz.arr[1], a = (z.arr[1]\u003e\u003en)|a, z.arr[1]\u003c\u003c(64-n)\n\nsh192:\n\tz.arr[0] = (z.arr[0] \u003e\u003e n) | a\n\n\treturn z\n}\n\nfunc (z *Uint) lsh64(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[2], x.arr[1], x.arr[0], 0\n\treturn z\n}\n\nfunc (z *Uint) lsh128(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[1], x.arr[0], 0, 0\n\treturn z\n}\n\nfunc (z *Uint) lsh192(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[0], 0, 0, 0\n\treturn z\n}\n\nfunc (z *Uint) rsh64(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, x.arr[3], x.arr[2], x.arr[1]\n\treturn z\n}\n\nfunc (z *Uint) rsh128(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, x.arr[3], x.arr[2]\n\treturn z\n}\n\nfunc (z *Uint) rsh192(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x.arr[3]\n\treturn z\n}\n\nfunc (z *Uint) srsh64(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, x.arr[3], x.arr[2], x.arr[1]\n\treturn z\n}\n\nfunc (z *Uint) srsh128(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, x.arr[3], x.arr[2]\n\treturn z\n}\n\nfunc (z *Uint) srsh192(x *Uint) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, x.arr[3]\n\treturn z\n}\n" + }, + { + "name": "bitwise_test.gno", + "body": "package uint256\n\nimport \"testing\"\n\ntype logicOpTest struct {\n\tname string\n\tx Uint\n\ty Uint\n\twant Uint\n}\n\nfunc TestOr(t *testing.T) {\n\ttests := []logicOpTest{\n\t\t{\n\t\t\tname: \"all zeros\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tres := new(Uint).Or(\u0026tc.x, \u0026tc.y)\n\t\t\tif *res != tc.want {\n\t\t\t\tt.Errorf(\"Or(%s, %s) = %s, want %s\", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAnd(t *testing.T) {\n\ttests := []logicOpTest{\n\t\t{\n\t\t\tname: \"all zeros\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed 2\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed 3\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand zero\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}},\n\t\t\twant: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tres := new(Uint).And(\u0026tc.x, \u0026tc.y)\n\t\t\tif *res != tc.want {\n\t\t\t\tt.Errorf(\"And(%s, %s) = %s, want %s\", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNot(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tx Uint\n\t\twant Uint\n\t}{\n\t\t{\n\t\t\tname: \"all zeros\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t\t{\n\t\t\tname: \"all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tres := new(Uint).Not(\u0026tc.x)\n\t\t\tif *res != tc.want {\n\t\t\t\tt.Errorf(\"Not(%s) = %s, want %s\", tc.x.ToString(), res.ToString(), (tc.want).ToString())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAndNot(t *testing.T) {\n\ttests := []logicOpTest{\n\t\t{\n\t\t\tname: \"all zeros\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed 2\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed 3\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand zero\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}},\n\t\t\twant: Uint{arr: [4]uint64{0xAAAAAAAAAAAAAAAA, 0x5555555555555555, 0x0000000000000000, ^uint64(0)}},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tres := new(Uint).AndNot(\u0026tc.x, \u0026tc.y)\n\t\t\tif *res != tc.want {\n\t\t\t\tt.Errorf(\"AndNot(%s, %s) = %s, want %s\", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestXor(t *testing.T) {\n\ttests := []logicOpTest{\n\t\t{\n\t\t\tname: \"all zeros\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed 2\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed 3\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}},\n\t\t\twant: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand zero\",\n\t\t\tx: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\ty: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}},\n\t\t\twant: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand all ones\",\n\t\t\tx: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\ty: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}},\n\t\t\twant: Uint{arr: [4]uint64{0xAAAAAAAAAAAAAAAA, 0x5555555555555555, 0x0000000000000000, ^uint64(0)}},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tres := new(Uint).Xor(\u0026tc.x, \u0026tc.y)\n\t\t\tif *res != tc.want {\n\t\t\t\tt.Errorf(\"Xor(%s, %s) = %s, want %s\", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLsh(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\ty uint\n\t\twant string\n\t}{\n\t\t{\"0\", 0, \"0\"},\n\t\t{\"0\", 1, \"0\"},\n\t\t{\"0\", 64, \"0\"},\n\t\t{\"1\", 0, \"1\"},\n\t\t{\"1\", 1, \"2\"},\n\t\t{\"1\", 64, \"18446744073709551616\"},\n\t\t{\"1\", 128, \"340282366920938463463374607431768211456\"},\n\t\t{\"1\", 192, \"6277101735386680763835789423207666416102355444464034512896\"},\n\t\t{\"1\", 255, \"57896044618658097711785492504343953926634992332820282019728792003956564819968\"},\n\t\t{\"1\", 256, \"0\"},\n\t\t{\"31337\", 0, \"31337\"},\n\t\t{\"31337\", 1, \"62674\"},\n\t\t{\"31337\", 64, \"578065619037836218990592\"},\n\t\t{\"31337\", 128, \"10663428532201448629551770073089320442396672\"},\n\t\t{\"31337\", 192, \"196705537081812415096322133155058642481399512563169449530621952\"},\n\t\t{\"31337\", 193, \"393411074163624830192644266310117284962799025126338899061243904\"},\n\t\t{\"31337\", 255, \"57896044618658097711785492504343953926634992332820282019728792003956564819968\"},\n\t\t{\"31337\", 256, \"0\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Lsh(x, tc.y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Lsh(%s, %d) = %s, want %s\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestRsh(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\ty uint\n\t\twant string\n\t}{\n\t\t{\"0\", 0, \"0\"},\n\t\t{\"0\", 1, \"0\"},\n\t\t{\"0\", 64, \"0\"},\n\t\t{\"1\", 0, \"1\"},\n\t\t{\"1\", 1, \"0\"},\n\t\t{\"1\", 64, \"0\"},\n\t\t{\"1\", 128, \"0\"},\n\t\t{\"1\", 192, \"0\"},\n\t\t{\"1\", 255, \"0\"},\n\t\t{\"57896044618658097711785492504343953926634992332820282019728792003956564819968\", 255, \"1\"},\n\t\t{\"6277101735386680763835789423207666416102355444464034512896\", 192, \"1\"},\n\t\t{\"340282366920938463463374607431768211456\", 128, \"1\"},\n\t\t{\"18446744073709551616\", 64, \"1\"},\n\t\t{\"393411074163624830192644266310117284962799025126338899061243904\", 193, \"31337\"},\n\t\t{\"196705537081812415096322133155058642481399512563169449530621952\", 192, \"31337\"},\n\t\t{\"10663428532201448629551770073089320442396672\", 128, \"31337\"},\n\t\t{\"578065619037836218990592\", 64, \"31337\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := \u0026Uint{}\n\t\tgot.Rsh(x, tc.y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Rsh(%s, %d) = %s, want %s\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n" + }, + { + "name": "cmp.gno", + "body": "// cmp (or, comparisons) includes methods for comparing Uint instances.\n// These comparison functions cover a range of operations including equality checks, less than/greater than\n// evaluations, and specialized comparisons such as signed greater than. These are fundamental for logical\n// decision making based on Uint values.\npackage uint256\n\nimport (\n\t\"math/bits\"\n)\n\n// Cmp compares z and x and returns:\n//\n//\t-1 if z \u003c x\n//\t 0 if z == x\n//\t+1 if z \u003e x\nfunc (z *Uint) Cmp(x *Uint) (r int) {\n\t// z \u003c x \u003c=\u003e z - x \u003c 0 i.e. when subtraction overflows.\n\td0, carry := bits.Sub64(z.arr[0], x.arr[0], 0)\n\td1, carry := bits.Sub64(z.arr[1], x.arr[1], carry)\n\td2, carry := bits.Sub64(z.arr[2], x.arr[2], carry)\n\td3, carry := bits.Sub64(z.arr[3], x.arr[3], carry)\n\tif carry == 1 {\n\t\treturn -1\n\t}\n\tif d0|d1|d2|d3 == 0 {\n\t\treturn 0\n\t}\n\treturn 1\n}\n\n// IsZero returns true if z == 0\nfunc (z *Uint) IsZero() bool {\n\treturn (z.arr[0] | z.arr[1] | z.arr[2] | z.arr[3]) == 0\n}\n\n// Sign returns:\n//\n//\t-1 if z \u003c 0\n//\t 0 if z == 0\n//\t+1 if z \u003e 0\n//\n// Where z is interpreted as a two's complement signed number\nfunc (z *Uint) Sign() int {\n\tif z.IsZero() {\n\t\treturn 0\n\t}\n\tif z.arr[3] \u003c 0x8000000000000000 {\n\t\treturn 1\n\t}\n\treturn -1\n}\n\n// LtUint64 returns true if z is smaller than n\nfunc (z *Uint) LtUint64(n uint64) bool {\n\treturn z.arr[0] \u003c n \u0026\u0026 (z.arr[1]|z.arr[2]|z.arr[3]) == 0\n}\n\n// GtUint64 returns true if z is larger than n\nfunc (z *Uint) GtUint64(n uint64) bool {\n\treturn z.arr[0] \u003e n || (z.arr[1]|z.arr[2]|z.arr[3]) != 0\n}\n\n// Lt returns true if z \u003c x\nfunc (z *Uint) Lt(x *Uint) bool {\n\t// z \u003c x \u003c=\u003e z - x \u003c 0 i.e. when subtraction overflows.\n\t_, carry := bits.Sub64(z.arr[0], x.arr[0], 0)\n\t_, carry = bits.Sub64(z.arr[1], x.arr[1], carry)\n\t_, carry = bits.Sub64(z.arr[2], x.arr[2], carry)\n\t_, carry = bits.Sub64(z.arr[3], x.arr[3], carry)\n\n\treturn carry != 0\n}\n\n// Gt returns true if z \u003e x\nfunc (z *Uint) Gt(x *Uint) bool {\n\treturn x.Lt(z)\n}\n\n// Lte returns true if z \u003c= x\nfunc (z *Uint) Lte(x *Uint) bool {\n\tcond1 := z.Lt(x)\n\tcond2 := z.Eq(x)\n\n\tif cond1 || cond2 {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Gte returns true if z \u003e= x\nfunc (z *Uint) Gte(x *Uint) bool {\n\tcond1 := z.Gt(x)\n\tcond2 := z.Eq(x)\n\n\tif cond1 || cond2 {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Eq returns true if z == x\nfunc (z *Uint) Eq(x *Uint) bool {\n\treturn (z.arr[0] == x.arr[0]) \u0026\u0026 (z.arr[1] == x.arr[1]) \u0026\u0026 (z.arr[2] == x.arr[2]) \u0026\u0026 (z.arr[3] == x.arr[3])\n}\n\n// Neq returns true if z != x\nfunc (z *Uint) Neq(x *Uint) bool {\n\treturn !z.Eq(x)\n}\n\n// Sgt interprets z and x as signed integers, and returns\n// true if z \u003e x\nfunc (z *Uint) Sgt(x *Uint) bool {\n\tzSign := z.Sign()\n\txSign := x.Sign()\n\n\tswitch {\n\tcase zSign \u003e= 0 \u0026\u0026 xSign \u003c 0:\n\t\treturn true\n\tcase zSign \u003c 0 \u0026\u0026 xSign \u003e= 0:\n\t\treturn false\n\tdefault:\n\t\treturn z.Gt(x)\n\t}\n}\n" + }, + { + "name": "cmp_test.gno", + "body": "package uint256\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestCmp(t *testing.T) {\n\ttests := []struct {\n\t\tx, y string\n\t\twant int\n\t}{\n\t\t{\"0\", \"0\", 0},\n\t\t{\"0\", \"1\", -1},\n\t\t{\"1\", \"0\", 1},\n\t\t{\"1\", \"1\", 0},\n\t\t{\"10\", \"10\", 0},\n\t\t{\"10\", \"11\", -1},\n\t\t{\"11\", \"10\", 1},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.Cmp(y)\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Cmp(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestIsZero(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant bool\n\t}{\n\t\t{\"0\", true},\n\t\t{\"1\", false},\n\t\t{\"10\", false},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.IsZero()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"IsZero(%s) = %v, want %v\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestLtUint64(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\ty uint64\n\t\twant bool\n\t}{\n\t\t{\"0\", 1, true},\n\t\t{\"1\", 0, false},\n\t\t{\"10\", 10, false},\n\t\t{\"0xffffffffffffffff\", 0, false},\n\t\t{\"0x10000000000000000\", 10000000000000000, false},\n\t}\n\n\tfor _, tc := range tests {\n\t\tvar x *Uint\n\t\tvar err error\n\n\t\tif strings.HasPrefix(tc.x, \"0x\") {\n\t\t\tx, err = FromHex(tc.x)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else {\n\t\t\tx, err = FromDecimal(tc.x)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tgot := x.LtUint64(tc.y)\n\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"LtUint64(%s, %d) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestSGT(t *testing.T) {\n\tx := MustFromHex(\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\")\n\ty := MustFromHex(\"0x0\")\n\tactual := x.Sgt(y)\n\tif actual {\n\t\tt.Fatalf(\"Expected %v false\", actual)\n\t}\n\n\tx = MustFromHex(\"0x0\")\n\ty = MustFromHex(\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\")\n\tactual = x.Sgt(y)\n\tif !actual {\n\t\tt.Fatalf(\"Expected %v true\", actual)\n\t}\n}\n\nfunc TestEq(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\ty string\n\t\twant bool\n\t}{\n\t\t{\"0xffffffffffffffff\", \"18446744073709551615\", true},\n\t\t{\"0x10000000000000000\", \"18446744073709551616\", true},\n\t\t{\"0\", \"0\", true},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\", true},\n\t}\n\n\tfor i, tc := range tests {\n\t\tvar x *Uint\n\t\tvar err error\n\n\t\tif strings.HasPrefix(tc.x, \"0x\") {\n\t\t\tx, err = FromHex(tc.x)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else {\n\t\t\tx, err = FromDecimal(tc.x)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.Eq(y)\n\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Eq(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n" + }, + { + "name": "conversion.gno", + "body": "// conversions contains methods for converting Uint instances to other types and vice versa.\n// This includes conversions to and from basic types such as uint64 and int32, as well as string representations\n// and byte slices. Additionally, it covers marshaling and unmarshaling for JSON and other text formats.\npackage uint256\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Uint64 returns the lower 64-bits of z\nfunc (z *Uint) Uint64() uint64 {\n\treturn z.arr[0]\n}\n\n// Uint64WithOverflow returns the lower 64-bits of z and bool whether overflow occurred\nfunc (z *Uint) Uint64WithOverflow() (uint64, bool) {\n\treturn z.arr[0], (z.arr[1] | z.arr[2] | z.arr[3]) != 0\n}\n\n// SetUint64 sets z to the value x\nfunc (z *Uint) SetUint64(x uint64) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x\n\treturn z\n}\n\n// IsUint64 reports whether z can be represented as a uint64.\nfunc (z *Uint) IsUint64() bool {\n\treturn (z.arr[1] | z.arr[2] | z.arr[3]) == 0\n}\n\n// Dec returns the decimal representation of z.\nfunc (z *Uint) Dec() string {\n\tif z.IsZero() {\n\t\treturn \"0\"\n\t}\n\tif z.IsUint64() {\n\t\treturn strconv.FormatUint(z.Uint64(), 10)\n\t}\n\n\t// The max uint64 value being 18446744073709551615, the largest\n\t// power-of-ten below that is 10000000000000000000.\n\t// When we do a DivMod using that number, the remainder that we\n\t// get back is the lower part of the output.\n\t//\n\t// The ascii-output of remainder will never exceed 19 bytes (since it will be\n\t// below 10000000000000000000).\n\t//\n\t// Algorithm example using 100 as divisor\n\t//\n\t// 12345 % 100 = 45 (rem)\n\t// 12345 / 100 = 123 (quo)\n\t// -\u003e output '45', continue iterate on 123\n\tvar (\n\t\t// out is 98 bytes long: 78 (max size of a string without leading zeroes,\n\t\t// plus slack so we can copy 19 bytes every iteration).\n\t\t// We init it with zeroes, because when strconv appends the ascii representations,\n\t\t// it will omit leading zeroes.\n\t\tout = []byte(\"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\")\n\t\tdivisor = NewUint(10000000000000000000) // 20 digits\n\t\ty = new(Uint).Set(z) // copy to avoid modifying z\n\t\tpos = len(out) // position to write to\n\t\tbuf = make([]byte, 0, 19) // buffer to write uint64:s to\n\t)\n\tfor {\n\t\t// Obtain Q and R for divisor\n\t\tvar quot Uint\n\t\trem := udivrem(quot.arr[:], y.arr[:], divisor)\n\t\ty.Set(\u0026quot) // Set Q for next loop\n\t\t// Convert the R to ascii representation\n\t\tbuf = strconv.AppendUint(buf[:0], rem.Uint64(), 10)\n\t\t// Copy in the ascii digits\n\t\tcopy(out[pos-len(buf):], buf)\n\t\tif y.IsZero() {\n\t\t\tbreak\n\t\t}\n\t\t// Move 19 digits left\n\t\tpos -= 19\n\t}\n\t// skip leading zeroes by only using the 'used size' of buf\n\treturn string(out[pos-len(buf):])\n}\n\nfunc (z *Uint) Scan(src interface{}) error {\n\tif src == nil {\n\t\tz.Clear()\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn z.scanScientificFromString(src)\n\tcase []byte:\n\t\treturn z.scanScientificFromString(string(src))\n\t}\n\treturn errors.New(\"default // unsupported type: can't convert to uint256.Uint\")\n}\n\nfunc (z *Uint) scanScientificFromString(src string) error {\n\tif len(src) == 0 {\n\t\tz.Clear()\n\t\treturn nil\n\t}\n\n\tidx := strings.IndexByte(src, 'e')\n\tif idx == -1 {\n\t\treturn z.SetFromDecimal(src)\n\t}\n\tif err := z.SetFromDecimal(src[:idx]); err != nil {\n\t\treturn err\n\t}\n\tif src[(idx+1):] == \"0\" {\n\t\treturn nil\n\t}\n\texp := new(Uint)\n\tif err := exp.SetFromDecimal(src[(idx + 1):]); err != nil {\n\t\treturn err\n\t}\n\tif exp.GtUint64(77) { // 10**78 is larger than 2**256\n\t\treturn ErrBig256Range\n\t}\n\texp.Exp(NewUint(10), exp)\n\tif _, overflow := z.MulOverflow(z, exp); overflow {\n\t\treturn ErrBig256Range\n\t}\n\treturn nil\n}\n\n// ToString returns the decimal string representation of z. It returns an empty string if z is nil.\n// OBS: doesn't exist from holiman's uint256\nfunc (z *Uint) ToString() string {\n\tif z == nil {\n\t\treturn \"\"\n\t}\n\n\treturn z.Dec()\n}\n\n// MarshalJSON implements json.Marshaler.\n// MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible\n// with big.Uint: big.Uint marshals into JSON 'native' numeric format.\n//\n// The JSON native format is, on some platforms, (e.g. javascript), limited to 53-bit large\n// integer space. Thus, U256 uses string-format, which is not compatible with\n// big.int (big.Uint refuses to unmarshal a string representation).\nfunc (z *Uint) MarshalJSON() ([]byte, error) {\n\treturn []byte(`\"` + z.Dec() + `\"`), nil\n}\n\n// UnmarshalJSON implements json.Unmarshaler. UnmarshalJSON accepts either\n// - Quoted string: either hexadecimal OR decimal\n// - Not quoted string: only decimal\nfunc (z *Uint) UnmarshalJSON(input []byte) error {\n\tif len(input) \u003c 2 || input[0] != '\"' || input[len(input)-1] != '\"' {\n\t\t// if not quoted, it must be decimal\n\t\treturn z.fromDecimal(string(input))\n\t}\n\treturn z.UnmarshalText(input[1 : len(input)-1])\n}\n\n// MarshalText implements encoding.TextMarshaler\n// MarshalText marshals using the decimal representation (compatible with big.Uint)\nfunc (z *Uint) MarshalText() ([]byte, error) {\n\treturn []byte(z.Dec()), nil\n}\n\n// UnmarshalText implements encoding.TextUnmarshaler. This method\n// can unmarshal either hexadecimal or decimal.\n// - For hexadecimal, the input _must_ be prefixed with 0x or 0X\nfunc (z *Uint) UnmarshalText(input []byte) error {\n\tif len(input) \u003e= 2 \u0026\u0026 input[0] == '0' \u0026\u0026 (input[1] == 'x' || input[1] == 'X') {\n\t\treturn z.fromHex(string(input))\n\t}\n\treturn z.fromDecimal(string(input))\n}\n\n// SetBytes interprets buf as the bytes of a big-endian unsigned\n// integer, sets z to that value, and returns z.\n// If buf is larger than 32 bytes, the last 32 bytes is used.\nfunc (z *Uint) SetBytes(buf []byte) *Uint {\n\tswitch l := len(buf); l {\n\tcase 0:\n\t\tz.Clear()\n\tcase 1:\n\t\tz.SetBytes1(buf)\n\tcase 2:\n\t\tz.SetBytes2(buf)\n\tcase 3:\n\t\tz.SetBytes3(buf)\n\tcase 4:\n\t\tz.SetBytes4(buf)\n\tcase 5:\n\t\tz.SetBytes5(buf)\n\tcase 6:\n\t\tz.SetBytes6(buf)\n\tcase 7:\n\t\tz.SetBytes7(buf)\n\tcase 8:\n\t\tz.SetBytes8(buf)\n\tcase 9:\n\t\tz.SetBytes9(buf)\n\tcase 10:\n\t\tz.SetBytes10(buf)\n\tcase 11:\n\t\tz.SetBytes11(buf)\n\tcase 12:\n\t\tz.SetBytes12(buf)\n\tcase 13:\n\t\tz.SetBytes13(buf)\n\tcase 14:\n\t\tz.SetBytes14(buf)\n\tcase 15:\n\t\tz.SetBytes15(buf)\n\tcase 16:\n\t\tz.SetBytes16(buf)\n\tcase 17:\n\t\tz.SetBytes17(buf)\n\tcase 18:\n\t\tz.SetBytes18(buf)\n\tcase 19:\n\t\tz.SetBytes19(buf)\n\tcase 20:\n\t\tz.SetBytes20(buf)\n\tcase 21:\n\t\tz.SetBytes21(buf)\n\tcase 22:\n\t\tz.SetBytes22(buf)\n\tcase 23:\n\t\tz.SetBytes23(buf)\n\tcase 24:\n\t\tz.SetBytes24(buf)\n\tcase 25:\n\t\tz.SetBytes25(buf)\n\tcase 26:\n\t\tz.SetBytes26(buf)\n\tcase 27:\n\t\tz.SetBytes27(buf)\n\tcase 28:\n\t\tz.SetBytes28(buf)\n\tcase 29:\n\t\tz.SetBytes29(buf)\n\tcase 30:\n\t\tz.SetBytes30(buf)\n\tcase 31:\n\t\tz.SetBytes31(buf)\n\tdefault:\n\t\tz.SetBytes32(buf[l-32:])\n\t}\n\treturn z\n}\n\n// SetBytes1 is identical to SetBytes(in[:1]), but panics is input is too short\nfunc (z *Uint) SetBytes1(in []byte) *Uint {\n\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\tz.arr[0] = uint64(in[0])\n\treturn z\n}\n\n// SetBytes2 is identical to SetBytes(in[:2]), but panics is input is too short\nfunc (z *Uint) SetBytes2(in []byte) *Uint {\n\t_ = in[1] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\tz.arr[0] = uint64(binary.BigEndian.Uint16(in[0:2]))\n\treturn z\n}\n\n// SetBytes3 is identical to SetBytes(in[:3]), but panics is input is too short\nfunc (z *Uint) SetBytes3(in []byte) *Uint {\n\t_ = in[2] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\tz.arr[0] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])\u003c\u003c16\n\treturn z\n}\n\n// SetBytes4 is identical to SetBytes(in[:4]), but panics is input is too short\nfunc (z *Uint) SetBytes4(in []byte) *Uint {\n\t_ = in[3] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\tz.arr[0] = uint64(binary.BigEndian.Uint32(in[0:4]))\n\treturn z\n}\n\n// SetBytes5 is identical to SetBytes(in[:5]), but panics is input is too short\nfunc (z *Uint) SetBytes5(in []byte) *Uint {\n\t_ = in[4] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\tz.arr[0] = bigEndianUint40(in[0:5])\n\treturn z\n}\n\n// SetBytes6 is identical to SetBytes(in[:6]), but panics is input is too short\nfunc (z *Uint) SetBytes6(in []byte) *Uint {\n\t_ = in[5] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\tz.arr[0] = bigEndianUint48(in[0:6])\n\treturn z\n}\n\n// SetBytes7 is identical to SetBytes(in[:7]), but panics is input is too short\nfunc (z *Uint) SetBytes7(in []byte) *Uint {\n\t_ = in[6] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\tz.arr[0] = bigEndianUint56(in[0:7])\n\treturn z\n}\n\n// SetBytes8 is identical to SetBytes(in[:8]), but panics is input is too short\nfunc (z *Uint) SetBytes8(in []byte) *Uint {\n\t_ = in[7] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\tz.arr[0] = binary.BigEndian.Uint64(in[0:8])\n\treturn z\n}\n\n// SetBytes9 is identical to SetBytes(in[:9]), but panics is input is too short\nfunc (z *Uint) SetBytes9(in []byte) *Uint {\n\t_ = in[8] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2] = 0, 0\n\tz.arr[1] = uint64(in[0])\n\tz.arr[0] = binary.BigEndian.Uint64(in[1:9])\n\treturn z\n}\n\n// SetBytes10 is identical to SetBytes(in[:10]), but panics is input is too short\nfunc (z *Uint) SetBytes10(in []byte) *Uint {\n\t_ = in[9] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2] = 0, 0\n\tz.arr[1] = uint64(binary.BigEndian.Uint16(in[0:2]))\n\tz.arr[0] = binary.BigEndian.Uint64(in[2:10])\n\treturn z\n}\n\n// SetBytes11 is identical to SetBytes(in[:11]), but panics is input is too short\nfunc (z *Uint) SetBytes11(in []byte) *Uint {\n\t_ = in[10] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2] = 0, 0\n\tz.arr[1] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])\u003c\u003c16\n\tz.arr[0] = binary.BigEndian.Uint64(in[3:11])\n\treturn z\n}\n\n// SetBytes12 is identical to SetBytes(in[:12]), but panics is input is too short\nfunc (z *Uint) SetBytes12(in []byte) *Uint {\n\t_ = in[11] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2] = 0, 0\n\tz.arr[1] = uint64(binary.BigEndian.Uint32(in[0:4]))\n\tz.arr[0] = binary.BigEndian.Uint64(in[4:12])\n\treturn z\n}\n\n// SetBytes13 is identical to SetBytes(in[:13]), but panics is input is too short\nfunc (z *Uint) SetBytes13(in []byte) *Uint {\n\t_ = in[12] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2] = 0, 0\n\tz.arr[1] = bigEndianUint40(in[0:5])\n\tz.arr[0] = binary.BigEndian.Uint64(in[5:13])\n\treturn z\n}\n\n// SetBytes14 is identical to SetBytes(in[:14]), but panics is input is too short\nfunc (z *Uint) SetBytes14(in []byte) *Uint {\n\t_ = in[13] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2] = 0, 0\n\tz.arr[1] = bigEndianUint48(in[0:6])\n\tz.arr[0] = binary.BigEndian.Uint64(in[6:14])\n\treturn z\n}\n\n// SetBytes15 is identical to SetBytes(in[:15]), but panics is input is too short\nfunc (z *Uint) SetBytes15(in []byte) *Uint {\n\t_ = in[14] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2] = 0, 0\n\tz.arr[1] = bigEndianUint56(in[0:7])\n\tz.arr[0] = binary.BigEndian.Uint64(in[7:15])\n\treturn z\n}\n\n// SetBytes16 is identical to SetBytes(in[:16]), but panics is input is too short\nfunc (z *Uint) SetBytes16(in []byte) *Uint {\n\t_ = in[15] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3], z.arr[2] = 0, 0\n\tz.arr[1] = binary.BigEndian.Uint64(in[0:8])\n\tz.arr[0] = binary.BigEndian.Uint64(in[8:16])\n\treturn z\n}\n\n// SetBytes17 is identical to SetBytes(in[:17]), but panics is input is too short\nfunc (z *Uint) SetBytes17(in []byte) *Uint {\n\t_ = in[16] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = 0\n\tz.arr[2] = uint64(in[0])\n\tz.arr[1] = binary.BigEndian.Uint64(in[1:9])\n\tz.arr[0] = binary.BigEndian.Uint64(in[9:17])\n\treturn z\n}\n\n// SetBytes18 is identical to SetBytes(in[:18]), but panics is input is too short\nfunc (z *Uint) SetBytes18(in []byte) *Uint {\n\t_ = in[17] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = 0\n\tz.arr[2] = uint64(binary.BigEndian.Uint16(in[0:2]))\n\tz.arr[1] = binary.BigEndian.Uint64(in[2:10])\n\tz.arr[0] = binary.BigEndian.Uint64(in[10:18])\n\treturn z\n}\n\n// SetBytes19 is identical to SetBytes(in[:19]), but panics is input is too short\nfunc (z *Uint) SetBytes19(in []byte) *Uint {\n\t_ = in[18] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = 0\n\tz.arr[2] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])\u003c\u003c16\n\tz.arr[1] = binary.BigEndian.Uint64(in[3:11])\n\tz.arr[0] = binary.BigEndian.Uint64(in[11:19])\n\treturn z\n}\n\n// SetBytes20 is identical to SetBytes(in[:20]), but panics is input is too short\nfunc (z *Uint) SetBytes20(in []byte) *Uint {\n\t_ = in[19] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = 0\n\tz.arr[2] = uint64(binary.BigEndian.Uint32(in[0:4]))\n\tz.arr[1] = binary.BigEndian.Uint64(in[4:12])\n\tz.arr[0] = binary.BigEndian.Uint64(in[12:20])\n\treturn z\n}\n\n// SetBytes21 is identical to SetBytes(in[:21]), but panics is input is too short\nfunc (z *Uint) SetBytes21(in []byte) *Uint {\n\t_ = in[20] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = 0\n\tz.arr[2] = bigEndianUint40(in[0:5])\n\tz.arr[1] = binary.BigEndian.Uint64(in[5:13])\n\tz.arr[0] = binary.BigEndian.Uint64(in[13:21])\n\treturn z\n}\n\n// SetBytes22 is identical to SetBytes(in[:22]), but panics is input is too short\nfunc (z *Uint) SetBytes22(in []byte) *Uint {\n\t_ = in[21] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = 0\n\tz.arr[2] = bigEndianUint48(in[0:6])\n\tz.arr[1] = binary.BigEndian.Uint64(in[6:14])\n\tz.arr[0] = binary.BigEndian.Uint64(in[14:22])\n\treturn z\n}\n\n// SetBytes23 is identical to SetBytes(in[:23]), but panics is input is too short\nfunc (z *Uint) SetBytes23(in []byte) *Uint {\n\t_ = in[22] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = 0\n\tz.arr[2] = bigEndianUint56(in[0:7])\n\tz.arr[1] = binary.BigEndian.Uint64(in[7:15])\n\tz.arr[0] = binary.BigEndian.Uint64(in[15:23])\n\treturn z\n}\n\n// SetBytes24 is identical to SetBytes(in[:24]), but panics is input is too short\nfunc (z *Uint) SetBytes24(in []byte) *Uint {\n\t_ = in[23] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = 0\n\tz.arr[2] = binary.BigEndian.Uint64(in[0:8])\n\tz.arr[1] = binary.BigEndian.Uint64(in[8:16])\n\tz.arr[0] = binary.BigEndian.Uint64(in[16:24])\n\treturn z\n}\n\n// SetBytes25 is identical to SetBytes(in[:25]), but panics is input is too short\nfunc (z *Uint) SetBytes25(in []byte) *Uint {\n\t_ = in[24] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = uint64(in[0])\n\tz.arr[2] = binary.BigEndian.Uint64(in[1:9])\n\tz.arr[1] = binary.BigEndian.Uint64(in[9:17])\n\tz.arr[0] = binary.BigEndian.Uint64(in[17:25])\n\treturn z\n}\n\n// SetBytes26 is identical to SetBytes(in[:26]), but panics is input is too short\nfunc (z *Uint) SetBytes26(in []byte) *Uint {\n\t_ = in[25] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = uint64(binary.BigEndian.Uint16(in[0:2]))\n\tz.arr[2] = binary.BigEndian.Uint64(in[2:10])\n\tz.arr[1] = binary.BigEndian.Uint64(in[10:18])\n\tz.arr[0] = binary.BigEndian.Uint64(in[18:26])\n\treturn z\n}\n\n// SetBytes27 is identical to SetBytes(in[:27]), but panics is input is too short\nfunc (z *Uint) SetBytes27(in []byte) *Uint {\n\t_ = in[26] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])\u003c\u003c16\n\tz.arr[2] = binary.BigEndian.Uint64(in[3:11])\n\tz.arr[1] = binary.BigEndian.Uint64(in[11:19])\n\tz.arr[0] = binary.BigEndian.Uint64(in[19:27])\n\treturn z\n}\n\n// SetBytes28 is identical to SetBytes(in[:28]), but panics is input is too short\nfunc (z *Uint) SetBytes28(in []byte) *Uint {\n\t_ = in[27] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = uint64(binary.BigEndian.Uint32(in[0:4]))\n\tz.arr[2] = binary.BigEndian.Uint64(in[4:12])\n\tz.arr[1] = binary.BigEndian.Uint64(in[12:20])\n\tz.arr[0] = binary.BigEndian.Uint64(in[20:28])\n\treturn z\n}\n\n// SetBytes29 is identical to SetBytes(in[:29]), but panics is input is too short\nfunc (z *Uint) SetBytes29(in []byte) *Uint {\n\t_ = in[23] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = bigEndianUint40(in[0:5])\n\tz.arr[2] = binary.BigEndian.Uint64(in[5:13])\n\tz.arr[1] = binary.BigEndian.Uint64(in[13:21])\n\tz.arr[0] = binary.BigEndian.Uint64(in[21:29])\n\treturn z\n}\n\n// SetBytes30 is identical to SetBytes(in[:30]), but panics is input is too short\nfunc (z *Uint) SetBytes30(in []byte) *Uint {\n\t_ = in[29] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = bigEndianUint48(in[0:6])\n\tz.arr[2] = binary.BigEndian.Uint64(in[6:14])\n\tz.arr[1] = binary.BigEndian.Uint64(in[14:22])\n\tz.arr[0] = binary.BigEndian.Uint64(in[22:30])\n\treturn z\n}\n\n// SetBytes31 is identical to SetBytes(in[:31]), but panics is input is too short\nfunc (z *Uint) SetBytes31(in []byte) *Uint {\n\t_ = in[30] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = bigEndianUint56(in[0:7])\n\tz.arr[2] = binary.BigEndian.Uint64(in[7:15])\n\tz.arr[1] = binary.BigEndian.Uint64(in[15:23])\n\tz.arr[0] = binary.BigEndian.Uint64(in[23:31])\n\treturn z\n}\n\n// SetBytes32 sets z to the value of the big-endian 256-bit unsigned integer in.\nfunc (z *Uint) SetBytes32(in []byte) *Uint {\n\t_ = in[31] // bounds check hint to compiler; see golang.org/issue/14808\n\tz.arr[3] = binary.BigEndian.Uint64(in[0:8])\n\tz.arr[2] = binary.BigEndian.Uint64(in[8:16])\n\tz.arr[1] = binary.BigEndian.Uint64(in[16:24])\n\tz.arr[0] = binary.BigEndian.Uint64(in[24:32])\n\treturn z\n}\n\n// Utility methods that are \"missing\" among the bigEndian.UintXX methods.\n\n// bigEndianUint40 returns the uint64 value represented by the 5 bytes in big-endian order.\nfunc bigEndianUint40(b []byte) uint64 {\n\t_ = b[4] // bounds check hint to compiler; see golang.org/issue/14808\n\treturn uint64(b[4]) | uint64(b[3])\u003c\u003c8 | uint64(b[2])\u003c\u003c16 | uint64(b[1])\u003c\u003c24 |\n\t\tuint64(b[0])\u003c\u003c32\n}\n\n// bigEndianUint56 returns the uint64 value represented by the 7 bytes in big-endian order.\nfunc bigEndianUint56(b []byte) uint64 {\n\t_ = b[6] // bounds check hint to compiler; see golang.org/issue/14808\n\treturn uint64(b[6]) | uint64(b[5])\u003c\u003c8 | uint64(b[4])\u003c\u003c16 | uint64(b[3])\u003c\u003c24 |\n\t\tuint64(b[2])\u003c\u003c32 | uint64(b[1])\u003c\u003c40 | uint64(b[0])\u003c\u003c48\n}\n\n// bigEndianUint48 returns the uint64 value represented by the 6 bytes in big-endian order.\nfunc bigEndianUint48(b []byte) uint64 {\n\t_ = b[5] // bounds check hint to compiler; see golang.org/issue/14808\n\treturn uint64(b[5]) | uint64(b[4])\u003c\u003c8 | uint64(b[3])\u003c\u003c16 | uint64(b[2])\u003c\u003c24 |\n\t\tuint64(b[1])\u003c\u003c32 | uint64(b[0])\u003c\u003c40\n}\n" + }, + { + "name": "conversion_test.gno", + "body": "package uint256\n\nimport \"testing\"\n\nfunc TestIsUint64(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant bool\n\t}{\n\t\t{\"0x0\", true},\n\t\t{\"0x1\", true},\n\t\t{\"0x10\", true},\n\t\t{\"0xffffffffffffffff\", true},\n\t\t{\"0x10000000000000000\", false},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx := MustFromHex(tc.x)\n\t\tgot := x.IsUint64()\n\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"IsUint64(%s) = %v, want %v\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestDec(t *testing.T) {\n\ttestCases := []struct {\n\t\tname string\n\t\tz Uint\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"zero\",\n\t\t\tz: Uint{arr: [4]uint64{0, 0, 0, 0}},\n\t\t\twant: \"0\",\n\t\t},\n\t\t{\n\t\t\tname: \"less than 20 digits\",\n\t\t\tz: Uint{arr: [4]uint64{1234567890, 0, 0, 0}},\n\t\t\twant: \"1234567890\",\n\t\t},\n\t\t{\n\t\t\tname: \"max possible value\",\n\t\t\tz: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}},\n\t\t\twant: \"115792089237316195423570985008687907853269984665640564039457584007913129639935\",\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tresult := tc.z.Dec()\n\t\t\tif result != tc.want {\n\t\t\t\tt.Errorf(\"Dec(%v) = %s, want %s\", tc.z, result, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "error.gno", + "body": "package uint256\n\nimport (\n\t\"errors\"\n)\n\nvar (\n\tErrEmptyString = errors.New(\"empty hex string\")\n\tErrSyntax = errors.New(\"invalid hex string\")\n\tErrRange = errors.New(\"number out of range\")\n\tErrMissingPrefix = errors.New(\"hex string without 0x prefix\")\n\tErrEmptyNumber = errors.New(\"hex string \\\"0x\\\"\")\n\tErrLeadingZero = errors.New(\"hex number with leading zero digits\")\n\tErrBig256Range = errors.New(\"hex number \u003e 256 bits\")\n\tErrBadBufferLength = errors.New(\"bad ssz buffer length\")\n\tErrBadEncodedLength = errors.New(\"bad ssz encoded length\")\n\tErrInvalidBase = errors.New(\"invalid base\")\n\tErrInvalidBitSize = errors.New(\"invalid bit size\")\n)\n\ntype u256Error struct {\n\tfn string // function name\n\tinput string\n\terr error\n}\n\nfunc (e *u256Error) Error() string {\n\treturn e.fn + \": \" + e.input + \": \" + e.err.Error()\n}\n\nfunc (e *u256Error) Unwrap() error {\n\treturn e.err\n}\n\nfunc errEmptyString(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrEmptyString}\n}\n\nfunc errSyntax(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrSyntax}\n}\n\nfunc errMissingPrefix(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrMissingPrefix}\n}\n\nfunc errEmptyNumber(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrEmptyNumber}\n}\n\nfunc errLeadingZero(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrLeadingZero}\n}\n\nfunc errRange(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrRange}\n}\n\nfunc errBig256Range(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrBig256Range}\n}\n\nfunc errBadBufferLength(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrBadBufferLength}\n}\n\nfunc errInvalidBase(fn string, base int) error {\n\treturn \u0026u256Error{fn: fn, input: string(base), err: ErrInvalidBase}\n}\n\nfunc errInvalidBitSize(fn string, bitSize int) error {\n\treturn \u0026u256Error{fn: fn, input: string(bitSize), err: ErrInvalidBitSize}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/uint256\n" + }, + { + "name": "mod.gno", + "body": "package uint256\n\nimport (\n\t\"math/bits\"\n)\n\n// Some utility functions\n\n// Reciprocal computes a 320-bit value representing 1/m\n//\n// Notes:\n// - specialized for m.arr[3] != 0, hence limited to 2^192 \u003c= m \u003c 2^256\n// - returns zero if m.arr[3] == 0\n// - starts with a 32-bit division, refines with newton-raphson iterations\nfunc Reciprocal(m *Uint) (mu [5]uint64) {\n\tif m.arr[3] == 0 {\n\t\treturn mu\n\t}\n\n\ts := bits.LeadingZeros64(m.arr[3]) // Replace with leadingZeros(m) for general case\n\tp := 255 - s // floor(log_2(m)), m\u003e0\n\n\t// 0 or a power of 2?\n\n\t// Check if at least one bit is set in m.arr[2], m.arr[1] or m.arr[0],\n\t// or at least two bits in m.arr[3]\n\n\tif m.arr[0]|m.arr[1]|m.arr[2]|(m.arr[3]\u0026(m.arr[3]-1)) == 0 {\n\n\t\tmu[4] = ^uint64(0) \u003e\u003e uint(p\u002663)\n\t\tmu[3] = ^uint64(0)\n\t\tmu[2] = ^uint64(0)\n\t\tmu[1] = ^uint64(0)\n\t\tmu[0] = ^uint64(0)\n\n\t\treturn mu\n\t}\n\n\t// Maximise division precision by left-aligning divisor\n\n\tvar (\n\t\ty Uint // left-aligned copy of m\n\t\tr0 uint32 // estimate of 2^31/y\n\t)\n\n\ty.Lsh(m, uint(s)) // 1/2 \u003c y \u003c 1\n\n\t// Extract most significant 32 bits\n\n\tyh := uint32(y.arr[3] \u003e\u003e 32)\n\n\tif yh == 0x80000000 { // Avoid overflow in division\n\t\tr0 = 0xffffffff\n\t} else {\n\t\tr0, _ = bits.Div32(0x80000000, 0, yh)\n\t}\n\n\t// First iteration: 32 -\u003e 64\n\n\tt1 := uint64(r0) // 2^31/y\n\tt1 *= t1 // 2^62/y^2\n\tt1, _ = bits.Mul64(t1, y.arr[3]) // 2^62/y^2 * 2^64/y / 2^64 = 2^62/y\n\n\tr1 := uint64(r0) \u003c\u003c 32 // 2^63/y\n\tr1 -= t1 // 2^63/y - 2^62/y = 2^62/y\n\tr1 *= 2 // 2^63/y\n\n\tif (r1 | (y.arr[3] \u003c\u003c 1)) == 0 {\n\t\tr1 = ^uint64(0)\n\t}\n\n\t// Second iteration: 64 -\u003e 128\n\n\t// square: 2^126/y^2\n\ta2h, a2l := bits.Mul64(r1, r1)\n\n\t// multiply by y: e2h:e2l:b2h = 2^126/y^2 * 2^128/y / 2^128 = 2^126/y\n\tb2h, _ := bits.Mul64(a2l, y.arr[2])\n\tc2h, c2l := bits.Mul64(a2l, y.arr[3])\n\td2h, d2l := bits.Mul64(a2h, y.arr[2])\n\te2h, e2l := bits.Mul64(a2h, y.arr[3])\n\n\tb2h, c := bits.Add64(b2h, c2l, 0)\n\te2l, c = bits.Add64(e2l, c2h, c)\n\te2h, _ = bits.Add64(e2h, 0, c)\n\n\t_, c = bits.Add64(b2h, d2l, 0)\n\te2l, c = bits.Add64(e2l, d2h, c)\n\te2h, _ = bits.Add64(e2h, 0, c)\n\n\t// subtract: t2h:t2l = 2^127/y - 2^126/y = 2^126/y\n\tt2l, b := bits.Sub64(0, e2l, 0)\n\tt2h, _ := bits.Sub64(r1, e2h, b)\n\n\t// double: r2h:r2l = 2^127/y\n\tr2l, c := bits.Add64(t2l, t2l, 0)\n\tr2h, _ := bits.Add64(t2h, t2h, c)\n\n\tif (r2h | r2l | (y.arr[3] \u003c\u003c 1)) == 0 {\n\t\tr2h = ^uint64(0)\n\t\tr2l = ^uint64(0)\n\t}\n\n\t// Third iteration: 128 -\u003e 192\n\n\t// square r2 (keep 256 bits): 2^190/y^2\n\ta3h, a3l := bits.Mul64(r2l, r2l)\n\tb3h, b3l := bits.Mul64(r2l, r2h)\n\tc3h, c3l := bits.Mul64(r2h, r2h)\n\n\ta3h, c = bits.Add64(a3h, b3l, 0)\n\tc3l, c = bits.Add64(c3l, b3h, c)\n\tc3h, _ = bits.Add64(c3h, 0, c)\n\n\ta3h, c = bits.Add64(a3h, b3l, 0)\n\tc3l, c = bits.Add64(c3l, b3h, c)\n\tc3h, _ = bits.Add64(c3h, 0, c)\n\n\t// multiply by y: q = 2^190/y^2 * 2^192/y / 2^192 = 2^190/y\n\n\tx0 := a3l\n\tx1 := a3h\n\tx2 := c3l\n\tx3 := c3h\n\n\tvar q0, q1, q2, q3, q4, t0 uint64\n\n\tq0, _ = bits.Mul64(x2, y.arr[0])\n\tq1, t0 = bits.Mul64(x3, y.arr[0])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, _ = bits.Add64(q1, 0, c)\n\n\tt1, _ = bits.Mul64(x1, y.arr[1])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tq2, t0 = bits.Mul64(x3, y.arr[1])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, _ = bits.Add64(q2, 0, c)\n\n\tt1, t0 = bits.Mul64(x2, y.arr[1])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tq2, _ = bits.Add64(q2, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[2])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tq3, t0 = bits.Mul64(x3, y.arr[2])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, _ = bits.Add64(q3, 0, c)\n\n\tt1, _ = bits.Mul64(x0, y.arr[2])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tt1, t0 = bits.Mul64(x2, y.arr[2])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, c = bits.Add64(q2, t1, c)\n\tq3, _ = bits.Add64(q3, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[3])\n\tq1, c = bits.Add64(q1, t0, 0)\n\tq2, c = bits.Add64(q2, t1, c)\n\tq4, t0 = bits.Mul64(x3, y.arr[3])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, _ = bits.Add64(q4, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, y.arr[3])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tt1, t0 = bits.Mul64(x2, y.arr[3])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, c = bits.Add64(q3, t1, c)\n\tq4, _ = bits.Add64(q4, 0, c)\n\n\t// subtract: t3 = 2^191/y - 2^190/y = 2^190/y\n\t_, b = bits.Sub64(0, q0, 0)\n\t_, b = bits.Sub64(0, q1, b)\n\tt3l, b := bits.Sub64(0, q2, b)\n\tt3m, b := bits.Sub64(r2l, q3, b)\n\tt3h, _ := bits.Sub64(r2h, q4, b)\n\n\t// double: r3 = 2^191/y\n\tr3l, c := bits.Add64(t3l, t3l, 0)\n\tr3m, c := bits.Add64(t3m, t3m, c)\n\tr3h, _ := bits.Add64(t3h, t3h, c)\n\n\t// Fourth iteration: 192 -\u003e 320\n\n\t// square r3\n\n\ta4h, a4l := bits.Mul64(r3l, r3l)\n\tb4h, b4l := bits.Mul64(r3l, r3m)\n\tc4h, c4l := bits.Mul64(r3l, r3h)\n\td4h, d4l := bits.Mul64(r3m, r3m)\n\te4h, e4l := bits.Mul64(r3m, r3h)\n\tf4h, f4l := bits.Mul64(r3h, r3h)\n\n\tb4h, c = bits.Add64(b4h, c4l, 0)\n\te4l, c = bits.Add64(e4l, c4h, c)\n\te4h, _ = bits.Add64(e4h, 0, c)\n\n\ta4h, c = bits.Add64(a4h, b4l, 0)\n\td4l, c = bits.Add64(d4l, b4h, c)\n\td4h, c = bits.Add64(d4h, e4l, c)\n\tf4l, c = bits.Add64(f4l, e4h, c)\n\tf4h, _ = bits.Add64(f4h, 0, c)\n\n\ta4h, c = bits.Add64(a4h, b4l, 0)\n\td4l, c = bits.Add64(d4l, b4h, c)\n\td4h, c = bits.Add64(d4h, e4l, c)\n\tf4l, c = bits.Add64(f4l, e4h, c)\n\tf4h, _ = bits.Add64(f4h, 0, c)\n\n\t// multiply by y\n\n\tx1, x0 = bits.Mul64(d4h, y.arr[0])\n\tx3, x2 = bits.Mul64(f4h, y.arr[0])\n\tt1, t0 = bits.Mul64(f4l, y.arr[0])\n\tx1, c = bits.Add64(x1, t0, 0)\n\tx2, c = bits.Add64(x2, t1, c)\n\tx3, _ = bits.Add64(x3, 0, c)\n\n\tt1, t0 = bits.Mul64(d4h, y.arr[1])\n\tx1, c = bits.Add64(x1, t0, 0)\n\tx2, c = bits.Add64(x2, t1, c)\n\tx4, t0 := bits.Mul64(f4h, y.arr[1])\n\tx3, c = bits.Add64(x3, t0, c)\n\tx4, _ = bits.Add64(x4, 0, c)\n\tt1, t0 = bits.Mul64(d4l, y.arr[1])\n\tx0, c = bits.Add64(x0, t0, 0)\n\tx1, c = bits.Add64(x1, t1, c)\n\tt1, t0 = bits.Mul64(f4l, y.arr[1])\n\tx2, c = bits.Add64(x2, t0, c)\n\tx3, c = bits.Add64(x3, t1, c)\n\tx4, _ = bits.Add64(x4, 0, c)\n\n\tt1, t0 = bits.Mul64(a4h, y.arr[2])\n\tx0, c = bits.Add64(x0, t0, 0)\n\tx1, c = bits.Add64(x1, t1, c)\n\tt1, t0 = bits.Mul64(d4h, y.arr[2])\n\tx2, c = bits.Add64(x2, t0, c)\n\tx3, c = bits.Add64(x3, t1, c)\n\tx5, t0 := bits.Mul64(f4h, y.arr[2])\n\tx4, c = bits.Add64(x4, t0, c)\n\tx5, _ = bits.Add64(x5, 0, c)\n\tt1, t0 = bits.Mul64(d4l, y.arr[2])\n\tx1, c = bits.Add64(x1, t0, 0)\n\tx2, c = bits.Add64(x2, t1, c)\n\tt1, t0 = bits.Mul64(f4l, y.arr[2])\n\tx3, c = bits.Add64(x3, t0, c)\n\tx4, c = bits.Add64(x4, t1, c)\n\tx5, _ = bits.Add64(x5, 0, c)\n\n\tt1, t0 = bits.Mul64(a4h, y.arr[3])\n\tx1, c = bits.Add64(x1, t0, 0)\n\tx2, c = bits.Add64(x2, t1, c)\n\tt1, t0 = bits.Mul64(d4h, y.arr[3])\n\tx3, c = bits.Add64(x3, t0, c)\n\tx4, c = bits.Add64(x4, t1, c)\n\tx6, t0 := bits.Mul64(f4h, y.arr[3])\n\tx5, c = bits.Add64(x5, t0, c)\n\tx6, _ = bits.Add64(x6, 0, c)\n\tt1, t0 = bits.Mul64(a4l, y.arr[3])\n\tx0, c = bits.Add64(x0, t0, 0)\n\tx1, c = bits.Add64(x1, t1, c)\n\tt1, t0 = bits.Mul64(d4l, y.arr[3])\n\tx2, c = bits.Add64(x2, t0, c)\n\tx3, c = bits.Add64(x3, t1, c)\n\tt1, t0 = bits.Mul64(f4l, y.arr[3])\n\tx4, c = bits.Add64(x4, t0, c)\n\tx5, c = bits.Add64(x5, t1, c)\n\tx6, _ = bits.Add64(x6, 0, c)\n\n\t// subtract\n\t_, b = bits.Sub64(0, x0, 0)\n\t_, b = bits.Sub64(0, x1, b)\n\tr4l, b := bits.Sub64(0, x2, b)\n\tr4k, b := bits.Sub64(0, x3, b)\n\tr4j, b := bits.Sub64(r3l, x4, b)\n\tr4i, b := bits.Sub64(r3m, x5, b)\n\tr4h, _ := bits.Sub64(r3h, x6, b)\n\n\t// Multiply candidate for 1/4y by y, with full precision\n\n\tx0 = r4l\n\tx1 = r4k\n\tx2 = r4j\n\tx3 = r4i\n\tx4 = r4h\n\n\tq1, q0 = bits.Mul64(x0, y.arr[0])\n\tq3, q2 = bits.Mul64(x2, y.arr[0])\n\tq5, q4 := bits.Mul64(x4, y.arr[0])\n\n\tt1, t0 = bits.Mul64(x1, y.arr[0])\n\tq1, c = bits.Add64(q1, t0, 0)\n\tq2, c = bits.Add64(q2, t1, c)\n\tt1, t0 = bits.Mul64(x3, y.arr[0])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, c = bits.Add64(q4, t1, c)\n\tq5, _ = bits.Add64(q5, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, y.arr[1])\n\tq1, c = bits.Add64(q1, t0, 0)\n\tq2, c = bits.Add64(q2, t1, c)\n\tt1, t0 = bits.Mul64(x2, y.arr[1])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, c = bits.Add64(q4, t1, c)\n\tq6, t0 := bits.Mul64(x4, y.arr[1])\n\tq5, c = bits.Add64(q5, t0, c)\n\tq6, _ = bits.Add64(q6, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[1])\n\tq2, c = bits.Add64(q2, t0, 0)\n\tq3, c = bits.Add64(q3, t1, c)\n\tt1, t0 = bits.Mul64(x3, y.arr[1])\n\tq4, c = bits.Add64(q4, t0, c)\n\tq5, c = bits.Add64(q5, t1, c)\n\tq6, _ = bits.Add64(q6, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, y.arr[2])\n\tq2, c = bits.Add64(q2, t0, 0)\n\tq3, c = bits.Add64(q3, t1, c)\n\tt1, t0 = bits.Mul64(x2, y.arr[2])\n\tq4, c = bits.Add64(q4, t0, c)\n\tq5, c = bits.Add64(q5, t1, c)\n\tq7, t0 := bits.Mul64(x4, y.arr[2])\n\tq6, c = bits.Add64(q6, t0, c)\n\tq7, _ = bits.Add64(q7, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[2])\n\tq3, c = bits.Add64(q3, t0, 0)\n\tq4, c = bits.Add64(q4, t1, c)\n\tt1, t0 = bits.Mul64(x3, y.arr[2])\n\tq5, c = bits.Add64(q5, t0, c)\n\tq6, c = bits.Add64(q6, t1, c)\n\tq7, _ = bits.Add64(q7, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, y.arr[3])\n\tq3, c = bits.Add64(q3, t0, 0)\n\tq4, c = bits.Add64(q4, t1, c)\n\tt1, t0 = bits.Mul64(x2, y.arr[3])\n\tq5, c = bits.Add64(q5, t0, c)\n\tq6, c = bits.Add64(q6, t1, c)\n\tq8, t0 := bits.Mul64(x4, y.arr[3])\n\tq7, c = bits.Add64(q7, t0, c)\n\tq8, _ = bits.Add64(q8, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[3])\n\tq4, c = bits.Add64(q4, t0, 0)\n\tq5, c = bits.Add64(q5, t1, c)\n\tt1, t0 = bits.Mul64(x3, y.arr[3])\n\tq6, c = bits.Add64(q6, t0, c)\n\tq7, c = bits.Add64(q7, t1, c)\n\tq8, _ = bits.Add64(q8, 0, c)\n\n\t// Final adjustment\n\n\t// subtract q from 1/4\n\t_, b = bits.Sub64(0, q0, 0)\n\t_, b = bits.Sub64(0, q1, b)\n\t_, b = bits.Sub64(0, q2, b)\n\t_, b = bits.Sub64(0, q3, b)\n\t_, b = bits.Sub64(0, q4, b)\n\t_, b = bits.Sub64(0, q5, b)\n\t_, b = bits.Sub64(0, q6, b)\n\t_, b = bits.Sub64(0, q7, b)\n\t_, b = bits.Sub64(uint64(1)\u003c\u003c62, q8, b)\n\n\t// decrement the result\n\tx0, t := bits.Sub64(r4l, 1, 0)\n\tx1, t = bits.Sub64(r4k, 0, t)\n\tx2, t = bits.Sub64(r4j, 0, t)\n\tx3, t = bits.Sub64(r4i, 0, t)\n\tx4, _ = bits.Sub64(r4h, 0, t)\n\n\t// commit the decrement if the subtraction underflowed (reciprocal was too large)\n\tif b != 0 {\n\t\tr4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0\n\t}\n\n\t// Shift to correct bit alignment, truncating excess bits\n\n\tp = (p \u0026 63) - 1\n\n\tx0, c = bits.Add64(r4l, r4l, 0)\n\tx1, c = bits.Add64(r4k, r4k, c)\n\tx2, c = bits.Add64(r4j, r4j, c)\n\tx3, c = bits.Add64(r4i, r4i, c)\n\tx4, _ = bits.Add64(r4h, r4h, c)\n\n\tif p \u003c 0 {\n\t\tr4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0\n\t\tp = 0 // avoid negative shift below\n\t}\n\n\t{\n\t\tr := uint(p) // right shift\n\t\tl := uint(64 - r) // left shift\n\n\t\tx0 = (r4l \u003e\u003e r) | (r4k \u003c\u003c l)\n\t\tx1 = (r4k \u003e\u003e r) | (r4j \u003c\u003c l)\n\t\tx2 = (r4j \u003e\u003e r) | (r4i \u003c\u003c l)\n\t\tx3 = (r4i \u003e\u003e r) | (r4h \u003c\u003c l)\n\t\tx4 = (r4h \u003e\u003e r)\n\t}\n\n\tif p \u003e 0 {\n\t\tr4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0\n\t}\n\n\tmu[0] = r4l\n\tmu[1] = r4k\n\tmu[2] = r4j\n\tmu[3] = r4i\n\tmu[4] = r4h\n\n\treturn mu\n}\n\n// reduce4 computes the least non-negative residue of x modulo m\n//\n// requires a four-word modulus (m.arr[3] \u003e 1) and its inverse (mu)\nfunc reduce4(x [8]uint64, m *Uint, mu [5]uint64) (z Uint) {\n\t// NB: Most variable names in the comments match the pseudocode for\n\t// \tBarrett reduction in the Handbook of Applied Cryptography.\n\n\t// q1 = x/2^192\n\n\tx0 := x[3]\n\tx1 := x[4]\n\tx2 := x[5]\n\tx3 := x[6]\n\tx4 := x[7]\n\n\t// q2 = q1 * mu; q3 = q2 / 2^320\n\n\tvar q0, q1, q2, q3, q4, q5, t0, t1, c uint64\n\n\tq0, _ = bits.Mul64(x3, mu[0])\n\tq1, t0 = bits.Mul64(x4, mu[0])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, _ = bits.Add64(q1, 0, c)\n\n\tt1, _ = bits.Mul64(x2, mu[1])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tq2, t0 = bits.Mul64(x4, mu[1])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, _ = bits.Add64(q2, 0, c)\n\n\tt1, t0 = bits.Mul64(x3, mu[1])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tq2, _ = bits.Add64(q2, 0, c)\n\n\tt1, t0 = bits.Mul64(x2, mu[2])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tq3, t0 = bits.Mul64(x4, mu[2])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, _ = bits.Add64(q3, 0, c)\n\n\tt1, _ = bits.Mul64(x1, mu[2])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tt1, t0 = bits.Mul64(x3, mu[2])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, c = bits.Add64(q2, t1, c)\n\tq3, _ = bits.Add64(q3, 0, c)\n\n\tt1, _ = bits.Mul64(x0, mu[3])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tt1, t0 = bits.Mul64(x2, mu[3])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, c = bits.Add64(q2, t1, c)\n\tq4, t0 = bits.Mul64(x4, mu[3])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, _ = bits.Add64(q4, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, mu[3])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tt1, t0 = bits.Mul64(x3, mu[3])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, c = bits.Add64(q3, t1, c)\n\tq4, _ = bits.Add64(q4, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, mu[4])\n\t_, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tt1, t0 = bits.Mul64(x2, mu[4])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, c = bits.Add64(q3, t1, c)\n\tq5, t0 = bits.Mul64(x4, mu[4])\n\tq4, c = bits.Add64(q4, t0, c)\n\tq5, _ = bits.Add64(q5, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, mu[4])\n\tq1, c = bits.Add64(q1, t0, 0)\n\tq2, c = bits.Add64(q2, t1, c)\n\tt1, t0 = bits.Mul64(x3, mu[4])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, c = bits.Add64(q4, t1, c)\n\tq5, _ = bits.Add64(q5, 0, c)\n\n\t// Drop the fractional part of q3\n\n\tq0 = q1\n\tq1 = q2\n\tq2 = q3\n\tq3 = q4\n\tq4 = q5\n\n\t// r1 = x mod 2^320\n\n\tx0 = x[0]\n\tx1 = x[1]\n\tx2 = x[2]\n\tx3 = x[3]\n\tx4 = x[4]\n\n\t// r2 = q3 * m mod 2^320\n\n\tvar r0, r1, r2, r3, r4 uint64\n\n\tr4, r3 = bits.Mul64(q0, m.arr[3])\n\t_, t0 = bits.Mul64(q1, m.arr[3])\n\tr4, _ = bits.Add64(r4, t0, 0)\n\n\tt1, r2 = bits.Mul64(q0, m.arr[2])\n\tr3, c = bits.Add64(r3, t1, 0)\n\t_, t0 = bits.Mul64(q2, m.arr[2])\n\tr4, _ = bits.Add64(r4, t0, c)\n\n\tt1, t0 = bits.Mul64(q1, m.arr[2])\n\tr3, c = bits.Add64(r3, t0, 0)\n\tr4, _ = bits.Add64(r4, t1, c)\n\n\tt1, r1 = bits.Mul64(q0, m.arr[1])\n\tr2, c = bits.Add64(r2, t1, 0)\n\tt1, t0 = bits.Mul64(q2, m.arr[1])\n\tr3, c = bits.Add64(r3, t0, c)\n\tr4, _ = bits.Add64(r4, t1, c)\n\n\tt1, t0 = bits.Mul64(q1, m.arr[1])\n\tr2, c = bits.Add64(r2, t0, 0)\n\tr3, c = bits.Add64(r3, t1, c)\n\t_, t0 = bits.Mul64(q3, m.arr[1])\n\tr4, _ = bits.Add64(r4, t0, c)\n\n\tt1, r0 = bits.Mul64(q0, m.arr[0])\n\tr1, c = bits.Add64(r1, t1, 0)\n\tt1, t0 = bits.Mul64(q2, m.arr[0])\n\tr2, c = bits.Add64(r2, t0, c)\n\tr3, c = bits.Add64(r3, t1, c)\n\t_, t0 = bits.Mul64(q4, m.arr[0])\n\tr4, _ = bits.Add64(r4, t0, c)\n\n\tt1, t0 = bits.Mul64(q1, m.arr[0])\n\tr1, c = bits.Add64(r1, t0, 0)\n\tr2, c = bits.Add64(r2, t1, c)\n\tt1, t0 = bits.Mul64(q3, m.arr[0])\n\tr3, c = bits.Add64(r3, t0, c)\n\tr4, _ = bits.Add64(r4, t1, c)\n\n\t// r = r1 - r2\n\n\tvar b uint64\n\n\tr0, b = bits.Sub64(x0, r0, 0)\n\tr1, b = bits.Sub64(x1, r1, b)\n\tr2, b = bits.Sub64(x2, r2, b)\n\tr3, b = bits.Sub64(x3, r3, b)\n\tr4, b = bits.Sub64(x4, r4, b)\n\n\t// if r\u003c0 then r+=m\n\n\tif b != 0 {\n\t\tr0, c = bits.Add64(r0, m.arr[0], 0)\n\t\tr1, c = bits.Add64(r1, m.arr[1], c)\n\t\tr2, c = bits.Add64(r2, m.arr[2], c)\n\t\tr3, c = bits.Add64(r3, m.arr[3], c)\n\t\tr4, _ = bits.Add64(r4, 0, c)\n\t}\n\n\t// while (r\u003e=m) r-=m\n\n\tfor {\n\t\t// q = r - m\n\t\tq0, b = bits.Sub64(r0, m.arr[0], 0)\n\t\tq1, b = bits.Sub64(r1, m.arr[1], b)\n\t\tq2, b = bits.Sub64(r2, m.arr[2], b)\n\t\tq3, b = bits.Sub64(r3, m.arr[3], b)\n\t\tq4, b = bits.Sub64(r4, 0, b)\n\n\t\t// if borrow break\n\t\tif b != 0 {\n\t\t\tbreak\n\t\t}\n\n\t\t// r = q\n\t\tr4, r3, r2, r1, r0 = q4, q3, q2, q1, q0\n\t}\n\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = r3, r2, r1, r0\n\n\treturn z\n}\n" + }, + { + "name": "uint256.gno", + "body": "// Ported from https://github.com/holiman/uint256\n// This package provides a 256-bit unsigned integer type, Uint256, and associated functions.\npackage uint256\n\nimport (\n\t\"errors\"\n\t\"math/bits\"\n)\n\nconst (\n\tMaxUint64 = 1\u003c\u003c64 - 1\n\tuintSize = 32 \u003c\u003c (^uint(0) \u003e\u003e 63)\n)\n\n// Uint is represented as an array of 4 uint64, in little-endian order,\n// so that Uint[3] is the most significant, and Uint[0] is the least significant\ntype Uint struct {\n\tarr [4]uint64\n}\n\n// NewUint returns a new initialized Uint.\nfunc NewUint(val uint64) *Uint {\n\tz := \u0026Uint{arr: [4]uint64{val, 0, 0, 0}}\n\treturn z\n}\n\n// Zero returns a new Uint initialized to zero.\nfunc Zero() *Uint {\n\treturn NewUint(0)\n}\n\n// One returns a new Uint initialized to one.\nfunc One() *Uint {\n\treturn NewUint(1)\n}\n\n// SetAllOne sets all the bits of z to 1\nfunc (z *Uint) SetAllOne() *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, MaxUint64\n\treturn z\n}\n\n// Set sets z to x and returns z.\nfunc (z *Uint) Set(x *Uint) *Uint {\n\t*z = *x\n\n\treturn z\n}\n\n// SetOne sets z to 1\nfunc (z *Uint) SetOne() *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 1\n\treturn z\n}\n\nconst twoPow256Sub1 = \"115792089237316195423570985008687907853269984665640564039457584007913129639935\"\n\n// SetFromDecimal sets z from the given string, interpreted as a decimal number.\n// OBS! This method is _not_ strictly identical to the (*big.Uint).SetString(..., 10) method.\n// Notable differences:\n// - This method does not accept underscore input, e.g. \"100_000\",\n// - This method does not accept negative zero as valid, e.g \"-0\",\n// - (this method does not accept any negative input as valid))\nfunc (z *Uint) SetFromDecimal(s string) (err error) {\n\t// Remove max one leading +\n\tif len(s) \u003e 0 \u0026\u0026 s[0] == '+' {\n\t\ts = s[1:]\n\t}\n\t// Remove any number of leading zeroes\n\tif len(s) \u003e 0 \u0026\u0026 s[0] == '0' {\n\t\tvar i int\n\t\tvar c rune\n\t\tfor i, c = range s {\n\t\t\tif c != '0' {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\ts = s[i:]\n\t}\n\tif len(s) \u003c len(twoPow256Sub1) {\n\t\treturn z.fromDecimal(s)\n\t}\n\tif len(s) == len(twoPow256Sub1) {\n\t\tif s \u003e twoPow256Sub1 {\n\t\t\treturn ErrBig256Range\n\t\t}\n\t\treturn z.fromDecimal(s)\n\t}\n\treturn ErrBig256Range\n}\n\n// FromDecimal is a convenience-constructor to create an Uint from a\n// decimal (base 10) string. Numbers larger than 256 bits are not accepted.\nfunc FromDecimal(decimal string) (*Uint, error) {\n\tvar z Uint\n\tif err := z.SetFromDecimal(decimal); err != nil {\n\t\treturn nil, err\n\t}\n\treturn \u0026z, nil\n}\n\n// MustFromDecimal is a convenience-constructor to create an Uint from a\n// decimal (base 10) string.\n// Returns a new Uint and panics if any error occurred.\nfunc MustFromDecimal(decimal string) *Uint {\n\tvar z Uint\n\tif err := z.SetFromDecimal(decimal); err != nil {\n\t\tpanic(err)\n\t}\n\treturn \u0026z\n}\n\n// multipliers holds the values that are needed for fromDecimal\nvar multipliers = [5]*Uint{\n\tnil, // represents first round, no multiplication needed\n\t{[4]uint64{10000000000000000000, 0, 0, 0}}, // 10 ^ 19\n\t{[4]uint64{687399551400673280, 5421010862427522170, 0, 0}}, // 10 ^ 38\n\t{[4]uint64{5332261958806667264, 17004971331911604867, 2938735877055718769, 0}}, // 10 ^ 57\n\t{[4]uint64{0, 8607968719199866880, 532749306367912313, 1593091911132452277}}, // 10 ^ 76\n}\n\n// fromDecimal is a helper function to only ever be called via SetFromDecimal\n// this function takes a string and chunks it up, calling ParseUint on it up to 5 times\n// these chunks are then multiplied by the proper power of 10, then added together.\nfunc (z *Uint) fromDecimal(bs string) error {\n\t// first clear the input\n\tz.Clear()\n\t// the maximum value of uint64 is 18446744073709551615, which is 20 characters\n\t// one less means that a string of 19 9's is always within the uint64 limit\n\tvar (\n\t\tnum uint64\n\t\terr error\n\t\tremaining = len(bs)\n\t)\n\tif remaining == 0 {\n\t\treturn errors.New(\"EOF\")\n\t}\n\t// We proceed in steps of 19 characters (nibbles), from least significant to most significant.\n\t// This means that the first (up to) 19 characters do not need to be multiplied.\n\t// In the second iteration, our slice of 19 characters needs to be multipleied\n\t// by a factor of 10^19. Et cetera.\n\tfor i, mult := range multipliers {\n\t\tif remaining \u003c= 0 {\n\t\t\treturn nil // Done\n\t\t} else if remaining \u003e 19 {\n\t\t\tnum, err = parseUint(bs[remaining-19:remaining], 10, 64)\n\t\t} else {\n\t\t\t// Final round\n\t\t\tnum, err = parseUint(bs, 10, 64)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// add that number to our running total\n\t\tif i == 0 {\n\t\t\tz.SetUint64(num)\n\t\t} else {\n\t\t\tbase := NewUint(num)\n\t\t\tz.Add(z, base.Mul(base, mult))\n\t\t}\n\t\t// Chop off another 19 characters\n\t\tif remaining \u003e 19 {\n\t\t\tbs = bs[0 : remaining-19]\n\t\t}\n\t\tremaining -= 19\n\t}\n\treturn nil\n}\n\n// Byte sets z to the value of the byte at position n,\n// with 'z' considered as a big-endian 32-byte integer\n// if 'n' \u003e 32, f is set to 0\n// Example: f = '5', n=31 =\u003e 5\nfunc (z *Uint) Byte(n *Uint) *Uint {\n\t// in z, z.arr[0] is the least significant\n\tif number, overflow := n.Uint64WithOverflow(); !overflow {\n\t\tif number \u003c 32 {\n\t\t\tnumber := z.arr[4-1-number/8]\n\t\t\toffset := (n.arr[0] \u0026 0x7) \u003c\u003c 3 // 8*(n.d % 8)\n\t\t\tz.arr[0] = (number \u0026 (0xff00000000000000 \u003e\u003e offset)) \u003e\u003e (56 - offset)\n\t\t\tz.arr[3], z.arr[2], z.arr[1] = 0, 0, 0\n\t\t\treturn z\n\t\t}\n\t}\n\n\treturn z.Clear()\n}\n\n// BitLen returns the number of bits required to represent z\nfunc (z *Uint) BitLen() int {\n\tswitch {\n\tcase z.arr[3] != 0:\n\t\treturn 192 + bits.Len64(z.arr[3])\n\tcase z.arr[2] != 0:\n\t\treturn 128 + bits.Len64(z.arr[2])\n\tcase z.arr[1] != 0:\n\t\treturn 64 + bits.Len64(z.arr[1])\n\tdefault:\n\t\treturn bits.Len64(z.arr[0])\n\t}\n}\n\n// ByteLen returns the number of bytes required to represent z\nfunc (z *Uint) ByteLen() int {\n\treturn (z.BitLen() + 7) / 8\n}\n\n// Clear sets z to 0\nfunc (z *Uint) Clear() *Uint {\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0\n\treturn z\n}\n\nconst (\n\t// hextable = \"0123456789abcdef\"\n\tbintable = \"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\a\\b\\t\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\n\\v\\f\\r\\x0e\\x0f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\n\\v\\f\\r\\x0e\\x0f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"\n\tbadNibble = 0xff\n)\n\n// SetFromHex sets z from the given string, interpreted as a hexadecimal number.\n// OBS! This method is _not_ strictly identical to the (*big.Int).SetString(..., 16) method.\n// Notable differences:\n// - This method _require_ \"0x\" or \"0X\" prefix.\n// - This method does not accept zero-prefixed hex, e.g. \"0x0001\"\n// - This method does not accept underscore input, e.g. \"100_000\",\n// - This method does not accept negative zero as valid, e.g \"-0x0\",\n// - (this method does not accept any negative input as valid)\nfunc (z *Uint) SetFromHex(hex string) error {\n\treturn z.fromHex(hex)\n}\n\n// fromHex is the internal implementation of parsing a hex-string.\nfunc (z *Uint) fromHex(hex string) error {\n\tif err := checkNumberS(hex); err != nil {\n\t\treturn err\n\t}\n\tif len(hex) \u003e 66 {\n\t\treturn ErrBig256Range\n\t}\n\tz.Clear()\n\tend := len(hex)\n\tfor i := 0; i \u003c 4; i++ {\n\t\tstart := end - 16\n\t\tif start \u003c 2 {\n\t\t\tstart = 2\n\t\t}\n\t\tfor ri := start; ri \u003c end; ri++ {\n\t\t\tnib := bintable[hex[ri]]\n\t\t\tif nib == badNibble {\n\t\t\t\treturn ErrSyntax\n\t\t\t}\n\t\t\tz.arr[i] = z.arr[i] \u003c\u003c 4\n\t\t\tz.arr[i] += uint64(nib)\n\t\t}\n\t\tend = start\n\t}\n\treturn nil\n}\n\n// FromHex is a convenience-constructor to create an Uint from\n// a hexadecimal string. The string is required to be '0x'-prefixed\n// Numbers larger than 256 bits are not accepted.\nfunc FromHex(hex string) (*Uint, error) {\n\tvar z Uint\n\tif err := z.fromHex(hex); err != nil {\n\t\treturn nil, err\n\t}\n\treturn \u0026z, nil\n}\n\n// MustFromHex is a convenience-constructor to create an Uint from\n// a hexadecimal string.\n// Returns a new Uint and panics if any error occurred.\nfunc MustFromHex(hex string) *Uint {\n\tvar z Uint\n\tif err := z.fromHex(hex); err != nil {\n\t\tpanic(err)\n\t}\n\treturn \u0026z\n}\n\n// Clone creates a new Uint identical to z\nfunc (z *Uint) Clone() *Uint {\n\tvar x Uint\n\tx.arr[0] = z.arr[0]\n\tx.arr[1] = z.arr[1]\n\tx.arr[2] = z.arr[2]\n\tx.arr[3] = z.arr[3]\n\n\treturn \u0026x\n}\n" + }, + { + "name": "utils.gno", + "body": "package uint256\n\n// lower(c) is a lower-case letter if and only if\n// c is either that lower-case letter or the equivalent upper-case letter.\n// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'.\n// Note that lower of non-letters can produce other non-letters.\nfunc lower(c byte) byte {\n\treturn c | ('x' - 'X')\n}\n\n// underscoreOK reports whether the underscores in s are allowed.\n// Checking them in this one function lets all the parsers skip over them simply.\n// Underscore must appear only between digits or between a base prefix and a digit.\nfunc underscoreOK(s string) bool {\n\t// saw tracks the last character (class) we saw:\n\t// ^ for beginning of number,\n\t// 0 for a digit or base prefix,\n\t// _ for an underscore,\n\t// ! for none of the above.\n\tsaw := '^'\n\ti := 0\n\n\t// Optional sign.\n\tif len(s) \u003e= 1 \u0026\u0026 (s[0] == '-' || s[0] == '+') {\n\t\ts = s[1:]\n\t}\n\n\t// Optional base prefix.\n\thex := false\n\tif len(s) \u003e= 2 \u0026\u0026 s[0] == '0' \u0026\u0026 (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') {\n\t\ti = 2\n\t\tsaw = '0' // base prefix counts as a digit for \"underscore as digit separator\"\n\t\thex = lower(s[1]) == 'x'\n\t}\n\n\t// Number proper.\n\tfor ; i \u003c len(s); i++ {\n\t\t// Digits are always okay.\n\t\tif '0' \u003c= s[i] \u0026\u0026 s[i] \u003c= '9' || hex \u0026\u0026 'a' \u003c= lower(s[i]) \u0026\u0026 lower(s[i]) \u003c= 'f' {\n\t\t\tsaw = '0'\n\t\t\tcontinue\n\t\t}\n\t\t// Underscore must follow digit.\n\t\tif s[i] == '_' {\n\t\t\tif saw != '0' {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tsaw = '_'\n\t\t\tcontinue\n\t\t}\n\t\t// Underscore must also be followed by digit.\n\t\tif saw == '_' {\n\t\t\treturn false\n\t\t}\n\t\t// Saw non-digit, non-underscore.\n\t\tsaw = '!'\n\t}\n\treturn saw != '_'\n}\n\nfunc checkNumberS(input string) error {\n\tconst fn = \"UnmarshalText\"\n\tl := len(input)\n\tif l == 0 {\n\t\treturn errEmptyString(fn, input)\n\t}\n\tif l \u003c 2 || input[0] != '0' ||\n\t\t(input[1] != 'x' \u0026\u0026 input[1] != 'X') {\n\t\treturn errMissingPrefix(fn, input)\n\t}\n\tif l == 2 {\n\t\treturn errEmptyNumber(fn, input)\n\t}\n\tif len(input) \u003e 3 \u0026\u0026 input[2] == '0' {\n\t\treturn errLeadingZero(fn, input)\n\t}\n\treturn nil\n}\n\n// ParseUint is like ParseUint but for unsigned numbers.\n//\n// A sign prefix is not permitted.\nfunc parseUint(s string, base int, bitSize int) (uint64, error) {\n\tconst fnParseUint = \"ParseUint\"\n\n\tif s == \"\" {\n\t\treturn 0, errSyntax(fnParseUint, s)\n\t}\n\n\tbase0 := base == 0\n\n\ts0 := s\n\tswitch {\n\tcase 2 \u003c= base \u0026\u0026 base \u003c= 36:\n\t\t// valid base; nothing to do\n\n\tcase base == 0:\n\t\t// Look for octal, hex prefix.\n\t\tbase = 10\n\t\tif s[0] == '0' {\n\t\t\tswitch {\n\t\t\tcase len(s) \u003e= 3 \u0026\u0026 lower(s[1]) == 'b':\n\t\t\t\tbase = 2\n\t\t\t\ts = s[2:]\n\t\t\tcase len(s) \u003e= 3 \u0026\u0026 lower(s[1]) == 'o':\n\t\t\t\tbase = 8\n\t\t\t\ts = s[2:]\n\t\t\tcase len(s) \u003e= 3 \u0026\u0026 lower(s[1]) == 'x':\n\t\t\t\tbase = 16\n\t\t\t\ts = s[2:]\n\t\t\tdefault:\n\t\t\t\tbase = 8\n\t\t\t\ts = s[1:]\n\t\t\t}\n\t\t}\n\n\tdefault:\n\t\treturn 0, errInvalidBase(fnParseUint, base)\n\t}\n\n\tif bitSize == 0 {\n\t\tbitSize = uintSize\n\t} else if bitSize \u003c 0 || bitSize \u003e 64 {\n\t\treturn 0, errInvalidBitSize(fnParseUint, bitSize)\n\t}\n\n\t// Cutoff is the smallest number such that cutoff*base \u003e maxUint64.\n\t// Use compile-time constants for common cases.\n\tvar cutoff uint64\n\tswitch base {\n\tcase 10:\n\t\tcutoff = MaxUint64/10 + 1\n\tcase 16:\n\t\tcutoff = MaxUint64/16 + 1\n\tdefault:\n\t\tcutoff = MaxUint64/uint64(base) + 1\n\t}\n\n\tmaxVal := uint64(1)\u003c\u003cuint(bitSize) - 1\n\n\tunderscores := false\n\tvar n uint64\n\tfor _, c := range []byte(s) {\n\t\tvar d byte\n\t\tswitch {\n\t\tcase c == '_' \u0026\u0026 base0:\n\t\t\tunderscores = true\n\t\t\tcontinue\n\t\tcase '0' \u003c= c \u0026\u0026 c \u003c= '9':\n\t\t\td = c - '0'\n\t\tcase 'a' \u003c= lower(c) \u0026\u0026 lower(c) \u003c= 'z':\n\t\t\td = lower(c) - 'a' + 10\n\t\tdefault:\n\t\t\treturn 0, errSyntax(fnParseUint, s0)\n\t\t}\n\n\t\tif d \u003e= byte(base) {\n\t\t\treturn 0, errSyntax(fnParseUint, s0)\n\t\t}\n\n\t\tif n \u003e= cutoff {\n\t\t\t// n*base overflows\n\t\t\treturn maxVal, errRange(fnParseUint, s0)\n\t\t}\n\t\tn *= uint64(base)\n\n\t\tn1 := n + uint64(d)\n\t\tif n1 \u003c n || n1 \u003e maxVal {\n\t\t\t// n+d overflows\n\t\t\treturn maxVal, errRange(fnParseUint, s0)\n\t\t}\n\t\tn = n1\n\t}\n\n\tif underscores \u0026\u0026 !underscoreOK(s0) {\n\t\treturn 0, errSyntax(fnParseUint, s0)\n\t}\n\n\treturn n, nil\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "int256", + "path": "gno.land/p/demo/int256", + "files": [ + { + "name": "LICENSE", + "body": "MIT License\n\nCopyright (c) 2023 Trịnh Đức Bảo Linh(Kevin)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." + }, + { + "name": "README.md", + "body": "# Fixed size signed 256-bit math library\n\n1. This is a library specialized at replacing the big.Int library for math based on signed 256-bit types.\n2. It uses [uint256](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo/uint256) as the underlying type.\n\nported from [mempooler/int256](https://github.com/mempooler/int256)\n" + }, + { + "name": "absolute.gno", + "body": "package int256\n\nimport \"gno.land/p/demo/uint256\"\n\n// Abs returns |z|\nfunc (z *Int) Abs() *uint256.Uint {\n\treturn z.abs.Clone()\n}\n\n// AbsGt returns true if |z| \u003e x, where x is a uint256\nfunc (z *Int) AbsGt(x *uint256.Uint) bool {\n\treturn z.abs.Gt(x)\n}\n\n// AbsLt returns true if |z| \u003c x, where x is a uint256\nfunc (z *Int) AbsLt(x *uint256.Uint) bool {\n\treturn z.abs.Lt(x)\n}\n" + }, + { + "name": "absolute_test.gno", + "body": "package int256\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/uint256\"\n)\n\nfunc TestAbs(t *testing.T) {\n\ttests := []struct {\n\t\tx, want string\n\t}{\n\t\t{\"0\", \"0\"},\n\t\t{\"1\", \"1\"},\n\t\t{\"-1\", \"1\"},\n\t\t{\"-2\", \"2\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.Abs()\n\n\t\tif got.ToString() != tc.want {\n\t\t\tt.Errorf(\"Abs(%s) = %v, want %v\", tc.x, got.ToString(), tc.want)\n\t\t}\n\t}\n}\n\nfunc TestAbsGt(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"0\", \"false\"},\n\t\t{\"1\", \"0\", \"true\"},\n\t\t{\"-1\", \"0\", \"true\"},\n\t\t{\"-1\", \"1\", \"false\"},\n\t\t{\"-2\", \"1\", \"true\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"0\", \"true\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"1\", \"true\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"false\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := uint256.FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.AbsGt(y)\n\n\t\tif got != (tc.want == \"true\") {\n\t\t\tt.Errorf(\"AbsGt(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestAbsLt(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"0\", \"false\"},\n\t\t{\"1\", \"0\", \"false\"},\n\t\t{\"-1\", \"0\", \"false\"},\n\t\t{\"-1\", \"1\", \"false\"},\n\t\t{\"-2\", \"1\", \"false\"},\n\t\t{\"-5\", \"10\", \"true\"},\n\t\t{\"31330\", \"31337\", \"true\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"0\", \"false\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"1\", \"false\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"false\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := uint256.FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.AbsLt(y)\n\n\t\tif got != (tc.want == \"true\") {\n\t\t\tt.Errorf(\"AbsLt(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n" + }, + { + "name": "arithmetic.gno", + "body": "package int256\n\nimport \"gno.land/p/demo/uint256\"\n\nfunc (z *Int) Add(x, y *Int) *Int {\n\tz.initiateAbs()\n\n\tneg := x.neg\n\n\tif x.neg == y.neg {\n\t\t// x + y == x + y\n\t\t// (-x) + (-y) == -(x + y)\n\t\tz.abs = z.abs.Add(x.abs, y.abs)\n\t} else {\n\t\t// x + (-y) == x - y == -(y - x)\n\t\t// (-x) + y == y - x == -(x - y)\n\t\tif x.abs.Cmp(y.abs) \u003e= 0 {\n\t\t\tz.abs = z.abs.Sub(x.abs, y.abs)\n\t\t} else {\n\t\t\tneg = !neg\n\t\t\tz.abs = z.abs.Sub(y.abs, x.abs)\n\t\t}\n\t}\n\tz.neg = neg // 0 has no sign\n\treturn z\n}\n\n// AddUint256 set z to the sum x + y, where y is a uint256, and returns z\nfunc (z *Int) AddUint256(x *Int, y *uint256.Uint) *Int {\n\tif x.neg {\n\t\tif x.abs.Gt(y) {\n\t\t\tz.abs.Sub(x.abs, y)\n\t\t\tz.neg = true\n\t\t} else {\n\t\t\tz.abs.Sub(y, x.abs)\n\t\t\tz.neg = false\n\t\t}\n\t} else {\n\t\tz.abs.Add(x.abs, y)\n\t\tz.neg = false\n\t}\n\treturn z\n}\n\n// Sets z to the sum x + y, where z and x are uint256s and y is an int256.\nfunc AddDelta(z, x *uint256.Uint, y *Int) {\n\tif y.neg {\n\t\tz.Sub(x, y.abs)\n\t} else {\n\t\tz.Add(x, y.abs)\n\t}\n}\n\n// Sets z to the sum x + y, where z and x are uint256s and y is an int256.\nfunc AddDeltaOverflow(z, x *uint256.Uint, y *Int) bool {\n\tvar overflow bool\n\tif y.neg {\n\t\t_, overflow = z.SubOverflow(x, y.abs)\n\t} else {\n\t\t_, overflow = z.AddOverflow(x, y.abs)\n\t}\n\treturn overflow\n}\n\n// Sub sets z to the difference x-y and returns z.\nfunc (z *Int) Sub(x, y *Int) *Int {\n\tz.initiateAbs()\n\n\tneg := x.neg\n\tif x.neg != y.neg {\n\t\t// x - (-y) == x + y\n\t\t// (-x) - y == -(x + y)\n\t\tz.abs = z.abs.Add(x.abs, y.abs)\n\t} else {\n\t\t// x - y == x - y == -(y - x)\n\t\t// (-x) - (-y) == y - x == -(x - y)\n\t\tif x.abs.Cmp(y.abs) \u003e= 0 {\n\t\t\tz.abs = z.abs.Sub(x.abs, y.abs)\n\t\t} else {\n\t\t\tneg = !neg\n\t\t\tz.abs = z.abs.Sub(y.abs, x.abs)\n\t\t}\n\t}\n\tz.neg = neg // 0 has no sign\n\treturn z\n}\n\n// SubUint256 set z to the difference x - y, where y is a uint256, and returns z\nfunc (z *Int) SubUint256(x *Int, y *uint256.Uint) *Int {\n\tif x.neg {\n\t\tz.abs.Add(x.abs, y)\n\t\tz.neg = true\n\t} else {\n\t\tif x.abs.Lt(y) {\n\t\t\tz.abs.Sub(y, x.abs)\n\t\t\tz.neg = true\n\t\t} else {\n\t\t\tz.abs.Sub(x.abs, y)\n\t\t\tz.neg = false\n\t\t}\n\t}\n\treturn z\n}\n\n// Mul sets z to the product x*y and returns z.\nfunc (z *Int) Mul(x, y *Int) *Int {\n\tz.initiateAbs()\n\n\tz.abs = z.abs.Mul(x.abs, y.abs)\n\tz.neg = x.neg != y.neg // 0 has no sign\n\treturn z\n}\n\n// MulUint256 sets z to the product x*y, where y is a uint256, and returns z\nfunc (z *Int) MulUint256(x *Int, y *uint256.Uint) *Int {\n\tz.abs.Mul(x.abs, y)\n\tif z.abs.IsZero() {\n\t\tz.neg = false\n\t} else {\n\t\tz.neg = x.neg\n\t}\n\treturn z\n}\n\n// Div sets z to the quotient x/y for y != 0 and returns z.\nfunc (z *Int) Div(x, y *Int) *Int {\n\tz.initiateAbs()\n\n\tz.abs.Div(x.abs, y.abs)\n\tif x.neg == y.neg {\n\t\tz.neg = false\n\t} else {\n\t\tz.neg = true\n\t}\n\treturn z\n}\n\n// DivUint256 sets z to the quotient x/y, where y is a uint256, and returns z\n// If y == 0, z is set to 0\nfunc (z *Int) DivUint256(x *Int, y *uint256.Uint) *Int {\n\tz.abs.Div(x.abs, y)\n\tif z.abs.IsZero() {\n\t\tz.neg = false\n\t} else {\n\t\tz.neg = x.neg\n\t}\n\treturn z\n}\n\n// Quo sets z to the quotient x/y for y != 0 and returns z.\n// If y == 0, a division-by-zero run-time panic occurs.\n// OBS: differs from mempooler int256, we need to panic manually if y == 0\n// Quo implements truncated division (like Go); see QuoRem for more details.\nfunc (z *Int) Quo(x, y *Int) *Int {\n\tif y.IsZero() {\n\t\tpanic(\"division by zero\")\n\t}\n\n\tz.initiateAbs()\n\n\tz.abs = z.abs.Div(x.abs, y.abs)\n\tz.neg = !(z.abs.IsZero()) \u0026\u0026 x.neg != y.neg // 0 has no sign\n\treturn z\n}\n\n// Rem sets z to the remainder x%y for y != 0 and returns z.\n// If y == 0, a division-by-zero run-time panic occurs.\n// OBS: differs from mempooler int256, we need to panic manually if y == 0\n// Rem implements truncated modulus (like Go); see QuoRem for more details.\nfunc (z *Int) Rem(x, y *Int) *Int {\n\tif y.IsZero() {\n\t\tpanic(\"division by zero\")\n\t}\n\n\tz.initiateAbs()\n\n\tz.abs.Mod(x.abs, y.abs)\n\tz.neg = z.abs.Sign() \u003e 0 \u0026\u0026 x.neg // 0 has no sign\n\treturn z\n}\n\n// Mod sets z to the modulus x%y for y != 0 and returns z.\n// If y == 0, z is set to 0 (OBS: differs from the big.Int)\nfunc (z *Int) Mod(x, y *Int) *Int {\n\tif x.neg {\n\t\tz.abs.Div(x.abs, y.abs)\n\t\tz.abs.Add(z.abs, one)\n\t\tz.abs.Mul(z.abs, y.abs)\n\t\tz.abs.Sub(z.abs, x.abs)\n\t\tz.abs.Mod(z.abs, y.abs)\n\t} else {\n\t\tz.abs.Mod(x.abs, y.abs)\n\t}\n\tz.neg = false\n\treturn z\n}\n" + }, + { + "name": "arithmetic_test.gno", + "body": "package int256\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/uint256\"\n)\n\nfunc TestAdd(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"1\"},\n\t\t{\"1\", \"0\", \"1\"},\n\t\t{\"1\", \"1\", \"2\"},\n\t\t{\"1\", \"2\", \"3\"},\n\t\t// NEGATIVE\n\t\t{\"-1\", \"1\", \"-0\"}, // TODO: remove negative sign for 0 ??\n\t\t{\"1\", \"-1\", \"0\"},\n\t\t{\"-1\", \"-1\", \"-2\"},\n\t\t{\"-1\", \"-2\", \"-3\"},\n\t\t{\"-1\", \"3\", \"2\"},\n\t\t{\"3\", \"-1\", \"2\"},\n\t\t// OVERFLOW\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"1\", \"0\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Add(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Add(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestAddUint256(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"1\"},\n\t\t{\"1\", \"0\", \"1\"},\n\t\t{\"1\", \"1\", \"2\"},\n\t\t{\"1\", \"2\", \"3\"},\n\t\t{\"-1\", \"1\", \"0\"},\n\t\t{\"-1\", \"3\", \"2\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639934\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"1\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"115792089237316195423570985008687907853269984665640564039457584007913129639934\", \"-1\"},\n\t\t// OVERFLOW\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"0\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := uint256.FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.AddUint256(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"AddUint256(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestAddDelta(t *testing.T) {\n\ttests := []struct {\n\t\tz, x, y, want string\n\t}{\n\t\t{\"0\", \"0\", \"0\", \"0\"},\n\t\t{\"0\", \"0\", \"1\", \"1\"},\n\t\t{\"0\", \"1\", \"0\", \"1\"},\n\t\t{\"0\", \"1\", \"1\", \"2\"},\n\t\t{\"1\", \"2\", \"3\", \"5\"},\n\t\t{\"5\", \"10\", \"-3\", \"7\"},\n\t\t// underflow\n\t\t{\"1\", \"2\", \"-3\", \"115792089237316195423570985008687907853269984665640564039457584007913129639935\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz, err := uint256.FromDecimal(tc.z)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tx, err := uint256.FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := uint256.FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tAddDelta(z, x, y)\n\n\t\tif z.Neq(want) {\n\t\t\tt.Errorf(\"AddDelta(%s, %s, %s) = %v, want %v\", tc.z, tc.x, tc.y, z.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestAddDeltaOverflow(t *testing.T) {\n\ttests := []struct {\n\t\tz, x, y string\n\t\twant bool\n\t}{\n\t\t{\"0\", \"0\", \"0\", false},\n\t\t// underflow\n\t\t{\"1\", \"2\", \"-3\", true},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz, err := uint256.FromDecimal(tc.z)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tx, err := uint256.FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tresult := AddDeltaOverflow(z, x, y)\n\t\tif result != tc.want {\n\t\t\tt.Errorf(\"AddDeltaOverflow(%s, %s, %s) = %v, want %v\", tc.z, tc.x, tc.y, result, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestSub(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"1\", \"0\", \"1\"},\n\t\t{\"1\", \"1\", \"0\"},\n\t\t{\"-1\", \"1\", \"-2\"},\n\t\t{\"1\", \"-1\", \"2\"},\n\t\t{\"-1\", \"-1\", \"-0\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-0\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"0\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\"},\n\t\t{x: \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", y: \"1\", want: \"-0\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Sub(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Sub(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestSubUint256(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"-1\"},\n\t\t{\"1\", \"0\", \"1\"},\n\t\t{\"1\", \"1\", \"0\"},\n\t\t{\"1\", \"2\", \"-1\"},\n\t\t{\"-1\", \"1\", \"-2\"},\n\t\t{\"-1\", \"3\", \"-4\"},\n\t\t// underflow\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"1\", \"-0\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"2\", \"-1\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"3\", \"-2\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := uint256.FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.SubUint256(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"SubUint256(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestMul(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"5\", \"3\", \"15\"},\n\t\t{\"-5\", \"3\", \"-15\"},\n\t\t{\"5\", \"-3\", \"-15\"},\n\t\t{\"0\", \"3\", \"0\"},\n\t\t{\"3\", \"0\", \"0\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Mul(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Mul(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestMulUint256(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"0\"},\n\t\t{\"1\", \"0\", \"0\"},\n\t\t{\"1\", \"1\", \"1\"},\n\t\t{\"1\", \"2\", \"2\"},\n\t\t{\"-1\", \"1\", \"-1\"},\n\t\t{\"-1\", \"3\", \"-3\"},\n\t\t{\"3\", \"4\", \"12\"},\n\t\t{\"-3\", \"4\", \"-12\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639934\", \"2\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639932\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639934\", \"2\", \"115792089237316195423570985008687907853269984665640564039457584007913129639932\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := uint256.FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.MulUint256(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"MulUint256(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestDiv(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"0\"},\n\t\t{\"0\", \"-1\", \"-0\"},\n\t\t{\"10\", \"0\", \"0\"},\n\t\t{\"10\", \"1\", \"10\"},\n\t\t{\"10\", \"-1\", \"-10\"},\n\t\t{\"-10\", \"0\", \"-0\"},\n\t\t{\"-10\", \"1\", \"-10\"},\n\t\t{\"-10\", \"-1\", \"10\"},\n\t\t{\"10\", \"-3\", \"-3\"},\n\t\t{\"10\", \"3\", \"3\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Div(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Div(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestDivUint256(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"0\"},\n\t\t{\"1\", \"0\", \"0\"},\n\t\t{\"1\", \"1\", \"1\"},\n\t\t{\"1\", \"2\", \"0\"},\n\t\t{\"-1\", \"1\", \"-1\"},\n\t\t{\"-1\", \"3\", \"0\"},\n\t\t{\"4\", \"3\", \"1\"},\n\t\t{\"25\", \"5\", \"5\"},\n\t\t{\"25\", \"4\", \"6\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639934\", \"2\", \"-57896044618658097711785492504343953926634992332820282019728792003956564819967\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639934\", \"2\", \"57896044618658097711785492504343953926634992332820282019728792003956564819967\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := uint256.FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.DivUint256(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"DivUint256(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestQuo(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"0\"},\n\t\t{\"0\", \"-1\", \"0\"},\n\t\t{\"10\", \"1\", \"10\"},\n\t\t{\"10\", \"-1\", \"-10\"},\n\t\t{\"-10\", \"1\", \"-10\"},\n\t\t{\"-10\", \"-1\", \"10\"},\n\t\t{\"10\", \"-3\", \"-3\"},\n\t\t{\"10\", \"3\", \"3\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Quo(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Quo(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestRem(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"0\"},\n\t\t{\"0\", \"-1\", \"0\"},\n\t\t{\"10\", \"1\", \"0\"},\n\t\t{\"10\", \"-1\", \"0\"},\n\t\t{\"-10\", \"1\", \"0\"},\n\t\t{\"-10\", \"-1\", \"0\"},\n\t\t{\"10\", \"3\", \"1\"},\n\t\t{\"10\", \"-3\", \"1\"},\n\t\t{\"-10\", \"3\", \"-1\"},\n\t\t{\"-10\", \"-3\", \"-1\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Rem(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Rem(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n\nfunc TestMod(t *testing.T) {\n\ttests := []struct {\n\t\tx, y, want string\n\t}{\n\t\t{\"0\", \"1\", \"0\"},\n\t\t{\"0\", \"-1\", \"0\"},\n\t\t{\"10\", \"0\", \"0\"},\n\t\t{\"10\", \"1\", \"0\"},\n\t\t{\"10\", \"-1\", \"0\"},\n\t\t{\"-10\", \"0\", \"0\"},\n\t\t{\"-10\", \"1\", \"0\"},\n\t\t{\"-10\", \"-1\", \"0\"},\n\t\t{\"10\", \"3\", \"1\"},\n\t\t{\"10\", \"-3\", \"1\"},\n\t\t{\"-10\", \"3\", \"2\"},\n\t\t{\"-10\", \"-3\", \"2\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\twant, err := FromDecimal(tc.want)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Mod(x, y)\n\n\t\tif got.Neq(want) {\n\t\t\tt.Errorf(\"Mod(%s, %s) = %v, want %v\", tc.x, tc.y, got.ToString(), want.ToString())\n\t\t}\n\t}\n}\n" + }, + { + "name": "bitwise.gno", + "body": "package int256\n\nimport (\n\t\"gno.land/p/demo/uint256\"\n)\n\n// Or sets z = x | y and returns z.\nfunc (z *Int) Or(x, y *Int) *Int {\n\tif x.neg == y.neg {\n\t\tif x.neg {\n\t\t\t// (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) \u0026 (y-1)) == -(((x-1) \u0026 (y-1)) + 1)\n\t\t\tx1 := new(uint256.Uint).Sub(x.abs, one)\n\t\t\ty1 := new(uint256.Uint).Sub(y.abs, one)\n\t\t\tz.abs = z.abs.Add(z.abs.And(x1, y1), one)\n\t\t\tz.neg = true // z cannot be zero if x and y are negative\n\t\t\treturn z\n\t\t}\n\n\t\t// x | y == x | y\n\t\tz.abs = z.abs.Or(x.abs, y.abs)\n\t\tz.neg = false\n\t\treturn z\n\t}\n\n\t// x.neg != y.neg\n\tif x.neg {\n\t\tx, y = y, x // | is symmetric\n\t}\n\n\t// x | (-y) == x | ^(y-1) == ^((y-1) \u0026^ x) == -(^((y-1) \u0026^ x) + 1)\n\ty1 := new(uint256.Uint).Sub(y.abs, one)\n\tz.abs = z.abs.Add(z.abs.AndNot(y1, x.abs), one)\n\tz.neg = true // z cannot be zero if one of x or y is negative\n\n\treturn z\n}\n\n// And sets z = x \u0026 y and returns z.\nfunc (z *Int) And(x, y *Int) *Int {\n\tif x.neg == y.neg {\n\t\tif x.neg {\n\t\t\t// (-x) \u0026 (-y) == ^(x-1) \u0026 ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1)\n\t\t\tx1 := new(uint256.Uint).Sub(x.abs, one)\n\t\t\ty1 := new(uint256.Uint).Sub(y.abs, one)\n\t\t\tz.abs = z.abs.Add(z.abs.Or(x1, y1), one)\n\t\t\tz.neg = true // z cannot be zero if x and y are negative\n\t\t\treturn z\n\t\t}\n\n\t\t// x \u0026 y == x \u0026 y\n\t\tz.abs = z.abs.And(x.abs, y.abs)\n\t\tz.neg = false\n\t\treturn z\n\t}\n\n\t// x.neg != y.neg\n\t// REF: https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/math/big/int.go;l=1192-1202;drc=d57303e65f00b84b528ee682747dbe1fd3316d30\n\tif x.neg {\n\t\tx, y = y, x // \u0026 is symmetric\n\t}\n\n\t// x \u0026 (-y) == x \u0026 ^(y-1) == x \u0026^ (y-1)\n\ty1 := new(uint256.Uint).Sub(y.abs, uint256.One())\n\tz.abs = z.abs.AndNot(x.abs, y1)\n\tz.neg = false\n\treturn z\n}\n\n// Rsh sets z = x \u003e\u003e n and returns z.\n// OBS: Different from original implementation it was using math.Big\nfunc (z *Int) Rsh(x *Int, n uint) *Int {\n\tif !x.neg {\n\t\tz.abs.Rsh(x.abs, n)\n\t\tz.neg = x.neg\n\t\treturn z\n\t}\n\n\t// REF: https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/math/big/int.go;l=1118-1126;drc=d57303e65f00b84b528ee682747dbe1fd3316d30\n\tt := NewInt(0).Sub(FromUint256(x.abs), NewInt(1))\n\tt = t.Rsh(t, n)\n\n\t_tmp := t.Add(t, NewInt(1))\n\tz.abs = _tmp.Abs()\n\tz.neg = true\n\n\treturn z\n}\n\n// Lsh sets z = x \u003c\u003c n and returns z.\nfunc (z *Int) Lsh(x *Int, n uint) *Int {\n\tz.abs.Lsh(x.abs, n)\n\tz.neg = x.neg\n\treturn z\n}\n" + }, + { + "name": "bitwise_test.gno", + "body": "package int256\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/uint256\"\n)\n\nfunc TestOr(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tx, y, want Int\n\t}{\n\t\t{\n\t\t\tname: \"all zeroes\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"all ones\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand all ones\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := New()\n\t\t\tgot.Or(\u0026tc.x, \u0026tc.y)\n\n\t\t\tif got.Neq(\u0026tc.want) {\n\t\t\t\tt.Errorf(\"Or(%v, %v) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAnd(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tx, y, want Int\n\t}{\n\t\t{\n\t\t\tname: \"all zeroes\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"all ones\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed 2\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed 3\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand zero\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false},\n\t\t},\n\t\t{\n\t\t\tname: \"one operand all ones\",\n\t\t\tx: Int{abs: \u0026uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false},\n\t\t\ty: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false},\n\t\t\twant: Int{abs: \u0026uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := New()\n\t\t\tgot.And(\u0026tc.x, \u0026tc.y)\n\n\t\t\tif got.Neq(\u0026tc.want) {\n\t\t\t\tt.Errorf(\"And(%v, %v) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRsh(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\tn uint\n\t\twant string\n\t}{\n\t\t{\"1024\", 0, \"1024\"},\n\t\t{\"1024\", 1, \"512\"},\n\t\t{\"1024\", 2, \"256\"},\n\t\t{\"1024\", 10, \"1\"},\n\t\t{\"1024\", 11, \"0\"},\n\t\t{\"18446744073709551615\", 0, \"18446744073709551615\"},\n\t\t{\"18446744073709551615\", 1, \"9223372036854775807\"},\n\t\t{\"18446744073709551615\", 62, \"3\"},\n\t\t{\"18446744073709551615\", 63, \"1\"},\n\t\t{\"18446744073709551615\", 64, \"0\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", 0, \"115792089237316195423570985008687907853269984665640564039457584007913129639935\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", 1, \"57896044618658097711785492504343953926634992332820282019728792003956564819967\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", 128, \"340282366920938463463374607431768211455\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", 255, \"1\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", 256, \"0\"},\n\t\t{\"-1024\", 0, \"-1024\"},\n\t\t{\"-1024\", 1, \"-512\"},\n\t\t{\"-1024\", 2, \"-256\"},\n\t\t{\"-1024\", 10, \"-1\"},\n\t\t{\"-1024\", 10, \"-1\"},\n\t\t{\"-9223372036854775808\", 0, \"-9223372036854775808\"},\n\t\t{\"-9223372036854775808\", 1, \"-4611686018427387904\"},\n\t\t{\"-9223372036854775808\", 62, \"-2\"},\n\t\t{\"-9223372036854775808\", 63, \"-1\"},\n\t\t{\"-9223372036854775808\", 64, \"-1\"},\n\t\t{\"-57896044618658097711785492504343953926634992332820282019728792003956564819968\", 0, \"-57896044618658097711785492504343953926634992332820282019728792003956564819968\"},\n\t\t{\"-57896044618658097711785492504343953926634992332820282019728792003956564819968\", 1, \"-28948022309329048855892746252171976963317496166410141009864396001978282409984\"},\n\t\t{\"-57896044618658097711785492504343953926634992332820282019728792003956564819968\", 253, \"-4\"},\n\t\t{\"-57896044618658097711785492504343953926634992332820282019728792003956564819968\", 254, \"-2\"},\n\t\t{\"-57896044618658097711785492504343953926634992332820282019728792003956564819968\", 255, \"-1\"},\n\t\t{\"-57896044618658097711785492504343953926634992332820282019728792003956564819968\", 256, \"-1\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Rsh(x, tc.n)\n\n\t\tif got.ToString() != tc.want {\n\t\t\tt.Errorf(\"Rsh(%s, %d) = %v, want %v\", tc.x, tc.n, got.ToString(), tc.want)\n\t\t}\n\t}\n}\n\nfunc TestLsh(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\tn uint\n\t\twant string\n\t}{\n\t\t{\"1\", 0, \"1\"},\n\t\t{\"1\", 1, \"2\"},\n\t\t{\"1\", 2, \"4\"},\n\t\t{\"2\", 0, \"2\"},\n\t\t{\"2\", 1, \"4\"},\n\t\t{\"2\", 2, \"8\"},\n\t\t{\"-2\", 0, \"-2\"},\n\t\t{\"-4\", 0, \"-4\"},\n\t\t{\"-8\", 0, \"-8\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := New()\n\t\tgot.Lsh(x, tc.n)\n\n\t\tif got.ToString() != tc.want {\n\t\t\tt.Errorf(\"Lsh(%s, %d) = %v, want %v\", tc.x, tc.n, got.ToString(), tc.want)\n\t\t}\n\t}\n}\n" + }, + { + "name": "cmp.gno", + "body": "package int256\n\n// Eq returns true if z == x\nfunc (z *Int) Eq(x *Int) bool {\n\treturn (z.neg == x.neg) \u0026\u0026 z.abs.Eq(x.abs)\n}\n\n// Neq returns true if z != x\nfunc (z *Int) Neq(x *Int) bool {\n\treturn !z.Eq(x)\n}\n\n// Cmp compares x and y and returns:\n//\n//\t-1 if x \u003c y\n//\t 0 if x == y\n//\t+1 if x \u003e y\nfunc (z *Int) Cmp(x *Int) (r int) {\n\t// x cmp y == x cmp y\n\t// x cmp (-y) == x\n\t// (-x) cmp y == y\n\t// (-x) cmp (-y) == -(x cmp y)\n\tswitch {\n\tcase z == x:\n\t\t// nothing to do\n\tcase z.neg == x.neg:\n\t\tr = z.abs.Cmp(x.abs)\n\t\tif z.neg {\n\t\t\tr = -r\n\t\t}\n\tcase z.neg:\n\t\tr = -1\n\tdefault:\n\t\tr = 1\n\t}\n\treturn\n}\n\n// IsZero returns true if z == 0\nfunc (z *Int) IsZero() bool {\n\treturn z.abs.IsZero()\n}\n\n// IsNeg returns true if z \u003c 0\nfunc (z *Int) IsNeg() bool {\n\treturn z.neg\n}\n\n// Lt returns true if z \u003c x\nfunc (z *Int) Lt(x *Int) bool {\n\tif z.neg {\n\t\tif x.neg {\n\t\t\treturn z.abs.Gt(x.abs)\n\t\t} else {\n\t\t\treturn true\n\t\t}\n\t} else {\n\t\tif x.neg {\n\t\t\treturn false\n\t\t} else {\n\t\t\treturn z.abs.Lt(x.abs)\n\t\t}\n\t}\n}\n\n// Gt returns true if z \u003e x\nfunc (z *Int) Gt(x *Int) bool {\n\tif z.neg {\n\t\tif x.neg {\n\t\t\treturn z.abs.Lt(x.abs)\n\t\t} else {\n\t\t\treturn false\n\t\t}\n\t} else {\n\t\tif x.neg {\n\t\t\treturn true\n\t\t} else {\n\t\t\treturn z.abs.Gt(x.abs)\n\t\t}\n\t}\n}\n\n// Clone creates a new Int identical to z\nfunc (z *Int) Clone() *Int {\n\treturn \u0026Int{z.abs.Clone(), z.neg}\n}\n" + }, + { + "name": "cmp_test.gno", + "body": "package int256\n\nimport \"testing\"\n\nfunc TestEq(t *testing.T) {\n\ttests := []struct {\n\t\tx, y string\n\t\twant bool\n\t}{\n\t\t{\"0\", \"0\", true},\n\t\t{\"0\", \"1\", false},\n\t\t{\"1\", \"0\", false},\n\t\t{\"-1\", \"0\", false},\n\t\t{\"0\", \"-1\", false},\n\t\t{\"1\", \"1\", true},\n\t\t{\"-1\", \"-1\", true},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", false},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", true},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.Eq(y)\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Eq(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestNeq(t *testing.T) {\n\ttests := []struct {\n\t\tx, y string\n\t\twant bool\n\t}{\n\t\t{\"0\", \"0\", false},\n\t\t{\"0\", \"1\", true},\n\t\t{\"1\", \"0\", true},\n\t\t{\"-1\", \"0\", true},\n\t\t{\"0\", \"-1\", true},\n\t\t{\"1\", \"1\", false},\n\t\t{\"-1\", \"-1\", false},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", true},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", false},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.Neq(y)\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Neq(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestCmp(t *testing.T) {\n\ttests := []struct {\n\t\tx, y string\n\t\twant int\n\t}{\n\t\t{\"0\", \"0\", 0},\n\t\t{\"0\", \"1\", -1},\n\t\t{\"1\", \"0\", 1},\n\t\t{\"-1\", \"0\", -1},\n\t\t{\"0\", \"-1\", 1},\n\t\t{\"1\", \"1\", 0},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", 1},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.Cmp(y)\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Cmp(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestIsZero(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant bool\n\t}{\n\t\t{\"0\", true},\n\t\t{\"-0\", true},\n\t\t{\"1\", false},\n\t\t{\"-1\", false},\n\t\t{\"10\", false},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.IsZero()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"IsZero(%s) = %v, want %v\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestIsNeg(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant bool\n\t}{\n\t\t{\"0\", false},\n\t\t{\"-0\", true}, // TODO: should this be false?\n\t\t{\"1\", false},\n\t\t{\"-1\", true},\n\t\t{\"10\", false},\n\t\t{\"-10\", true},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.IsNeg()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"IsNeg(%s) = %v, want %v\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestLt(t *testing.T) {\n\ttests := []struct {\n\t\tx, y string\n\t\twant bool\n\t}{\n\t\t{\"0\", \"0\", false},\n\t\t{\"0\", \"1\", true},\n\t\t{\"1\", \"0\", false},\n\t\t{\"-1\", \"0\", true},\n\t\t{\"0\", \"-1\", false},\n\t\t{\"1\", \"1\", false},\n\t\t{\"-1\", \"-1\", false},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", false},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.Lt(y)\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Lt(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestGt(t *testing.T) {\n\ttests := []struct {\n\t\tx, y string\n\t\twant bool\n\t}{\n\t\t{\"0\", \"0\", false},\n\t\t{\"0\", \"1\", false},\n\t\t{\"1\", \"0\", true},\n\t\t{\"-1\", \"0\", false},\n\t\t{\"0\", \"-1\", true},\n\t\t{\"1\", \"1\", false},\n\t\t{\"-1\", \"-1\", false},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", \"-115792089237316195423570985008687907853269984665640564039457584007913129639935\", true},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty, err := FromDecimal(tc.y)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := x.Gt(y)\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Gt(%s, %s) = %v, want %v\", tc.x, tc.y, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestClone(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t}{\n\t\t{\"0\"},\n\t\t{\"-0\"},\n\t\t{\"1\"},\n\t\t{\"-1\"},\n\t\t{\"10\"},\n\t\t{\"-10\"},\n\t\t{\"115792089237316195423570985008687907853269984665640564039457584007913129639935\"},\n\t\t{\"-115792089237316195423570985008687907853269984665640564039457584007913129639935\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tx, err := FromDecimal(tc.x)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t\tcontinue\n\t\t}\n\n\t\ty := x.Clone()\n\n\t\tif x.Cmp(y) != 0 {\n\t\t\tt.Errorf(\"Clone(%s) = %v, want %v\", tc.x, y, x)\n\t\t}\n\t}\n}\n" + }, + { + "name": "conversion.gno", + "body": "package int256\n\nimport \"gno.land/p/demo/uint256\"\n\n// SetInt64 sets z to x and returns z.\nfunc (z *Int) SetInt64(x int64) *Int {\n\tz.initiateAbs()\n\n\tneg := false\n\tif x \u003c 0 {\n\t\tneg = true\n\t\tx = -x\n\t}\n\tif z.abs == nil {\n\t\tpanic(\"abs is nil\")\n\t}\n\tz.abs = z.abs.SetUint64(uint64(x))\n\tz.neg = neg\n\treturn z\n}\n\n// SetUint64 sets z to x and returns z.\nfunc (z *Int) SetUint64(x uint64) *Int {\n\tz.initiateAbs()\n\n\tif z.abs == nil {\n\t\tpanic(\"abs is nil\")\n\t}\n\tz.abs = z.abs.SetUint64(x)\n\tz.neg = false\n\treturn z\n}\n\n// Uint64 returns the lower 64-bits of z\nfunc (z *Int) Uint64() uint64 {\n\treturn z.abs.Uint64()\n}\n\n// Int64 returns the lower 64-bits of z\nfunc (z *Int) Int64() int64 {\n\t_abs := z.abs.Clone()\n\n\tif z.neg {\n\t\treturn -int64(_abs.Uint64())\n\t}\n\treturn int64(_abs.Uint64())\n}\n\n// Neg sets z to -x and returns z.)\nfunc (z *Int) Neg(x *Int) *Int {\n\tz.abs.Set(x.abs)\n\tif z.abs.IsZero() {\n\t\tz.neg = false\n\t} else {\n\t\tz.neg = !x.neg\n\t}\n\treturn z\n}\n\n// Set sets z to x and returns z.\nfunc (z *Int) Set(x *Int) *Int {\n\tz.abs.Set(x.abs)\n\tz.neg = x.neg\n\treturn z\n}\n\n// SetFromUint256 converts a uint256.Uint to Int and sets the value to z.\nfunc (z *Int) SetUint256(x *uint256.Uint) *Int {\n\tz.abs.Set(x)\n\tz.neg = false\n\treturn z\n}\n\n// OBS, differs from original mempooler int256\n// ToString returns the decimal representation of z.\nfunc (z *Int) ToString() string {\n\tif z == nil {\n\t\tpanic(\"int256: nil pointer to ToString()\")\n\t}\n\n\tt := z.abs.Dec()\n\tif z.neg {\n\t\treturn \"-\" + t\n\t}\n\treturn t\n}\n" + }, + { + "name": "conversion_test.gno", + "body": "package int256\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/uint256\"\n)\n\nfunc TestSetInt64(t *testing.T) {\n\ttests := []struct {\n\t\tx int64\n\t\twant string\n\t}{\n\t\t{0, \"0\"},\n\t\t{1, \"1\"},\n\t\t{-1, \"-1\"},\n\t\t{9223372036854775807, \"9223372036854775807\"},\n\t\t{-9223372036854775808, \"-9223372036854775808\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tvar z Int\n\t\tz.SetInt64(tc.x)\n\n\t\tgot := z.ToString()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"SetInt64(%d) = %s, want %s\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestSetUint64(t *testing.T) {\n\ttests := []struct {\n\t\tx uint64\n\t\twant string\n\t}{\n\t\t{0, \"0\"},\n\t\t{1, \"1\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tvar z Int\n\t\tz.SetUint64(tc.x)\n\n\t\tgot := z.ToString()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"SetUint64(%d) = %s, want %s\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestUint64(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant uint64\n\t}{\n\t\t{\"0\", 0},\n\t\t{\"1\", 1},\n\t\t{\"9223372036854775807\", 9223372036854775807},\n\t\t{\"9223372036854775808\", 9223372036854775808},\n\t\t{\"18446744073709551615\", 18446744073709551615},\n\t\t{\"18446744073709551616\", 0},\n\t\t{\"18446744073709551617\", 1},\n\t\t{\"-1\", 1},\n\t\t{\"-18446744073709551615\", 18446744073709551615},\n\t\t{\"-18446744073709551616\", 0},\n\t\t{\"-18446744073709551617\", 1},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\n\t\tgot := z.Uint64()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Uint64(%s) = %d, want %d\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestInt64(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant int64\n\t}{\n\t\t{\"0\", 0},\n\t\t{\"1\", 1},\n\t\t{\"9223372036854775807\", 9223372036854775807},\n\t\t{\"18446744073709551616\", 0},\n\t\t{\"18446744073709551617\", 1},\n\t\t{\"-1\", -1},\n\t\t{\"-9223372036854775808\", -9223372036854775808},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\n\t\tgot := z.Int64()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Uint64(%s) = %d, want %d\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestNeg(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant string\n\t}{\n\t\t{\"0\", \"0\"},\n\t\t{\"1\", \"-1\"},\n\t\t{\"-1\", \"1\"},\n\t\t{\"9223372036854775807\", \"-9223372036854775807\"},\n\t\t{\"-18446744073709551615\", \"18446744073709551615\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\t\tz.Neg(z)\n\n\t\tgot := z.ToString()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Neg(%s) = %s, want %s\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestSet(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant string\n\t}{\n\t\t{\"0\", \"0\"},\n\t\t{\"1\", \"1\"},\n\t\t{\"-1\", \"-1\"},\n\t\t{\"9223372036854775807\", \"9223372036854775807\"},\n\t\t{\"-18446744073709551615\", \"-18446744073709551615\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\t\tz.Set(z)\n\n\t\tgot := z.ToString()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Set(%s) = %s, want %s\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestSetUint256(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant string\n\t}{\n\t\t{\"0\", \"0\"},\n\t\t{\"1\", \"1\"},\n\t\t{\"9223372036854775807\", \"9223372036854775807\"},\n\t\t{\"18446744073709551615\", \"18446744073709551615\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tgot := New()\n\n\t\tz := uint256.MustFromDecimal(tc.x)\n\t\tgot.SetUint256(z)\n\n\t\tif got.ToString() != tc.want {\n\t\t\tt.Errorf(\"SetUint256(%s) = %s, want %s\", tc.x, got.ToString(), tc.want)\n\t\t}\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/int256\n\nrequire gno.land/p/demo/uint256 v0.0.0-latest\n" + }, + { + "name": "int256.gno", + "body": "// This package provides a 256-bit signed integer type, Int, and associated functions.\npackage int256\n\nimport (\n\t\"gno.land/p/demo/uint256\"\n)\n\nvar one = uint256.NewUint(1)\n\ntype Int struct {\n\tabs *uint256.Uint\n\tneg bool\n}\n\n// Zero returns a new Int set to 0.\nfunc Zero() *Int {\n\treturn NewInt(0)\n}\n\n// One returns a new Int set to 1.\nfunc One() *Int {\n\treturn NewInt(1)\n}\n\n// Sign returns:\n//\n//\t-1 if x \u003c 0\n//\t 0 if x == 0\n//\t+1 if x \u003e 0\nfunc (z *Int) Sign() int {\n\tz.initiateAbs()\n\n\tif z.abs.IsZero() {\n\t\treturn 0\n\t}\n\tif z.neg {\n\t\treturn -1\n\t}\n\treturn 1\n}\n\n// New returns a new Int set to 0.\nfunc New() *Int {\n\treturn \u0026Int{\n\t\tabs: new(uint256.Uint),\n\t}\n}\n\n// NewInt allocates and returns a new Int set to x.\nfunc NewInt(x int64) *Int {\n\treturn New().SetInt64(x)\n}\n\n// FromDecimal returns a new Int from a decimal string.\n// Returns a new Int and an error if the string is not a valid decimal.\nfunc FromDecimal(s string) (*Int, error) {\n\treturn new(Int).SetString(s)\n}\n\n// MustFromDecimal returns a new Int from a decimal string.\n// Panics if the string is not a valid decimal.\nfunc MustFromDecimal(s string) *Int {\n\tz, err := FromDecimal(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn z\n}\n\n// SetString sets s to the value of z and returns z and a boolean indicating success.\nfunc (z *Int) SetString(s string) (*Int, error) {\n\tneg := false\n\t// Remove max one leading +\n\tif len(s) \u003e 0 \u0026\u0026 s[0] == '+' {\n\t\tneg = false\n\t\ts = s[1:]\n\t}\n\n\tif len(s) \u003e 0 \u0026\u0026 s[0] == '-' {\n\t\tneg = true\n\t\ts = s[1:]\n\t}\n\tvar (\n\t\tabs *uint256.Uint\n\t\terr error\n\t)\n\tabs, err = uint256.FromDecimal(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn \u0026Int{\n\t\tabs,\n\t\tneg,\n\t}, nil\n}\n\n// FromUint256 is a convenience-constructor from uint256.Uint.\n// Returns a new Int and whether overflow occurred.\n// OBS: If u is `nil`, this method returns `nil, false`\nfunc FromUint256(x *uint256.Uint) *Int {\n\tif x == nil {\n\t\treturn nil\n\t}\n\tz := Zero()\n\n\tz.SetUint256(x)\n\treturn z\n}\n\n// OBS, differs from original mempooler int256\n// NilToZero sets z to 0 and return it if it's nil, otherwise it returns z\nfunc (z *Int) NilToZero() *Int {\n\tif z == nil {\n\t\treturn NewInt(0)\n\t}\n\treturn z\n}\n\n// initiateAbs sets default value for `z` or `z.abs` value if is nil\n// OBS: differs from mempooler int256. It checks not only `z.abs` but also `z`\nfunc (z *Int) initiateAbs() {\n\tif z == nil || z.abs == nil {\n\t\tz.abs = new(uint256.Uint)\n\t}\n}\n" + }, + { + "name": "int256_test.gno", + "body": "// ported from github.com/mempooler/int256\npackage int256\n\nimport \"testing\"\n\nfunc TestSign(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant int\n\t}{\n\t\t{\"0\", 0},\n\t\t{\"1\", 1},\n\t\t{\"-1\", -1},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\t\tgot := z.Sign()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Sign(%s) = %d, want %d\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "eisel_lemire", + "path": "gno.land/p/demo/json/eisel_lemire", + "files": [ + { + "name": "eisel_lemire.gno", + "body": "// Copyright 2020 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage eisel_lemire\n\n// This file implements the Eisel-Lemire ParseFloat algorithm, published in\n// 2020 and discussed extensively at\n// https://nigeltao.github.io/blog/2020/eisel-lemire.html\n//\n// The original C++ implementation is at\n// https://github.com/lemire/fast_double_parser/blob/644bef4306059d3be01a04e77d3cc84b379c596f/include/fast_double_parser.h#L840\n//\n// This Go re-implementation closely follows the C re-implementation at\n// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/internal/cgen/base/floatconv-submodule-code.c#L990\n//\n// Additional testing (on over several million test strings) is done by\n// https://github.com/nigeltao/parse-number-fxx-test-data/blob/5280dcfccf6d0b02a65ae282dad0b6d9de50e039/script/test-go-strconv.go\n\nimport (\n\t\"math\"\n\t\"math/bits\"\n)\n\nconst (\n\tfloat32ExponentBias = 127\n\tfloat64ExponentBias = 1023\n)\n\n// eiselLemire64 parses a floating-point number from its mantissa and exponent representation.\n// This implementation is based on the Eisel-Lemire ParseFloat algorithm, which is efficient\n// and precise for converting strings to floating-point numbers.\n//\n// Arguments:\n// man (uint64): The mantissa part of the floating-point number.\n// exp10 (int): The exponent part, representing the power of 10.\n// neg (bool): Indicates if the number is negative.\n//\n// Returns:\n// f (float64): The parsed floating-point number.\n// ok (bool): Indicates whether the parsing was successful.\n//\n// The function starts by handling special cases, such as zero mantissa.\n// It then checks if the exponent is within the allowed range.\n// After that, it normalizes the mantissa by left-shifting it to fill\n// the leading zeros. This is followed by the main algorithm logic that\n// converts the normalized mantissa and exponent into a 64-bit floating-point number.\n// The function returns this number along with a boolean indicating the success of the operation.\nfunc EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {\n\t// The terse comments in this function body refer to sections of the\n\t// https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post.\n\n\t// Exp10 Range.\n\tif man == 0 {\n\t\tif neg {\n\t\t\tf = math.Float64frombits(0x80000000_00000000) // Negative zero.\n\t\t}\n\n\t\treturn f, true\n\t}\n\n\tif exp10 \u003c detailedPowersOfTenMinExp10 || detailedPowersOfTenMaxExp10 \u003c exp10 {\n\t\treturn 0, false\n\t}\n\n\t// Normalization.\n\tclz := bits.LeadingZeros64(man)\n\tman \u003c\u003c= uint(clz)\n\tretExp2 := uint64(217706*exp10\u003e\u003e16+64+float64ExponentBias) - uint64(clz)\n\n\t// Multiplication.\n\txHi, xLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][1])\n\n\t// Wider Approximation.\n\tif xHi\u00260x1FF == 0x1FF \u0026\u0026 xLo+man \u003c man {\n\t\tyHi, yLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][0])\n\t\tmergedHi, mergedLo := xHi, xLo+yHi\n\t\tif mergedLo \u003c xLo {\n\t\t\tmergedHi++\n\t\t}\n\n\t\tif mergedHi\u00260x1FF == 0x1FF \u0026\u0026 mergedLo+1 == 0 \u0026\u0026 yLo+man \u003c man {\n\t\t\treturn 0, false\n\t\t}\n\n\t\txHi, xLo = mergedHi, mergedLo\n\t}\n\n\t// Shifting to 54 Bits.\n\tmsb := xHi \u003e\u003e 63\n\tretMantissa := xHi \u003e\u003e (msb + 9)\n\tretExp2 -= 1 ^ msb\n\n\t// Half-way Ambiguity.\n\tif xLo == 0 \u0026\u0026 xHi\u00260x1FF == 0 \u0026\u0026 retMantissa\u00263 == 1 {\n\t\treturn 0, false\n\t}\n\n\t// From 54 to 53 Bits.\n\tretMantissa += retMantissa \u0026 1\n\tretMantissa \u003e\u003e= 1\n\tif retMantissa\u003e\u003e53 \u003e 0 {\n\t\tretMantissa \u003e\u003e= 1\n\t\tretExp2 += 1\n\t}\n\n\t// retExp2 is a uint64. Zero or underflow means that we're in subnormal\n\t// float64 space. 0x7FF or above means that we're in Inf/NaN float64 space.\n\t//\n\t// The if block is equivalent to (but has fewer branches than):\n\t// if retExp2 \u003c= 0 || retExp2 \u003e= 0x7FF { etc }\n\tif retExp2-1 \u003e= 0x7FF-1 {\n\t\treturn 0, false\n\t}\n\n\tretBits := retExp2\u003c\u003c52 | retMantissa\u00260x000FFFFF_FFFFFFFF\n\tif neg {\n\t\tretBits |= 0x80000000_00000000\n\t}\n\n\treturn math.Float64frombits(retBits), true\n}\n\n// detailedPowersOfTen{Min,Max}Exp10 is the power of 10 represented by the\n// first and last rows of detailedPowersOfTen. Both bounds are inclusive.\nconst (\n\tdetailedPowersOfTenMinExp10 = -348\n\tdetailedPowersOfTenMaxExp10 = +347\n)\n\n// detailedPowersOfTen contains 128-bit mantissa approximations (rounded down)\n// to the powers of 10. For example:\n//\n// - 1e43 ≈ (0xE596B7B0_C643C719 * (2 ** 79))\n// - 1e43 = (0xE596B7B0_C643C719_6D9CCD05_D0000000 * (2 ** 15))\n//\n// The mantissas are explicitly listed. The exponents are implied by a linear\n// expression with slope 217706.0/65536.0 ≈ log(10)/log(2).\n//\n// The table was generated by\n// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/script/print-mpb-powers-of-10.go\nvar detailedPowersOfTen = [...][2]uint64{\n\t{0x1732C869CD60E453, 0xFA8FD5A0081C0288}, // 1e-348\n\t{0x0E7FBD42205C8EB4, 0x9C99E58405118195}, // 1e-347\n\t{0x521FAC92A873B261, 0xC3C05EE50655E1FA}, // 1e-346\n\t{0xE6A797B752909EF9, 0xF4B0769E47EB5A78}, // 1e-345\n\t{0x9028BED2939A635C, 0x98EE4A22ECF3188B}, // 1e-344\n\t{0x7432EE873880FC33, 0xBF29DCABA82FDEAE}, // 1e-343\n\t{0x113FAA2906A13B3F, 0xEEF453D6923BD65A}, // 1e-342\n\t{0x4AC7CA59A424C507, 0x9558B4661B6565F8}, // 1e-341\n\t{0x5D79BCF00D2DF649, 0xBAAEE17FA23EBF76}, // 1e-340\n\t{0xF4D82C2C107973DC, 0xE95A99DF8ACE6F53}, // 1e-339\n\t{0x79071B9B8A4BE869, 0x91D8A02BB6C10594}, // 1e-338\n\t{0x9748E2826CDEE284, 0xB64EC836A47146F9}, // 1e-337\n\t{0xFD1B1B2308169B25, 0xE3E27A444D8D98B7}, // 1e-336\n\t{0xFE30F0F5E50E20F7, 0x8E6D8C6AB0787F72}, // 1e-335\n\t{0xBDBD2D335E51A935, 0xB208EF855C969F4F}, // 1e-334\n\t{0xAD2C788035E61382, 0xDE8B2B66B3BC4723}, // 1e-333\n\t{0x4C3BCB5021AFCC31, 0x8B16FB203055AC76}, // 1e-332\n\t{0xDF4ABE242A1BBF3D, 0xADDCB9E83C6B1793}, // 1e-331\n\t{0xD71D6DAD34A2AF0D, 0xD953E8624B85DD78}, // 1e-330\n\t{0x8672648C40E5AD68, 0x87D4713D6F33AA6B}, // 1e-329\n\t{0x680EFDAF511F18C2, 0xA9C98D8CCB009506}, // 1e-328\n\t{0x0212BD1B2566DEF2, 0xD43BF0EFFDC0BA48}, // 1e-327\n\t{0x014BB630F7604B57, 0x84A57695FE98746D}, // 1e-326\n\t{0x419EA3BD35385E2D, 0xA5CED43B7E3E9188}, // 1e-325\n\t{0x52064CAC828675B9, 0xCF42894A5DCE35EA}, // 1e-324\n\t{0x7343EFEBD1940993, 0x818995CE7AA0E1B2}, // 1e-323\n\t{0x1014EBE6C5F90BF8, 0xA1EBFB4219491A1F}, // 1e-322\n\t{0xD41A26E077774EF6, 0xCA66FA129F9B60A6}, // 1e-321\n\t{0x8920B098955522B4, 0xFD00B897478238D0}, // 1e-320\n\t{0x55B46E5F5D5535B0, 0x9E20735E8CB16382}, // 1e-319\n\t{0xEB2189F734AA831D, 0xC5A890362FDDBC62}, // 1e-318\n\t{0xA5E9EC7501D523E4, 0xF712B443BBD52B7B}, // 1e-317\n\t{0x47B233C92125366E, 0x9A6BB0AA55653B2D}, // 1e-316\n\t{0x999EC0BB696E840A, 0xC1069CD4EABE89F8}, // 1e-315\n\t{0xC00670EA43CA250D, 0xF148440A256E2C76}, // 1e-314\n\t{0x380406926A5E5728, 0x96CD2A865764DBCA}, // 1e-313\n\t{0xC605083704F5ECF2, 0xBC807527ED3E12BC}, // 1e-312\n\t{0xF7864A44C633682E, 0xEBA09271E88D976B}, // 1e-311\n\t{0x7AB3EE6AFBE0211D, 0x93445B8731587EA3}, // 1e-310\n\t{0x5960EA05BAD82964, 0xB8157268FDAE9E4C}, // 1e-309\n\t{0x6FB92487298E33BD, 0xE61ACF033D1A45DF}, // 1e-308\n\t{0xA5D3B6D479F8E056, 0x8FD0C16206306BAB}, // 1e-307\n\t{0x8F48A4899877186C, 0xB3C4F1BA87BC8696}, // 1e-306\n\t{0x331ACDABFE94DE87, 0xE0B62E2929ABA83C}, // 1e-305\n\t{0x9FF0C08B7F1D0B14, 0x8C71DCD9BA0B4925}, // 1e-304\n\t{0x07ECF0AE5EE44DD9, 0xAF8E5410288E1B6F}, // 1e-303\n\t{0xC9E82CD9F69D6150, 0xDB71E91432B1A24A}, // 1e-302\n\t{0xBE311C083A225CD2, 0x892731AC9FAF056E}, // 1e-301\n\t{0x6DBD630A48AAF406, 0xAB70FE17C79AC6CA}, // 1e-300\n\t{0x092CBBCCDAD5B108, 0xD64D3D9DB981787D}, // 1e-299\n\t{0x25BBF56008C58EA5, 0x85F0468293F0EB4E}, // 1e-298\n\t{0xAF2AF2B80AF6F24E, 0xA76C582338ED2621}, // 1e-297\n\t{0x1AF5AF660DB4AEE1, 0xD1476E2C07286FAA}, // 1e-296\n\t{0x50D98D9FC890ED4D, 0x82CCA4DB847945CA}, // 1e-295\n\t{0xE50FF107BAB528A0, 0xA37FCE126597973C}, // 1e-294\n\t{0x1E53ED49A96272C8, 0xCC5FC196FEFD7D0C}, // 1e-293\n\t{0x25E8E89C13BB0F7A, 0xFF77B1FCBEBCDC4F}, // 1e-292\n\t{0x77B191618C54E9AC, 0x9FAACF3DF73609B1}, // 1e-291\n\t{0xD59DF5B9EF6A2417, 0xC795830D75038C1D}, // 1e-290\n\t{0x4B0573286B44AD1D, 0xF97AE3D0D2446F25}, // 1e-289\n\t{0x4EE367F9430AEC32, 0x9BECCE62836AC577}, // 1e-288\n\t{0x229C41F793CDA73F, 0xC2E801FB244576D5}, // 1e-287\n\t{0x6B43527578C1110F, 0xF3A20279ED56D48A}, // 1e-286\n\t{0x830A13896B78AAA9, 0x9845418C345644D6}, // 1e-285\n\t{0x23CC986BC656D553, 0xBE5691EF416BD60C}, // 1e-284\n\t{0x2CBFBE86B7EC8AA8, 0xEDEC366B11C6CB8F}, // 1e-283\n\t{0x7BF7D71432F3D6A9, 0x94B3A202EB1C3F39}, // 1e-282\n\t{0xDAF5CCD93FB0CC53, 0xB9E08A83A5E34F07}, // 1e-281\n\t{0xD1B3400F8F9CFF68, 0xE858AD248F5C22C9}, // 1e-280\n\t{0x23100809B9C21FA1, 0x91376C36D99995BE}, // 1e-279\n\t{0xABD40A0C2832A78A, 0xB58547448FFFFB2D}, // 1e-278\n\t{0x16C90C8F323F516C, 0xE2E69915B3FFF9F9}, // 1e-277\n\t{0xAE3DA7D97F6792E3, 0x8DD01FAD907FFC3B}, // 1e-276\n\t{0x99CD11CFDF41779C, 0xB1442798F49FFB4A}, // 1e-275\n\t{0x40405643D711D583, 0xDD95317F31C7FA1D}, // 1e-274\n\t{0x482835EA666B2572, 0x8A7D3EEF7F1CFC52}, // 1e-273\n\t{0xDA3243650005EECF, 0xAD1C8EAB5EE43B66}, // 1e-272\n\t{0x90BED43E40076A82, 0xD863B256369D4A40}, // 1e-271\n\t{0x5A7744A6E804A291, 0x873E4F75E2224E68}, // 1e-270\n\t{0x711515D0A205CB36, 0xA90DE3535AAAE202}, // 1e-269\n\t{0x0D5A5B44CA873E03, 0xD3515C2831559A83}, // 1e-268\n\t{0xE858790AFE9486C2, 0x8412D9991ED58091}, // 1e-267\n\t{0x626E974DBE39A872, 0xA5178FFF668AE0B6}, // 1e-266\n\t{0xFB0A3D212DC8128F, 0xCE5D73FF402D98E3}, // 1e-265\n\t{0x7CE66634BC9D0B99, 0x80FA687F881C7F8E}, // 1e-264\n\t{0x1C1FFFC1EBC44E80, 0xA139029F6A239F72}, // 1e-263\n\t{0xA327FFB266B56220, 0xC987434744AC874E}, // 1e-262\n\t{0x4BF1FF9F0062BAA8, 0xFBE9141915D7A922}, // 1e-261\n\t{0x6F773FC3603DB4A9, 0x9D71AC8FADA6C9B5}, // 1e-260\n\t{0xCB550FB4384D21D3, 0xC4CE17B399107C22}, // 1e-259\n\t{0x7E2A53A146606A48, 0xF6019DA07F549B2B}, // 1e-258\n\t{0x2EDA7444CBFC426D, 0x99C102844F94E0FB}, // 1e-257\n\t{0xFA911155FEFB5308, 0xC0314325637A1939}, // 1e-256\n\t{0x793555AB7EBA27CA, 0xF03D93EEBC589F88}, // 1e-255\n\t{0x4BC1558B2F3458DE, 0x96267C7535B763B5}, // 1e-254\n\t{0x9EB1AAEDFB016F16, 0xBBB01B9283253CA2}, // 1e-253\n\t{0x465E15A979C1CADC, 0xEA9C227723EE8BCB}, // 1e-252\n\t{0x0BFACD89EC191EC9, 0x92A1958A7675175F}, // 1e-251\n\t{0xCEF980EC671F667B, 0xB749FAED14125D36}, // 1e-250\n\t{0x82B7E12780E7401A, 0xE51C79A85916F484}, // 1e-249\n\t{0xD1B2ECB8B0908810, 0x8F31CC0937AE58D2}, // 1e-248\n\t{0x861FA7E6DCB4AA15, 0xB2FE3F0B8599EF07}, // 1e-247\n\t{0x67A791E093E1D49A, 0xDFBDCECE67006AC9}, // 1e-246\n\t{0xE0C8BB2C5C6D24E0, 0x8BD6A141006042BD}, // 1e-245\n\t{0x58FAE9F773886E18, 0xAECC49914078536D}, // 1e-244\n\t{0xAF39A475506A899E, 0xDA7F5BF590966848}, // 1e-243\n\t{0x6D8406C952429603, 0x888F99797A5E012D}, // 1e-242\n\t{0xC8E5087BA6D33B83, 0xAAB37FD7D8F58178}, // 1e-241\n\t{0xFB1E4A9A90880A64, 0xD5605FCDCF32E1D6}, // 1e-240\n\t{0x5CF2EEA09A55067F, 0x855C3BE0A17FCD26}, // 1e-239\n\t{0xF42FAA48C0EA481E, 0xA6B34AD8C9DFC06F}, // 1e-238\n\t{0xF13B94DAF124DA26, 0xD0601D8EFC57B08B}, // 1e-237\n\t{0x76C53D08D6B70858, 0x823C12795DB6CE57}, // 1e-236\n\t{0x54768C4B0C64CA6E, 0xA2CB1717B52481ED}, // 1e-235\n\t{0xA9942F5DCF7DFD09, 0xCB7DDCDDA26DA268}, // 1e-234\n\t{0xD3F93B35435D7C4C, 0xFE5D54150B090B02}, // 1e-233\n\t{0xC47BC5014A1A6DAF, 0x9EFA548D26E5A6E1}, // 1e-232\n\t{0x359AB6419CA1091B, 0xC6B8E9B0709F109A}, // 1e-231\n\t{0xC30163D203C94B62, 0xF867241C8CC6D4C0}, // 1e-230\n\t{0x79E0DE63425DCF1D, 0x9B407691D7FC44F8}, // 1e-229\n\t{0x985915FC12F542E4, 0xC21094364DFB5636}, // 1e-228\n\t{0x3E6F5B7B17B2939D, 0xF294B943E17A2BC4}, // 1e-227\n\t{0xA705992CEECF9C42, 0x979CF3CA6CEC5B5A}, // 1e-226\n\t{0x50C6FF782A838353, 0xBD8430BD08277231}, // 1e-225\n\t{0xA4F8BF5635246428, 0xECE53CEC4A314EBD}, // 1e-224\n\t{0x871B7795E136BE99, 0x940F4613AE5ED136}, // 1e-223\n\t{0x28E2557B59846E3F, 0xB913179899F68584}, // 1e-222\n\t{0x331AEADA2FE589CF, 0xE757DD7EC07426E5}, // 1e-221\n\t{0x3FF0D2C85DEF7621, 0x9096EA6F3848984F}, // 1e-220\n\t{0x0FED077A756B53A9, 0xB4BCA50B065ABE63}, // 1e-219\n\t{0xD3E8495912C62894, 0xE1EBCE4DC7F16DFB}, // 1e-218\n\t{0x64712DD7ABBBD95C, 0x8D3360F09CF6E4BD}, // 1e-217\n\t{0xBD8D794D96AACFB3, 0xB080392CC4349DEC}, // 1e-216\n\t{0xECF0D7A0FC5583A0, 0xDCA04777F541C567}, // 1e-215\n\t{0xF41686C49DB57244, 0x89E42CAAF9491B60}, // 1e-214\n\t{0x311C2875C522CED5, 0xAC5D37D5B79B6239}, // 1e-213\n\t{0x7D633293366B828B, 0xD77485CB25823AC7}, // 1e-212\n\t{0xAE5DFF9C02033197, 0x86A8D39EF77164BC}, // 1e-211\n\t{0xD9F57F830283FDFC, 0xA8530886B54DBDEB}, // 1e-210\n\t{0xD072DF63C324FD7B, 0xD267CAA862A12D66}, // 1e-209\n\t{0x4247CB9E59F71E6D, 0x8380DEA93DA4BC60}, // 1e-208\n\t{0x52D9BE85F074E608, 0xA46116538D0DEB78}, // 1e-207\n\t{0x67902E276C921F8B, 0xCD795BE870516656}, // 1e-206\n\t{0x00BA1CD8A3DB53B6, 0x806BD9714632DFF6}, // 1e-205\n\t{0x80E8A40ECCD228A4, 0xA086CFCD97BF97F3}, // 1e-204\n\t{0x6122CD128006B2CD, 0xC8A883C0FDAF7DF0}, // 1e-203\n\t{0x796B805720085F81, 0xFAD2A4B13D1B5D6C}, // 1e-202\n\t{0xCBE3303674053BB0, 0x9CC3A6EEC6311A63}, // 1e-201\n\t{0xBEDBFC4411068A9C, 0xC3F490AA77BD60FC}, // 1e-200\n\t{0xEE92FB5515482D44, 0xF4F1B4D515ACB93B}, // 1e-199\n\t{0x751BDD152D4D1C4A, 0x991711052D8BF3C5}, // 1e-198\n\t{0xD262D45A78A0635D, 0xBF5CD54678EEF0B6}, // 1e-197\n\t{0x86FB897116C87C34, 0xEF340A98172AACE4}, // 1e-196\n\t{0xD45D35E6AE3D4DA0, 0x9580869F0E7AAC0E}, // 1e-195\n\t{0x8974836059CCA109, 0xBAE0A846D2195712}, // 1e-194\n\t{0x2BD1A438703FC94B, 0xE998D258869FACD7}, // 1e-193\n\t{0x7B6306A34627DDCF, 0x91FF83775423CC06}, // 1e-192\n\t{0x1A3BC84C17B1D542, 0xB67F6455292CBF08}, // 1e-191\n\t{0x20CABA5F1D9E4A93, 0xE41F3D6A7377EECA}, // 1e-190\n\t{0x547EB47B7282EE9C, 0x8E938662882AF53E}, // 1e-189\n\t{0xE99E619A4F23AA43, 0xB23867FB2A35B28D}, // 1e-188\n\t{0x6405FA00E2EC94D4, 0xDEC681F9F4C31F31}, // 1e-187\n\t{0xDE83BC408DD3DD04, 0x8B3C113C38F9F37E}, // 1e-186\n\t{0x9624AB50B148D445, 0xAE0B158B4738705E}, // 1e-185\n\t{0x3BADD624DD9B0957, 0xD98DDAEE19068C76}, // 1e-184\n\t{0xE54CA5D70A80E5D6, 0x87F8A8D4CFA417C9}, // 1e-183\n\t{0x5E9FCF4CCD211F4C, 0xA9F6D30A038D1DBC}, // 1e-182\n\t{0x7647C3200069671F, 0xD47487CC8470652B}, // 1e-181\n\t{0x29ECD9F40041E073, 0x84C8D4DFD2C63F3B}, // 1e-180\n\t{0xF468107100525890, 0xA5FB0A17C777CF09}, // 1e-179\n\t{0x7182148D4066EEB4, 0xCF79CC9DB955C2CC}, // 1e-178\n\t{0xC6F14CD848405530, 0x81AC1FE293D599BF}, // 1e-177\n\t{0xB8ADA00E5A506A7C, 0xA21727DB38CB002F}, // 1e-176\n\t{0xA6D90811F0E4851C, 0xCA9CF1D206FDC03B}, // 1e-175\n\t{0x908F4A166D1DA663, 0xFD442E4688BD304A}, // 1e-174\n\t{0x9A598E4E043287FE, 0x9E4A9CEC15763E2E}, // 1e-173\n\t{0x40EFF1E1853F29FD, 0xC5DD44271AD3CDBA}, // 1e-172\n\t{0xD12BEE59E68EF47C, 0xF7549530E188C128}, // 1e-171\n\t{0x82BB74F8301958CE, 0x9A94DD3E8CF578B9}, // 1e-170\n\t{0xE36A52363C1FAF01, 0xC13A148E3032D6E7}, // 1e-169\n\t{0xDC44E6C3CB279AC1, 0xF18899B1BC3F8CA1}, // 1e-168\n\t{0x29AB103A5EF8C0B9, 0x96F5600F15A7B7E5}, // 1e-167\n\t{0x7415D448F6B6F0E7, 0xBCB2B812DB11A5DE}, // 1e-166\n\t{0x111B495B3464AD21, 0xEBDF661791D60F56}, // 1e-165\n\t{0xCAB10DD900BEEC34, 0x936B9FCEBB25C995}, // 1e-164\n\t{0x3D5D514F40EEA742, 0xB84687C269EF3BFB}, // 1e-163\n\t{0x0CB4A5A3112A5112, 0xE65829B3046B0AFA}, // 1e-162\n\t{0x47F0E785EABA72AB, 0x8FF71A0FE2C2E6DC}, // 1e-161\n\t{0x59ED216765690F56, 0xB3F4E093DB73A093}, // 1e-160\n\t{0x306869C13EC3532C, 0xE0F218B8D25088B8}, // 1e-159\n\t{0x1E414218C73A13FB, 0x8C974F7383725573}, // 1e-158\n\t{0xE5D1929EF90898FA, 0xAFBD2350644EEACF}, // 1e-157\n\t{0xDF45F746B74ABF39, 0xDBAC6C247D62A583}, // 1e-156\n\t{0x6B8BBA8C328EB783, 0x894BC396CE5DA772}, // 1e-155\n\t{0x066EA92F3F326564, 0xAB9EB47C81F5114F}, // 1e-154\n\t{0xC80A537B0EFEFEBD, 0xD686619BA27255A2}, // 1e-153\n\t{0xBD06742CE95F5F36, 0x8613FD0145877585}, // 1e-152\n\t{0x2C48113823B73704, 0xA798FC4196E952E7}, // 1e-151\n\t{0xF75A15862CA504C5, 0xD17F3B51FCA3A7A0}, // 1e-150\n\t{0x9A984D73DBE722FB, 0x82EF85133DE648C4}, // 1e-149\n\t{0xC13E60D0D2E0EBBA, 0xA3AB66580D5FDAF5}, // 1e-148\n\t{0x318DF905079926A8, 0xCC963FEE10B7D1B3}, // 1e-147\n\t{0xFDF17746497F7052, 0xFFBBCFE994E5C61F}, // 1e-146\n\t{0xFEB6EA8BEDEFA633, 0x9FD561F1FD0F9BD3}, // 1e-145\n\t{0xFE64A52EE96B8FC0, 0xC7CABA6E7C5382C8}, // 1e-144\n\t{0x3DFDCE7AA3C673B0, 0xF9BD690A1B68637B}, // 1e-143\n\t{0x06BEA10CA65C084E, 0x9C1661A651213E2D}, // 1e-142\n\t{0x486E494FCFF30A62, 0xC31BFA0FE5698DB8}, // 1e-141\n\t{0x5A89DBA3C3EFCCFA, 0xF3E2F893DEC3F126}, // 1e-140\n\t{0xF89629465A75E01C, 0x986DDB5C6B3A76B7}, // 1e-139\n\t{0xF6BBB397F1135823, 0xBE89523386091465}, // 1e-138\n\t{0x746AA07DED582E2C, 0xEE2BA6C0678B597F}, // 1e-137\n\t{0xA8C2A44EB4571CDC, 0x94DB483840B717EF}, // 1e-136\n\t{0x92F34D62616CE413, 0xBA121A4650E4DDEB}, // 1e-135\n\t{0x77B020BAF9C81D17, 0xE896A0D7E51E1566}, // 1e-134\n\t{0x0ACE1474DC1D122E, 0x915E2486EF32CD60}, // 1e-133\n\t{0x0D819992132456BA, 0xB5B5ADA8AAFF80B8}, // 1e-132\n\t{0x10E1FFF697ED6C69, 0xE3231912D5BF60E6}, // 1e-131\n\t{0xCA8D3FFA1EF463C1, 0x8DF5EFABC5979C8F}, // 1e-130\n\t{0xBD308FF8A6B17CB2, 0xB1736B96B6FD83B3}, // 1e-129\n\t{0xAC7CB3F6D05DDBDE, 0xDDD0467C64BCE4A0}, // 1e-128\n\t{0x6BCDF07A423AA96B, 0x8AA22C0DBEF60EE4}, // 1e-127\n\t{0x86C16C98D2C953C6, 0xAD4AB7112EB3929D}, // 1e-126\n\t{0xE871C7BF077BA8B7, 0xD89D64D57A607744}, // 1e-125\n\t{0x11471CD764AD4972, 0x87625F056C7C4A8B}, // 1e-124\n\t{0xD598E40D3DD89BCF, 0xA93AF6C6C79B5D2D}, // 1e-123\n\t{0x4AFF1D108D4EC2C3, 0xD389B47879823479}, // 1e-122\n\t{0xCEDF722A585139BA, 0x843610CB4BF160CB}, // 1e-121\n\t{0xC2974EB4EE658828, 0xA54394FE1EEDB8FE}, // 1e-120\n\t{0x733D226229FEEA32, 0xCE947A3DA6A9273E}, // 1e-119\n\t{0x0806357D5A3F525F, 0x811CCC668829B887}, // 1e-118\n\t{0xCA07C2DCB0CF26F7, 0xA163FF802A3426A8}, // 1e-117\n\t{0xFC89B393DD02F0B5, 0xC9BCFF6034C13052}, // 1e-116\n\t{0xBBAC2078D443ACE2, 0xFC2C3F3841F17C67}, // 1e-115\n\t{0xD54B944B84AA4C0D, 0x9D9BA7832936EDC0}, // 1e-114\n\t{0x0A9E795E65D4DF11, 0xC5029163F384A931}, // 1e-113\n\t{0x4D4617B5FF4A16D5, 0xF64335BCF065D37D}, // 1e-112\n\t{0x504BCED1BF8E4E45, 0x99EA0196163FA42E}, // 1e-111\n\t{0xE45EC2862F71E1D6, 0xC06481FB9BCF8D39}, // 1e-110\n\t{0x5D767327BB4E5A4C, 0xF07DA27A82C37088}, // 1e-109\n\t{0x3A6A07F8D510F86F, 0x964E858C91BA2655}, // 1e-108\n\t{0x890489F70A55368B, 0xBBE226EFB628AFEA}, // 1e-107\n\t{0x2B45AC74CCEA842E, 0xEADAB0ABA3B2DBE5}, // 1e-106\n\t{0x3B0B8BC90012929D, 0x92C8AE6B464FC96F}, // 1e-105\n\t{0x09CE6EBB40173744, 0xB77ADA0617E3BBCB}, // 1e-104\n\t{0xCC420A6A101D0515, 0xE55990879DDCAABD}, // 1e-103\n\t{0x9FA946824A12232D, 0x8F57FA54C2A9EAB6}, // 1e-102\n\t{0x47939822DC96ABF9, 0xB32DF8E9F3546564}, // 1e-101\n\t{0x59787E2B93BC56F7, 0xDFF9772470297EBD}, // 1e-100\n\t{0x57EB4EDB3C55B65A, 0x8BFBEA76C619EF36}, // 1e-99\n\t{0xEDE622920B6B23F1, 0xAEFAE51477A06B03}, // 1e-98\n\t{0xE95FAB368E45ECED, 0xDAB99E59958885C4}, // 1e-97\n\t{0x11DBCB0218EBB414, 0x88B402F7FD75539B}, // 1e-96\n\t{0xD652BDC29F26A119, 0xAAE103B5FCD2A881}, // 1e-95\n\t{0x4BE76D3346F0495F, 0xD59944A37C0752A2}, // 1e-94\n\t{0x6F70A4400C562DDB, 0x857FCAE62D8493A5}, // 1e-93\n\t{0xCB4CCD500F6BB952, 0xA6DFBD9FB8E5B88E}, // 1e-92\n\t{0x7E2000A41346A7A7, 0xD097AD07A71F26B2}, // 1e-91\n\t{0x8ED400668C0C28C8, 0x825ECC24C873782F}, // 1e-90\n\t{0x728900802F0F32FA, 0xA2F67F2DFA90563B}, // 1e-89\n\t{0x4F2B40A03AD2FFB9, 0xCBB41EF979346BCA}, // 1e-88\n\t{0xE2F610C84987BFA8, 0xFEA126B7D78186BC}, // 1e-87\n\t{0x0DD9CA7D2DF4D7C9, 0x9F24B832E6B0F436}, // 1e-86\n\t{0x91503D1C79720DBB, 0xC6EDE63FA05D3143}, // 1e-85\n\t{0x75A44C6397CE912A, 0xF8A95FCF88747D94}, // 1e-84\n\t{0xC986AFBE3EE11ABA, 0x9B69DBE1B548CE7C}, // 1e-83\n\t{0xFBE85BADCE996168, 0xC24452DA229B021B}, // 1e-82\n\t{0xFAE27299423FB9C3, 0xF2D56790AB41C2A2}, // 1e-81\n\t{0xDCCD879FC967D41A, 0x97C560BA6B0919A5}, // 1e-80\n\t{0x5400E987BBC1C920, 0xBDB6B8E905CB600F}, // 1e-79\n\t{0x290123E9AAB23B68, 0xED246723473E3813}, // 1e-78\n\t{0xF9A0B6720AAF6521, 0x9436C0760C86E30B}, // 1e-77\n\t{0xF808E40E8D5B3E69, 0xB94470938FA89BCE}, // 1e-76\n\t{0xB60B1D1230B20E04, 0xE7958CB87392C2C2}, // 1e-75\n\t{0xB1C6F22B5E6F48C2, 0x90BD77F3483BB9B9}, // 1e-74\n\t{0x1E38AEB6360B1AF3, 0xB4ECD5F01A4AA828}, // 1e-73\n\t{0x25C6DA63C38DE1B0, 0xE2280B6C20DD5232}, // 1e-72\n\t{0x579C487E5A38AD0E, 0x8D590723948A535F}, // 1e-71\n\t{0x2D835A9DF0C6D851, 0xB0AF48EC79ACE837}, // 1e-70\n\t{0xF8E431456CF88E65, 0xDCDB1B2798182244}, // 1e-69\n\t{0x1B8E9ECB641B58FF, 0x8A08F0F8BF0F156B}, // 1e-68\n\t{0xE272467E3D222F3F, 0xAC8B2D36EED2DAC5}, // 1e-67\n\t{0x5B0ED81DCC6ABB0F, 0xD7ADF884AA879177}, // 1e-66\n\t{0x98E947129FC2B4E9, 0x86CCBB52EA94BAEA}, // 1e-65\n\t{0x3F2398D747B36224, 0xA87FEA27A539E9A5}, // 1e-64\n\t{0x8EEC7F0D19A03AAD, 0xD29FE4B18E88640E}, // 1e-63\n\t{0x1953CF68300424AC, 0x83A3EEEEF9153E89}, // 1e-62\n\t{0x5FA8C3423C052DD7, 0xA48CEAAAB75A8E2B}, // 1e-61\n\t{0x3792F412CB06794D, 0xCDB02555653131B6}, // 1e-60\n\t{0xE2BBD88BBEE40BD0, 0x808E17555F3EBF11}, // 1e-59\n\t{0x5B6ACEAEAE9D0EC4, 0xA0B19D2AB70E6ED6}, // 1e-58\n\t{0xF245825A5A445275, 0xC8DE047564D20A8B}, // 1e-57\n\t{0xEED6E2F0F0D56712, 0xFB158592BE068D2E}, // 1e-56\n\t{0x55464DD69685606B, 0x9CED737BB6C4183D}, // 1e-55\n\t{0xAA97E14C3C26B886, 0xC428D05AA4751E4C}, // 1e-54\n\t{0xD53DD99F4B3066A8, 0xF53304714D9265DF}, // 1e-53\n\t{0xE546A8038EFE4029, 0x993FE2C6D07B7FAB}, // 1e-52\n\t{0xDE98520472BDD033, 0xBF8FDB78849A5F96}, // 1e-51\n\t{0x963E66858F6D4440, 0xEF73D256A5C0F77C}, // 1e-50\n\t{0xDDE7001379A44AA8, 0x95A8637627989AAD}, // 1e-49\n\t{0x5560C018580D5D52, 0xBB127C53B17EC159}, // 1e-48\n\t{0xAAB8F01E6E10B4A6, 0xE9D71B689DDE71AF}, // 1e-47\n\t{0xCAB3961304CA70E8, 0x9226712162AB070D}, // 1e-46\n\t{0x3D607B97C5FD0D22, 0xB6B00D69BB55C8D1}, // 1e-45\n\t{0x8CB89A7DB77C506A, 0xE45C10C42A2B3B05}, // 1e-44\n\t{0x77F3608E92ADB242, 0x8EB98A7A9A5B04E3}, // 1e-43\n\t{0x55F038B237591ED3, 0xB267ED1940F1C61C}, // 1e-42\n\t{0x6B6C46DEC52F6688, 0xDF01E85F912E37A3}, // 1e-41\n\t{0x2323AC4B3B3DA015, 0x8B61313BBABCE2C6}, // 1e-40\n\t{0xABEC975E0A0D081A, 0xAE397D8AA96C1B77}, // 1e-39\n\t{0x96E7BD358C904A21, 0xD9C7DCED53C72255}, // 1e-38\n\t{0x7E50D64177DA2E54, 0x881CEA14545C7575}, // 1e-37\n\t{0xDDE50BD1D5D0B9E9, 0xAA242499697392D2}, // 1e-36\n\t{0x955E4EC64B44E864, 0xD4AD2DBFC3D07787}, // 1e-35\n\t{0xBD5AF13BEF0B113E, 0x84EC3C97DA624AB4}, // 1e-34\n\t{0xECB1AD8AEACDD58E, 0xA6274BBDD0FADD61}, // 1e-33\n\t{0x67DE18EDA5814AF2, 0xCFB11EAD453994BA}, // 1e-32\n\t{0x80EACF948770CED7, 0x81CEB32C4B43FCF4}, // 1e-31\n\t{0xA1258379A94D028D, 0xA2425FF75E14FC31}, // 1e-30\n\t{0x096EE45813A04330, 0xCAD2F7F5359A3B3E}, // 1e-29\n\t{0x8BCA9D6E188853FC, 0xFD87B5F28300CA0D}, // 1e-28\n\t{0x775EA264CF55347D, 0x9E74D1B791E07E48}, // 1e-27\n\t{0x95364AFE032A819D, 0xC612062576589DDA}, // 1e-26\n\t{0x3A83DDBD83F52204, 0xF79687AED3EEC551}, // 1e-25\n\t{0xC4926A9672793542, 0x9ABE14CD44753B52}, // 1e-24\n\t{0x75B7053C0F178293, 0xC16D9A0095928A27}, // 1e-23\n\t{0x5324C68B12DD6338, 0xF1C90080BAF72CB1}, // 1e-22\n\t{0xD3F6FC16EBCA5E03, 0x971DA05074DA7BEE}, // 1e-21\n\t{0x88F4BB1CA6BCF584, 0xBCE5086492111AEA}, // 1e-20\n\t{0x2B31E9E3D06C32E5, 0xEC1E4A7DB69561A5}, // 1e-19\n\t{0x3AFF322E62439FCF, 0x9392EE8E921D5D07}, // 1e-18\n\t{0x09BEFEB9FAD487C2, 0xB877AA3236A4B449}, // 1e-17\n\t{0x4C2EBE687989A9B3, 0xE69594BEC44DE15B}, // 1e-16\n\t{0x0F9D37014BF60A10, 0x901D7CF73AB0ACD9}, // 1e-15\n\t{0x538484C19EF38C94, 0xB424DC35095CD80F}, // 1e-14\n\t{0x2865A5F206B06FB9, 0xE12E13424BB40E13}, // 1e-13\n\t{0xF93F87B7442E45D3, 0x8CBCCC096F5088CB}, // 1e-12\n\t{0xF78F69A51539D748, 0xAFEBFF0BCB24AAFE}, // 1e-11\n\t{0xB573440E5A884D1B, 0xDBE6FECEBDEDD5BE}, // 1e-10\n\t{0x31680A88F8953030, 0x89705F4136B4A597}, // 1e-9\n\t{0xFDC20D2B36BA7C3D, 0xABCC77118461CEFC}, // 1e-8\n\t{0x3D32907604691B4C, 0xD6BF94D5E57A42BC}, // 1e-7\n\t{0xA63F9A49C2C1B10F, 0x8637BD05AF6C69B5}, // 1e-6\n\t{0x0FCF80DC33721D53, 0xA7C5AC471B478423}, // 1e-5\n\t{0xD3C36113404EA4A8, 0xD1B71758E219652B}, // 1e-4\n\t{0x645A1CAC083126E9, 0x83126E978D4FDF3B}, // 1e-3\n\t{0x3D70A3D70A3D70A3, 0xA3D70A3D70A3D70A}, // 1e-2\n\t{0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC}, // 1e-1\n\t{0x0000000000000000, 0x8000000000000000}, // 1e0\n\t{0x0000000000000000, 0xA000000000000000}, // 1e1\n\t{0x0000000000000000, 0xC800000000000000}, // 1e2\n\t{0x0000000000000000, 0xFA00000000000000}, // 1e3\n\t{0x0000000000000000, 0x9C40000000000000}, // 1e4\n\t{0x0000000000000000, 0xC350000000000000}, // 1e5\n\t{0x0000000000000000, 0xF424000000000000}, // 1e6\n\t{0x0000000000000000, 0x9896800000000000}, // 1e7\n\t{0x0000000000000000, 0xBEBC200000000000}, // 1e8\n\t{0x0000000000000000, 0xEE6B280000000000}, // 1e9\n\t{0x0000000000000000, 0x9502F90000000000}, // 1e10\n\t{0x0000000000000000, 0xBA43B74000000000}, // 1e11\n\t{0x0000000000000000, 0xE8D4A51000000000}, // 1e12\n\t{0x0000000000000000, 0x9184E72A00000000}, // 1e13\n\t{0x0000000000000000, 0xB5E620F480000000}, // 1e14\n\t{0x0000000000000000, 0xE35FA931A0000000}, // 1e15\n\t{0x0000000000000000, 0x8E1BC9BF04000000}, // 1e16\n\t{0x0000000000000000, 0xB1A2BC2EC5000000}, // 1e17\n\t{0x0000000000000000, 0xDE0B6B3A76400000}, // 1e18\n\t{0x0000000000000000, 0x8AC7230489E80000}, // 1e19\n\t{0x0000000000000000, 0xAD78EBC5AC620000}, // 1e20\n\t{0x0000000000000000, 0xD8D726B7177A8000}, // 1e21\n\t{0x0000000000000000, 0x878678326EAC9000}, // 1e22\n\t{0x0000000000000000, 0xA968163F0A57B400}, // 1e23\n\t{0x0000000000000000, 0xD3C21BCECCEDA100}, // 1e24\n\t{0x0000000000000000, 0x84595161401484A0}, // 1e25\n\t{0x0000000000000000, 0xA56FA5B99019A5C8}, // 1e26\n\t{0x0000000000000000, 0xCECB8F27F4200F3A}, // 1e27\n\t{0x4000000000000000, 0x813F3978F8940984}, // 1e28\n\t{0x5000000000000000, 0xA18F07D736B90BE5}, // 1e29\n\t{0xA400000000000000, 0xC9F2C9CD04674EDE}, // 1e30\n\t{0x4D00000000000000, 0xFC6F7C4045812296}, // 1e31\n\t{0xF020000000000000, 0x9DC5ADA82B70B59D}, // 1e32\n\t{0x6C28000000000000, 0xC5371912364CE305}, // 1e33\n\t{0xC732000000000000, 0xF684DF56C3E01BC6}, // 1e34\n\t{0x3C7F400000000000, 0x9A130B963A6C115C}, // 1e35\n\t{0x4B9F100000000000, 0xC097CE7BC90715B3}, // 1e36\n\t{0x1E86D40000000000, 0xF0BDC21ABB48DB20}, // 1e37\n\t{0x1314448000000000, 0x96769950B50D88F4}, // 1e38\n\t{0x17D955A000000000, 0xBC143FA4E250EB31}, // 1e39\n\t{0x5DCFAB0800000000, 0xEB194F8E1AE525FD}, // 1e40\n\t{0x5AA1CAE500000000, 0x92EFD1B8D0CF37BE}, // 1e41\n\t{0xF14A3D9E40000000, 0xB7ABC627050305AD}, // 1e42\n\t{0x6D9CCD05D0000000, 0xE596B7B0C643C719}, // 1e43\n\t{0xE4820023A2000000, 0x8F7E32CE7BEA5C6F}, // 1e44\n\t{0xDDA2802C8A800000, 0xB35DBF821AE4F38B}, // 1e45\n\t{0xD50B2037AD200000, 0xE0352F62A19E306E}, // 1e46\n\t{0x4526F422CC340000, 0x8C213D9DA502DE45}, // 1e47\n\t{0x9670B12B7F410000, 0xAF298D050E4395D6}, // 1e48\n\t{0x3C0CDD765F114000, 0xDAF3F04651D47B4C}, // 1e49\n\t{0xA5880A69FB6AC800, 0x88D8762BF324CD0F}, // 1e50\n\t{0x8EEA0D047A457A00, 0xAB0E93B6EFEE0053}, // 1e51\n\t{0x72A4904598D6D880, 0xD5D238A4ABE98068}, // 1e52\n\t{0x47A6DA2B7F864750, 0x85A36366EB71F041}, // 1e53\n\t{0x999090B65F67D924, 0xA70C3C40A64E6C51}, // 1e54\n\t{0xFFF4B4E3F741CF6D, 0xD0CF4B50CFE20765}, // 1e55\n\t{0xBFF8F10E7A8921A4, 0x82818F1281ED449F}, // 1e56\n\t{0xAFF72D52192B6A0D, 0xA321F2D7226895C7}, // 1e57\n\t{0x9BF4F8A69F764490, 0xCBEA6F8CEB02BB39}, // 1e58\n\t{0x02F236D04753D5B4, 0xFEE50B7025C36A08}, // 1e59\n\t{0x01D762422C946590, 0x9F4F2726179A2245}, // 1e60\n\t{0x424D3AD2B7B97EF5, 0xC722F0EF9D80AAD6}, // 1e61\n\t{0xD2E0898765A7DEB2, 0xF8EBAD2B84E0D58B}, // 1e62\n\t{0x63CC55F49F88EB2F, 0x9B934C3B330C8577}, // 1e63\n\t{0x3CBF6B71C76B25FB, 0xC2781F49FFCFA6D5}, // 1e64\n\t{0x8BEF464E3945EF7A, 0xF316271C7FC3908A}, // 1e65\n\t{0x97758BF0E3CBB5AC, 0x97EDD871CFDA3A56}, // 1e66\n\t{0x3D52EEED1CBEA317, 0xBDE94E8E43D0C8EC}, // 1e67\n\t{0x4CA7AAA863EE4BDD, 0xED63A231D4C4FB27}, // 1e68\n\t{0x8FE8CAA93E74EF6A, 0x945E455F24FB1CF8}, // 1e69\n\t{0xB3E2FD538E122B44, 0xB975D6B6EE39E436}, // 1e70\n\t{0x60DBBCA87196B616, 0xE7D34C64A9C85D44}, // 1e71\n\t{0xBC8955E946FE31CD, 0x90E40FBEEA1D3A4A}, // 1e72\n\t{0x6BABAB6398BDBE41, 0xB51D13AEA4A488DD}, // 1e73\n\t{0xC696963C7EED2DD1, 0xE264589A4DCDAB14}, // 1e74\n\t{0xFC1E1DE5CF543CA2, 0x8D7EB76070A08AEC}, // 1e75\n\t{0x3B25A55F43294BCB, 0xB0DE65388CC8ADA8}, // 1e76\n\t{0x49EF0EB713F39EBE, 0xDD15FE86AFFAD912}, // 1e77\n\t{0x6E3569326C784337, 0x8A2DBF142DFCC7AB}, // 1e78\n\t{0x49C2C37F07965404, 0xACB92ED9397BF996}, // 1e79\n\t{0xDC33745EC97BE906, 0xD7E77A8F87DAF7FB}, // 1e80\n\t{0x69A028BB3DED71A3, 0x86F0AC99B4E8DAFD}, // 1e81\n\t{0xC40832EA0D68CE0C, 0xA8ACD7C0222311BC}, // 1e82\n\t{0xF50A3FA490C30190, 0xD2D80DB02AABD62B}, // 1e83\n\t{0x792667C6DA79E0FA, 0x83C7088E1AAB65DB}, // 1e84\n\t{0x577001B891185938, 0xA4B8CAB1A1563F52}, // 1e85\n\t{0xED4C0226B55E6F86, 0xCDE6FD5E09ABCF26}, // 1e86\n\t{0x544F8158315B05B4, 0x80B05E5AC60B6178}, // 1e87\n\t{0x696361AE3DB1C721, 0xA0DC75F1778E39D6}, // 1e88\n\t{0x03BC3A19CD1E38E9, 0xC913936DD571C84C}, // 1e89\n\t{0x04AB48A04065C723, 0xFB5878494ACE3A5F}, // 1e90\n\t{0x62EB0D64283F9C76, 0x9D174B2DCEC0E47B}, // 1e91\n\t{0x3BA5D0BD324F8394, 0xC45D1DF942711D9A}, // 1e92\n\t{0xCA8F44EC7EE36479, 0xF5746577930D6500}, // 1e93\n\t{0x7E998B13CF4E1ECB, 0x9968BF6ABBE85F20}, // 1e94\n\t{0x9E3FEDD8C321A67E, 0xBFC2EF456AE276E8}, // 1e95\n\t{0xC5CFE94EF3EA101E, 0xEFB3AB16C59B14A2}, // 1e96\n\t{0xBBA1F1D158724A12, 0x95D04AEE3B80ECE5}, // 1e97\n\t{0x2A8A6E45AE8EDC97, 0xBB445DA9CA61281F}, // 1e98\n\t{0xF52D09D71A3293BD, 0xEA1575143CF97226}, // 1e99\n\t{0x593C2626705F9C56, 0x924D692CA61BE758}, // 1e100\n\t{0x6F8B2FB00C77836C, 0xB6E0C377CFA2E12E}, // 1e101\n\t{0x0B6DFB9C0F956447, 0xE498F455C38B997A}, // 1e102\n\t{0x4724BD4189BD5EAC, 0x8EDF98B59A373FEC}, // 1e103\n\t{0x58EDEC91EC2CB657, 0xB2977EE300C50FE7}, // 1e104\n\t{0x2F2967B66737E3ED, 0xDF3D5E9BC0F653E1}, // 1e105\n\t{0xBD79E0D20082EE74, 0x8B865B215899F46C}, // 1e106\n\t{0xECD8590680A3AA11, 0xAE67F1E9AEC07187}, // 1e107\n\t{0xE80E6F4820CC9495, 0xDA01EE641A708DE9}, // 1e108\n\t{0x3109058D147FDCDD, 0x884134FE908658B2}, // 1e109\n\t{0xBD4B46F0599FD415, 0xAA51823E34A7EEDE}, // 1e110\n\t{0x6C9E18AC7007C91A, 0xD4E5E2CDC1D1EA96}, // 1e111\n\t{0x03E2CF6BC604DDB0, 0x850FADC09923329E}, // 1e112\n\t{0x84DB8346B786151C, 0xA6539930BF6BFF45}, // 1e113\n\t{0xE612641865679A63, 0xCFE87F7CEF46FF16}, // 1e114\n\t{0x4FCB7E8F3F60C07E, 0x81F14FAE158C5F6E}, // 1e115\n\t{0xE3BE5E330F38F09D, 0xA26DA3999AEF7749}, // 1e116\n\t{0x5CADF5BFD3072CC5, 0xCB090C8001AB551C}, // 1e117\n\t{0x73D9732FC7C8F7F6, 0xFDCB4FA002162A63}, // 1e118\n\t{0x2867E7FDDCDD9AFA, 0x9E9F11C4014DDA7E}, // 1e119\n\t{0xB281E1FD541501B8, 0xC646D63501A1511D}, // 1e120\n\t{0x1F225A7CA91A4226, 0xF7D88BC24209A565}, // 1e121\n\t{0x3375788DE9B06958, 0x9AE757596946075F}, // 1e122\n\t{0x0052D6B1641C83AE, 0xC1A12D2FC3978937}, // 1e123\n\t{0xC0678C5DBD23A49A, 0xF209787BB47D6B84}, // 1e124\n\t{0xF840B7BA963646E0, 0x9745EB4D50CE6332}, // 1e125\n\t{0xB650E5A93BC3D898, 0xBD176620A501FBFF}, // 1e126\n\t{0xA3E51F138AB4CEBE, 0xEC5D3FA8CE427AFF}, // 1e127\n\t{0xC66F336C36B10137, 0x93BA47C980E98CDF}, // 1e128\n\t{0xB80B0047445D4184, 0xB8A8D9BBE123F017}, // 1e129\n\t{0xA60DC059157491E5, 0xE6D3102AD96CEC1D}, // 1e130\n\t{0x87C89837AD68DB2F, 0x9043EA1AC7E41392}, // 1e131\n\t{0x29BABE4598C311FB, 0xB454E4A179DD1877}, // 1e132\n\t{0xF4296DD6FEF3D67A, 0xE16A1DC9D8545E94}, // 1e133\n\t{0x1899E4A65F58660C, 0x8CE2529E2734BB1D}, // 1e134\n\t{0x5EC05DCFF72E7F8F, 0xB01AE745B101E9E4}, // 1e135\n\t{0x76707543F4FA1F73, 0xDC21A1171D42645D}, // 1e136\n\t{0x6A06494A791C53A8, 0x899504AE72497EBA}, // 1e137\n\t{0x0487DB9D17636892, 0xABFA45DA0EDBDE69}, // 1e138\n\t{0x45A9D2845D3C42B6, 0xD6F8D7509292D603}, // 1e139\n\t{0x0B8A2392BA45A9B2, 0x865B86925B9BC5C2}, // 1e140\n\t{0x8E6CAC7768D7141E, 0xA7F26836F282B732}, // 1e141\n\t{0x3207D795430CD926, 0xD1EF0244AF2364FF}, // 1e142\n\t{0x7F44E6BD49E807B8, 0x8335616AED761F1F}, // 1e143\n\t{0x5F16206C9C6209A6, 0xA402B9C5A8D3A6E7}, // 1e144\n\t{0x36DBA887C37A8C0F, 0xCD036837130890A1}, // 1e145\n\t{0xC2494954DA2C9789, 0x802221226BE55A64}, // 1e146\n\t{0xF2DB9BAA10B7BD6C, 0xA02AA96B06DEB0FD}, // 1e147\n\t{0x6F92829494E5ACC7, 0xC83553C5C8965D3D}, // 1e148\n\t{0xCB772339BA1F17F9, 0xFA42A8B73ABBF48C}, // 1e149\n\t{0xFF2A760414536EFB, 0x9C69A97284B578D7}, // 1e150\n\t{0xFEF5138519684ABA, 0xC38413CF25E2D70D}, // 1e151\n\t{0x7EB258665FC25D69, 0xF46518C2EF5B8CD1}, // 1e152\n\t{0xEF2F773FFBD97A61, 0x98BF2F79D5993802}, // 1e153\n\t{0xAAFB550FFACFD8FA, 0xBEEEFB584AFF8603}, // 1e154\n\t{0x95BA2A53F983CF38, 0xEEAABA2E5DBF6784}, // 1e155\n\t{0xDD945A747BF26183, 0x952AB45CFA97A0B2}, // 1e156\n\t{0x94F971119AEEF9E4, 0xBA756174393D88DF}, // 1e157\n\t{0x7A37CD5601AAB85D, 0xE912B9D1478CEB17}, // 1e158\n\t{0xAC62E055C10AB33A, 0x91ABB422CCB812EE}, // 1e159\n\t{0x577B986B314D6009, 0xB616A12B7FE617AA}, // 1e160\n\t{0xED5A7E85FDA0B80B, 0xE39C49765FDF9D94}, // 1e161\n\t{0x14588F13BE847307, 0x8E41ADE9FBEBC27D}, // 1e162\n\t{0x596EB2D8AE258FC8, 0xB1D219647AE6B31C}, // 1e163\n\t{0x6FCA5F8ED9AEF3BB, 0xDE469FBD99A05FE3}, // 1e164\n\t{0x25DE7BB9480D5854, 0x8AEC23D680043BEE}, // 1e165\n\t{0xAF561AA79A10AE6A, 0xADA72CCC20054AE9}, // 1e166\n\t{0x1B2BA1518094DA04, 0xD910F7FF28069DA4}, // 1e167\n\t{0x90FB44D2F05D0842, 0x87AA9AFF79042286}, // 1e168\n\t{0x353A1607AC744A53, 0xA99541BF57452B28}, // 1e169\n\t{0x42889B8997915CE8, 0xD3FA922F2D1675F2}, // 1e170\n\t{0x69956135FEBADA11, 0x847C9B5D7C2E09B7}, // 1e171\n\t{0x43FAB9837E699095, 0xA59BC234DB398C25}, // 1e172\n\t{0x94F967E45E03F4BB, 0xCF02B2C21207EF2E}, // 1e173\n\t{0x1D1BE0EEBAC278F5, 0x8161AFB94B44F57D}, // 1e174\n\t{0x6462D92A69731732, 0xA1BA1BA79E1632DC}, // 1e175\n\t{0x7D7B8F7503CFDCFE, 0xCA28A291859BBF93}, // 1e176\n\t{0x5CDA735244C3D43E, 0xFCB2CB35E702AF78}, // 1e177\n\t{0x3A0888136AFA64A7, 0x9DEFBF01B061ADAB}, // 1e178\n\t{0x088AAA1845B8FDD0, 0xC56BAEC21C7A1916}, // 1e179\n\t{0x8AAD549E57273D45, 0xF6C69A72A3989F5B}, // 1e180\n\t{0x36AC54E2F678864B, 0x9A3C2087A63F6399}, // 1e181\n\t{0x84576A1BB416A7DD, 0xC0CB28A98FCF3C7F}, // 1e182\n\t{0x656D44A2A11C51D5, 0xF0FDF2D3F3C30B9F}, // 1e183\n\t{0x9F644AE5A4B1B325, 0x969EB7C47859E743}, // 1e184\n\t{0x873D5D9F0DDE1FEE, 0xBC4665B596706114}, // 1e185\n\t{0xA90CB506D155A7EA, 0xEB57FF22FC0C7959}, // 1e186\n\t{0x09A7F12442D588F2, 0x9316FF75DD87CBD8}, // 1e187\n\t{0x0C11ED6D538AEB2F, 0xB7DCBF5354E9BECE}, // 1e188\n\t{0x8F1668C8A86DA5FA, 0xE5D3EF282A242E81}, // 1e189\n\t{0xF96E017D694487BC, 0x8FA475791A569D10}, // 1e190\n\t{0x37C981DCC395A9AC, 0xB38D92D760EC4455}, // 1e191\n\t{0x85BBE253F47B1417, 0xE070F78D3927556A}, // 1e192\n\t{0x93956D7478CCEC8E, 0x8C469AB843B89562}, // 1e193\n\t{0x387AC8D1970027B2, 0xAF58416654A6BABB}, // 1e194\n\t{0x06997B05FCC0319E, 0xDB2E51BFE9D0696A}, // 1e195\n\t{0x441FECE3BDF81F03, 0x88FCF317F22241E2}, // 1e196\n\t{0xD527E81CAD7626C3, 0xAB3C2FDDEEAAD25A}, // 1e197\n\t{0x8A71E223D8D3B074, 0xD60B3BD56A5586F1}, // 1e198\n\t{0xF6872D5667844E49, 0x85C7056562757456}, // 1e199\n\t{0xB428F8AC016561DB, 0xA738C6BEBB12D16C}, // 1e200\n\t{0xE13336D701BEBA52, 0xD106F86E69D785C7}, // 1e201\n\t{0xECC0024661173473, 0x82A45B450226B39C}, // 1e202\n\t{0x27F002D7F95D0190, 0xA34D721642B06084}, // 1e203\n\t{0x31EC038DF7B441F4, 0xCC20CE9BD35C78A5}, // 1e204\n\t{0x7E67047175A15271, 0xFF290242C83396CE}, // 1e205\n\t{0x0F0062C6E984D386, 0x9F79A169BD203E41}, // 1e206\n\t{0x52C07B78A3E60868, 0xC75809C42C684DD1}, // 1e207\n\t{0xA7709A56CCDF8A82, 0xF92E0C3537826145}, // 1e208\n\t{0x88A66076400BB691, 0x9BBCC7A142B17CCB}, // 1e209\n\t{0x6ACFF893D00EA435, 0xC2ABF989935DDBFE}, // 1e210\n\t{0x0583F6B8C4124D43, 0xF356F7EBF83552FE}, // 1e211\n\t{0xC3727A337A8B704A, 0x98165AF37B2153DE}, // 1e212\n\t{0x744F18C0592E4C5C, 0xBE1BF1B059E9A8D6}, // 1e213\n\t{0x1162DEF06F79DF73, 0xEDA2EE1C7064130C}, // 1e214\n\t{0x8ADDCB5645AC2BA8, 0x9485D4D1C63E8BE7}, // 1e215\n\t{0x6D953E2BD7173692, 0xB9A74A0637CE2EE1}, // 1e216\n\t{0xC8FA8DB6CCDD0437, 0xE8111C87C5C1BA99}, // 1e217\n\t{0x1D9C9892400A22A2, 0x910AB1D4DB9914A0}, // 1e218\n\t{0x2503BEB6D00CAB4B, 0xB54D5E4A127F59C8}, // 1e219\n\t{0x2E44AE64840FD61D, 0xE2A0B5DC971F303A}, // 1e220\n\t{0x5CEAECFED289E5D2, 0x8DA471A9DE737E24}, // 1e221\n\t{0x7425A83E872C5F47, 0xB10D8E1456105DAD}, // 1e222\n\t{0xD12F124E28F77719, 0xDD50F1996B947518}, // 1e223\n\t{0x82BD6B70D99AAA6F, 0x8A5296FFE33CC92F}, // 1e224\n\t{0x636CC64D1001550B, 0xACE73CBFDC0BFB7B}, // 1e225\n\t{0x3C47F7E05401AA4E, 0xD8210BEFD30EFA5A}, // 1e226\n\t{0x65ACFAEC34810A71, 0x8714A775E3E95C78}, // 1e227\n\t{0x7F1839A741A14D0D, 0xA8D9D1535CE3B396}, // 1e228\n\t{0x1EDE48111209A050, 0xD31045A8341CA07C}, // 1e229\n\t{0x934AED0AAB460432, 0x83EA2B892091E44D}, // 1e230\n\t{0xF81DA84D5617853F, 0xA4E4B66B68B65D60}, // 1e231\n\t{0x36251260AB9D668E, 0xCE1DE40642E3F4B9}, // 1e232\n\t{0xC1D72B7C6B426019, 0x80D2AE83E9CE78F3}, // 1e233\n\t{0xB24CF65B8612F81F, 0xA1075A24E4421730}, // 1e234\n\t{0xDEE033F26797B627, 0xC94930AE1D529CFC}, // 1e235\n\t{0x169840EF017DA3B1, 0xFB9B7CD9A4A7443C}, // 1e236\n\t{0x8E1F289560EE864E, 0x9D412E0806E88AA5}, // 1e237\n\t{0xF1A6F2BAB92A27E2, 0xC491798A08A2AD4E}, // 1e238\n\t{0xAE10AF696774B1DB, 0xF5B5D7EC8ACB58A2}, // 1e239\n\t{0xACCA6DA1E0A8EF29, 0x9991A6F3D6BF1765}, // 1e240\n\t{0x17FD090A58D32AF3, 0xBFF610B0CC6EDD3F}, // 1e241\n\t{0xDDFC4B4CEF07F5B0, 0xEFF394DCFF8A948E}, // 1e242\n\t{0x4ABDAF101564F98E, 0x95F83D0A1FB69CD9}, // 1e243\n\t{0x9D6D1AD41ABE37F1, 0xBB764C4CA7A4440F}, // 1e244\n\t{0x84C86189216DC5ED, 0xEA53DF5FD18D5513}, // 1e245\n\t{0x32FD3CF5B4E49BB4, 0x92746B9BE2F8552C}, // 1e246\n\t{0x3FBC8C33221DC2A1, 0xB7118682DBB66A77}, // 1e247\n\t{0x0FABAF3FEAA5334A, 0xE4D5E82392A40515}, // 1e248\n\t{0x29CB4D87F2A7400E, 0x8F05B1163BA6832D}, // 1e249\n\t{0x743E20E9EF511012, 0xB2C71D5BCA9023F8}, // 1e250\n\t{0x914DA9246B255416, 0xDF78E4B2BD342CF6}, // 1e251\n\t{0x1AD089B6C2F7548E, 0x8BAB8EEFB6409C1A}, // 1e252\n\t{0xA184AC2473B529B1, 0xAE9672ABA3D0C320}, // 1e253\n\t{0xC9E5D72D90A2741E, 0xDA3C0F568CC4F3E8}, // 1e254\n\t{0x7E2FA67C7A658892, 0x8865899617FB1871}, // 1e255\n\t{0xDDBB901B98FEEAB7, 0xAA7EEBFB9DF9DE8D}, // 1e256\n\t{0x552A74227F3EA565, 0xD51EA6FA85785631}, // 1e257\n\t{0xD53A88958F87275F, 0x8533285C936B35DE}, // 1e258\n\t{0x8A892ABAF368F137, 0xA67FF273B8460356}, // 1e259\n\t{0x2D2B7569B0432D85, 0xD01FEF10A657842C}, // 1e260\n\t{0x9C3B29620E29FC73, 0x8213F56A67F6B29B}, // 1e261\n\t{0x8349F3BA91B47B8F, 0xA298F2C501F45F42}, // 1e262\n\t{0x241C70A936219A73, 0xCB3F2F7642717713}, // 1e263\n\t{0xED238CD383AA0110, 0xFE0EFB53D30DD4D7}, // 1e264\n\t{0xF4363804324A40AA, 0x9EC95D1463E8A506}, // 1e265\n\t{0xB143C6053EDCD0D5, 0xC67BB4597CE2CE48}, // 1e266\n\t{0xDD94B7868E94050A, 0xF81AA16FDC1B81DA}, // 1e267\n\t{0xCA7CF2B4191C8326, 0x9B10A4E5E9913128}, // 1e268\n\t{0xFD1C2F611F63A3F0, 0xC1D4CE1F63F57D72}, // 1e269\n\t{0xBC633B39673C8CEC, 0xF24A01A73CF2DCCF}, // 1e270\n\t{0xD5BE0503E085D813, 0x976E41088617CA01}, // 1e271\n\t{0x4B2D8644D8A74E18, 0xBD49D14AA79DBC82}, // 1e272\n\t{0xDDF8E7D60ED1219E, 0xEC9C459D51852BA2}, // 1e273\n\t{0xCABB90E5C942B503, 0x93E1AB8252F33B45}, // 1e274\n\t{0x3D6A751F3B936243, 0xB8DA1662E7B00A17}, // 1e275\n\t{0x0CC512670A783AD4, 0xE7109BFBA19C0C9D}, // 1e276\n\t{0x27FB2B80668B24C5, 0x906A617D450187E2}, // 1e277\n\t{0xB1F9F660802DEDF6, 0xB484F9DC9641E9DA}, // 1e278\n\t{0x5E7873F8A0396973, 0xE1A63853BBD26451}, // 1e279\n\t{0xDB0B487B6423E1E8, 0x8D07E33455637EB2}, // 1e280\n\t{0x91CE1A9A3D2CDA62, 0xB049DC016ABC5E5F}, // 1e281\n\t{0x7641A140CC7810FB, 0xDC5C5301C56B75F7}, // 1e282\n\t{0xA9E904C87FCB0A9D, 0x89B9B3E11B6329BA}, // 1e283\n\t{0x546345FA9FBDCD44, 0xAC2820D9623BF429}, // 1e284\n\t{0xA97C177947AD4095, 0xD732290FBACAF133}, // 1e285\n\t{0x49ED8EABCCCC485D, 0x867F59A9D4BED6C0}, // 1e286\n\t{0x5C68F256BFFF5A74, 0xA81F301449EE8C70}, // 1e287\n\t{0x73832EEC6FFF3111, 0xD226FC195C6A2F8C}, // 1e288\n\t{0xC831FD53C5FF7EAB, 0x83585D8FD9C25DB7}, // 1e289\n\t{0xBA3E7CA8B77F5E55, 0xA42E74F3D032F525}, // 1e290\n\t{0x28CE1BD2E55F35EB, 0xCD3A1230C43FB26F}, // 1e291\n\t{0x7980D163CF5B81B3, 0x80444B5E7AA7CF85}, // 1e292\n\t{0xD7E105BCC332621F, 0xA0555E361951C366}, // 1e293\n\t{0x8DD9472BF3FEFAA7, 0xC86AB5C39FA63440}, // 1e294\n\t{0xB14F98F6F0FEB951, 0xFA856334878FC150}, // 1e295\n\t{0x6ED1BF9A569F33D3, 0x9C935E00D4B9D8D2}, // 1e296\n\t{0x0A862F80EC4700C8, 0xC3B8358109E84F07}, // 1e297\n\t{0xCD27BB612758C0FA, 0xF4A642E14C6262C8}, // 1e298\n\t{0x8038D51CB897789C, 0x98E7E9CCCFBD7DBD}, // 1e299\n\t{0xE0470A63E6BD56C3, 0xBF21E44003ACDD2C}, // 1e300\n\t{0x1858CCFCE06CAC74, 0xEEEA5D5004981478}, // 1e301\n\t{0x0F37801E0C43EBC8, 0x95527A5202DF0CCB}, // 1e302\n\t{0xD30560258F54E6BA, 0xBAA718E68396CFFD}, // 1e303\n\t{0x47C6B82EF32A2069, 0xE950DF20247C83FD}, // 1e304\n\t{0x4CDC331D57FA5441, 0x91D28B7416CDD27E}, // 1e305\n\t{0xE0133FE4ADF8E952, 0xB6472E511C81471D}, // 1e306\n\t{0x58180FDDD97723A6, 0xE3D8F9E563A198E5}, // 1e307\n\t{0x570F09EAA7EA7648, 0x8E679C2F5E44FF8F}, // 1e308\n\t{0x2CD2CC6551E513DA, 0xB201833B35D63F73}, // 1e309\n\t{0xF8077F7EA65E58D1, 0xDE81E40A034BCF4F}, // 1e310\n\t{0xFB04AFAF27FAF782, 0x8B112E86420F6191}, // 1e311\n\t{0x79C5DB9AF1F9B563, 0xADD57A27D29339F6}, // 1e312\n\t{0x18375281AE7822BC, 0xD94AD8B1C7380874}, // 1e313\n\t{0x8F2293910D0B15B5, 0x87CEC76F1C830548}, // 1e314\n\t{0xB2EB3875504DDB22, 0xA9C2794AE3A3C69A}, // 1e315\n\t{0x5FA60692A46151EB, 0xD433179D9C8CB841}, // 1e316\n\t{0xDBC7C41BA6BCD333, 0x849FEEC281D7F328}, // 1e317\n\t{0x12B9B522906C0800, 0xA5C7EA73224DEFF3}, // 1e318\n\t{0xD768226B34870A00, 0xCF39E50FEAE16BEF}, // 1e319\n\t{0xE6A1158300D46640, 0x81842F29F2CCE375}, // 1e320\n\t{0x60495AE3C1097FD0, 0xA1E53AF46F801C53}, // 1e321\n\t{0x385BB19CB14BDFC4, 0xCA5E89B18B602368}, // 1e322\n\t{0x46729E03DD9ED7B5, 0xFCF62C1DEE382C42}, // 1e323\n\t{0x6C07A2C26A8346D1, 0x9E19DB92B4E31BA9}, // 1e324\n\t{0xC7098B7305241885, 0xC5A05277621BE293}, // 1e325\n\t{0xB8CBEE4FC66D1EA7, 0xF70867153AA2DB38}, // 1e326\n\t{0x737F74F1DC043328, 0x9A65406D44A5C903}, // 1e327\n\t{0x505F522E53053FF2, 0xC0FE908895CF3B44}, // 1e328\n\t{0x647726B9E7C68FEF, 0xF13E34AABB430A15}, // 1e329\n\t{0x5ECA783430DC19F5, 0x96C6E0EAB509E64D}, // 1e330\n\t{0xB67D16413D132072, 0xBC789925624C5FE0}, // 1e331\n\t{0xE41C5BD18C57E88F, 0xEB96BF6EBADF77D8}, // 1e332\n\t{0x8E91B962F7B6F159, 0x933E37A534CBAAE7}, // 1e333\n\t{0x723627BBB5A4ADB0, 0xB80DC58E81FE95A1}, // 1e334\n\t{0xCEC3B1AAA30DD91C, 0xE61136F2227E3B09}, // 1e335\n\t{0x213A4F0AA5E8A7B1, 0x8FCAC257558EE4E6}, // 1e336\n\t{0xA988E2CD4F62D19D, 0xB3BD72ED2AF29E1F}, // 1e337\n\t{0x93EB1B80A33B8605, 0xE0ACCFA875AF45A7}, // 1e338\n\t{0xBC72F130660533C3, 0x8C6C01C9498D8B88}, // 1e339\n\t{0xEB8FAD7C7F8680B4, 0xAF87023B9BF0EE6A}, // 1e340\n\t{0xA67398DB9F6820E1, 0xDB68C2CA82ED2A05}, // 1e341\n\t{0x88083F8943A1148C, 0x892179BE91D43A43}, // 1e342\n\t{0x6A0A4F6B948959B0, 0xAB69D82E364948D4}, // 1e343\n\t{0x848CE34679ABB01C, 0xD6444E39C3DB9B09}, // 1e344\n\t{0xF2D80E0C0C0B4E11, 0x85EAB0E41A6940E5}, // 1e345\n\t{0x6F8E118F0F0E2195, 0xA7655D1D2103911F}, // 1e346\n\t{0x4B7195F2D2D1A9FB, 0xD13EB46469447567}, // 1e347\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/json/eisel_lemire\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "ryu", + "path": "gno.land/p/demo/json/ryu", + "files": [ + { + "name": "floatconv.gno", + "body": "// Copyright 2018 Ulf Adams\n// Modifications copyright 2019 Caleb Spare\n//\n// The contents of this file may be used under the terms of the Apache License,\n// Version 2.0.\n//\n// (See accompanying file LICENSE or copy at\n// http://www.apache.org/licenses/LICENSE-2.0)\n//\n// Unless required by applicable law or agreed to in writing, this software\n// is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n// KIND, either express or implied.\n//\n// The code in this file is part of a Go translation of the C code originally written by\n// Ulf Adams, which can be found at https://github.com/ulfjack/ryu. The original source\n// code is licensed under the Apache License 2.0. This code is a derivative work thereof,\n// adapted and modified to meet the specifications of the Gno language project.\n//\n// original Go implementation can be found at https://github.com/cespare/ryu.\n//\n// Please note that the modifications are also under the Apache License 2.0 unless\n// otherwise specified.\n\n// Package ryu implements the Ryu algorithm for quickly converting floating\n// point numbers into strings.\npackage ryu\n\nimport (\n\t\"math\"\n)\n\nconst (\n\tmantBits32 = 23\n\texpBits32 = 8\n\tbias32 = 127\n\n\tmantBits64 = 52\n\texpBits64 = 11\n\tbias64 = 1023\n)\n\n// FormatFloat64 converts a 64-bit floating point number f to a string.\n// It behaves like strconv.FormatFloat(f, 'e', -1, 64).\nfunc FormatFloat64(f float64) string {\n\tb := make([]byte, 0, 24)\n\tb = AppendFloat64(b, f)\n\treturn string(b)\n}\n\n// AppendFloat64 appends the string form of the 64-bit floating point number f,\n// as generated by FormatFloat64, to b and returns the extended buffer.\nfunc AppendFloat64(b []byte, f float64) []byte {\n\t// Step 1: Decode the floating-point number.\n\t// Unify normalized and subnormal cases.\n\tu := math.Float64bits(f)\n\tneg := u\u003e\u003e(mantBits64+expBits64) != 0\n\tmant := u \u0026 (uint64(1)\u003c\u003cmantBits64 - 1)\n\texp := (u \u003e\u003e mantBits64) \u0026 (uint64(1)\u003c\u003cexpBits64 - 1)\n\n\t// Exit early for easy cases.\n\tif exp == uint64(1)\u003c\u003cexpBits64-1 || (exp == 0 \u0026\u0026 mant == 0) {\n\t\treturn appendSpecial(b, neg, exp == 0, mant == 0)\n\t}\n\n\td, ok := float64ToDecimalExactInt(mant, exp)\n\tif !ok {\n\t\td = float64ToDecimal(mant, exp)\n\t}\n\treturn d.append(b, neg)\n}\n\nfunc appendSpecial(b []byte, neg, expZero, mantZero bool) []byte {\n\tif !mantZero {\n\t\treturn append(b, \"NaN\"...)\n\t}\n\tif !expZero {\n\t\tif neg {\n\t\t\treturn append(b, \"-Inf\"...)\n\t\t}\n\n\t\treturn append(b, \"+Inf\"...)\n\t}\n\n\tif neg {\n\t\tb = append(b, '-')\n\t}\n\treturn append(b, \"0e+00\"...)\n}\n\nfunc boolToInt(b bool) int {\n\tif b {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\nfunc boolToUint32(b bool) uint32 {\n\tif b {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\nfunc boolToUint64(b bool) uint64 {\n\tif b {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\nfunc assert(t bool, msg string) {\n\tif !t {\n\t\tpanic(msg)\n\t}\n}\n\n// log10Pow2 returns floor(log_10(2^e)).\nfunc log10Pow2(e int32) uint32 {\n\t// The first value this approximation fails for is 2^1651\n\t// which is just greater than 10^297.\n\tassert(e \u003e= 0, \"e \u003e= 0\")\n\tassert(e \u003c= 1650, \"e \u003c= 1650\")\n\treturn (uint32(e) * 78913) \u003e\u003e 18\n}\n\n// log10Pow5 returns floor(log_10(5^e)).\nfunc log10Pow5(e int32) uint32 {\n\t// The first value this approximation fails for is 5^2621\n\t// which is just greater than 10^1832.\n\tassert(e \u003e= 0, \"e \u003e= 0\")\n\tassert(e \u003c= 2620, \"e \u003c= 2620\")\n\treturn (uint32(e) * 732923) \u003e\u003e 20\n}\n\n// pow5Bits returns ceil(log_2(5^e)), or else 1 if e==0.\nfunc pow5Bits(e int32) int32 {\n\t// This approximation works up to the point that the multiplication\n\t// overflows at e = 3529. If the multiplication were done in 64 bits,\n\t// it would fail at 5^4004 which is just greater than 2^9297.\n\tassert(e \u003e= 0, \"e \u003e= 0\")\n\tassert(e \u003c= 3528, \"e \u003c= 3528\")\n\treturn int32((uint32(e)*1217359)\u003e\u003e19 + 1)\n}\n" + }, + { + "name": "floatconv_test.gno", + "body": "package ryu\n\nimport (\n\t\"math\"\n\t\"testing\"\n)\n\nfunc TestFormatFloat64(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tvalue float64\n\t\texpected string\n\t}{\n\t\t{\"positive infinity\", math.Inf(1), \"+Inf\"},\n\t\t{\"negative infinity\", math.Inf(-1), \"-Inf\"},\n\t\t{\"NaN\", math.NaN(), \"NaN\"},\n\t\t{\"zero\", 0.0, \"0e+00\"},\n\t\t{\"negative zero\", -0.0, \"0e+00\"},\n\t\t{\"positive number\", 3.14159, \"3.14159e+00\"},\n\t\t{\"negative number\", -2.71828, \"-2.71828e+00\"},\n\t\t{\"very small number\", 1.23e-20, \"1.23e-20\"},\n\t\t{\"very large number\", 1.23e+20, \"1.23e+20\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tresult := FormatFloat64(test.value)\n\t\t\tif result != test.expected {\n\t\t\t\tt.Errorf(\"FormatFloat64(%v) = %q, expected %q\", test.value, result, test.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/json/ryu\n" + }, + { + "name": "ryu64.gno", + "body": "package ryu\n\nimport (\n\t\"math/bits\"\n)\n\ntype uint128 struct {\n\tlo uint64\n\thi uint64\n}\n\n// dec64 is a floating decimal type representing m * 10^e.\ntype dec64 struct {\n\tm uint64\n\te int32\n}\n\nfunc (d dec64) append(b []byte, neg bool) []byte {\n\t// Step 5: Print the decimal representation.\n\tif neg {\n\t\tb = append(b, '-')\n\t}\n\n\tout := d.m\n\toutLen := decimalLen64(out)\n\tbufLen := outLen\n\tif bufLen \u003e 1 {\n\t\tbufLen++ // extra space for '.'\n\t}\n\n\t// Print the decimal digits.\n\tn := len(b)\n\tif cap(b)-len(b) \u003e= bufLen {\n\t\t// Avoid function call in the common case.\n\t\tb = b[:len(b)+bufLen]\n\t} else {\n\t\tb = append(b, make([]byte, bufLen)...)\n\t}\n\n\t// Avoid expensive 64-bit divisions.\n\t// We have at most 17 digits, and uint32 can store 9 digits.\n\t// If the output doesn't fit into a uint32, cut off 8 digits\n\t// so the rest will fit into a uint32.\n\tvar i int\n\tif out\u003e\u003e32 \u003e 0 {\n\t\tvar out32 uint32\n\t\tout, out32 = out/1e8, uint32(out%1e8)\n\t\tfor ; i \u003c 8; i++ {\n\t\t\tb[n+outLen-i] = '0' + byte(out32%10)\n\t\t\tout32 /= 10\n\t\t}\n\t}\n\tout32 := uint32(out)\n\tfor ; i \u003c outLen-1; i++ {\n\t\tb[n+outLen-i] = '0' + byte(out32%10)\n\t\tout32 /= 10\n\t}\n\tb[n] = '0' + byte(out32%10)\n\n\t// Print the '.' if needed.\n\tif outLen \u003e 1 {\n\t\tb[n+1] = '.'\n\t}\n\n\t// Print the exponent.\n\tb = append(b, 'e')\n\texp := d.e + int32(outLen) - 1\n\tif exp \u003c 0 {\n\t\tb = append(b, '-')\n\t\texp = -exp\n\t} else {\n\t\t// Unconditionally print a + here to match strconv's formatting.\n\t\tb = append(b, '+')\n\t}\n\t// Always print at least two digits to match strconv's formatting.\n\td2 := exp % 10\n\texp /= 10\n\td1 := exp % 10\n\td0 := exp / 10\n\tif d0 \u003e 0 {\n\t\tb = append(b, '0'+byte(d0))\n\t}\n\tb = append(b, '0'+byte(d1), '0'+byte(d2))\n\n\treturn b\n}\n\nfunc float64ToDecimalExactInt(mant, exp uint64) (d dec64, ok bool) {\n\te := exp - bias64\n\tif e \u003e mantBits64 {\n\t\treturn d, false\n\t}\n\tshift := mantBits64 - e\n\tmant |= 1 \u003c\u003c mantBits64 // implicit 1\n\td.m = mant \u003e\u003e shift\n\tif d.m\u003c\u003cshift != mant {\n\t\treturn d, false\n\t}\n\n\tfor d.m%10 == 0 {\n\t\td.m /= 10\n\t\td.e++\n\t}\n\treturn d, true\n}\n\nfunc float64ToDecimal(mant, exp uint64) dec64 {\n\tvar e2 int32\n\tvar m2 uint64\n\tif exp == 0 {\n\t\t// We subtract 2 so that the bounds computation has\n\t\t// 2 additional bits.\n\t\te2 = 1 - bias64 - mantBits64 - 2\n\t\tm2 = mant\n\t} else {\n\t\te2 = int32(exp) - bias64 - mantBits64 - 2\n\t\tm2 = uint64(1)\u003c\u003cmantBits64 | mant\n\t}\n\teven := m2\u00261 == 0\n\tacceptBounds := even\n\n\t// Step 2: Determine the interval of valid decimal representations.\n\tmv := 4 * m2\n\tmmShift := boolToUint64(mant != 0 || exp \u003c= 1)\n\t// We would compute mp and mm like this:\n\t// mp := 4 * m2 + 2;\n\t// mm := mv - 1 - mmShift;\n\n\t// Step 3: Convert to a decimal power base uing 128-bit arithmetic.\n\tvar (\n\t\tvr, vp, vm uint64\n\t\te10 int32\n\t\tvmIsTrailingZeros bool\n\t\tvrIsTrailingZeros bool\n\t)\n\tif e2 \u003e= 0 {\n\t\t// This expression is slightly faster than max(0, log10Pow2(e2) - 1).\n\t\tq := log10Pow2(e2) - boolToUint32(e2 \u003e 3)\n\t\te10 = int32(q)\n\t\tk := pow5InvNumBits64 + pow5Bits(int32(q)) - 1\n\t\ti := -e2 + int32(q) + k\n\t\tmul := pow5InvSplit64[q]\n\t\tvr = mulShift64(4*m2, mul, i)\n\t\tvp = mulShift64(4*m2+2, mul, i)\n\t\tvm = mulShift64(4*m2-1-mmShift, mul, i)\n\t\tif q \u003c= 21 {\n\t\t\t// This should use q \u003c= 22, but I think 21 is also safe.\n\t\t\t// Smaller values may still be safe, but it's more\n\t\t\t// difficult to reason about them. Only one of mp, mv,\n\t\t\t// and mm can be a multiple of 5, if any.\n\t\t\tif mv%5 == 0 {\n\t\t\t\tvrIsTrailingZeros = multipleOfPowerOfFive64(mv, q)\n\t\t\t} else if acceptBounds {\n\t\t\t\t// Same as min(e2 + (^mm \u0026 1), pow5Factor64(mm)) \u003e= q\n\t\t\t\t// \u003c=\u003e e2 + (^mm \u0026 1) \u003e= q \u0026\u0026 pow5Factor64(mm) \u003e= q\n\t\t\t\t// \u003c=\u003e true \u0026\u0026 pow5Factor64(mm) \u003e= q, since e2 \u003e= q.\n\t\t\t\tvmIsTrailingZeros = multipleOfPowerOfFive64(mv-1-mmShift, q)\n\t\t\t} else if multipleOfPowerOfFive64(mv+2, q) {\n\t\t\t\tvp--\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// This expression is slightly faster than max(0, log10Pow5(-e2) - 1).\n\t\tq := log10Pow5(-e2) - boolToUint32(-e2 \u003e 1)\n\t\te10 = int32(q) + e2\n\t\ti := -e2 - int32(q)\n\t\tk := pow5Bits(i) - pow5NumBits64\n\t\tj := int32(q) - k\n\t\tmul := pow5Split64[i]\n\t\tvr = mulShift64(4*m2, mul, j)\n\t\tvp = mulShift64(4*m2+2, mul, j)\n\t\tvm = mulShift64(4*m2-1-mmShift, mul, j)\n\t\tif q \u003c= 1 {\n\t\t\t// {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.\n\t\t\t// mv = 4 * m2, so it always has at least two trailing 0 bits.\n\t\t\tvrIsTrailingZeros = true\n\t\t\tif acceptBounds {\n\t\t\t\t// mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1.\n\t\t\t\tvmIsTrailingZeros = mmShift == 1\n\t\t\t} else {\n\t\t\t\t// mp = mv + 2, so it always has at least one trailing 0 bit.\n\t\t\t\tvp--\n\t\t\t}\n\t\t} else if q \u003c 63 { // TODO(ulfjack/cespare): Use a tighter bound here.\n\t\t\t// We need to compute min(ntz(mv), pow5Factor64(mv) - e2) \u003e= q - 1\n\t\t\t// \u003c=\u003e ntz(mv) \u003e= q - 1 \u0026\u0026 pow5Factor64(mv) - e2 \u003e= q - 1\n\t\t\t// \u003c=\u003e ntz(mv) \u003e= q - 1 (e2 is negative and -e2 \u003e= q)\n\t\t\t// \u003c=\u003e (mv \u0026 ((1 \u003c\u003c (q - 1)) - 1)) == 0\n\t\t\t// We also need to make sure that the left shift does not overflow.\n\t\t\tvrIsTrailingZeros = multipleOfPowerOfTwo64(mv, q-1)\n\t\t}\n\t}\n\n\t// Step 4: Find the shortest decimal representation\n\t// in the interval of valid representations.\n\tvar removed int32\n\tvar lastRemovedDigit uint8\n\tvar out uint64\n\t// On average, we remove ~2 digits.\n\tif vmIsTrailingZeros || vrIsTrailingZeros {\n\t\t// General case, which happens rarely (~0.7%).\n\t\tfor {\n\t\t\tvpDiv10 := vp / 10\n\t\t\tvmDiv10 := vm / 10\n\t\t\tif vpDiv10 \u003c= vmDiv10 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvmMod10 := vm % 10\n\t\t\tvrDiv10 := vr / 10\n\t\t\tvrMod10 := vr % 10\n\t\t\tvmIsTrailingZeros = vmIsTrailingZeros \u0026\u0026 vmMod10 == 0\n\t\t\tvrIsTrailingZeros = vrIsTrailingZeros \u0026\u0026 lastRemovedDigit == 0\n\t\t\tlastRemovedDigit = uint8(vrMod10)\n\t\t\tvr = vrDiv10\n\t\t\tvp = vpDiv10\n\t\t\tvm = vmDiv10\n\t\t\tremoved++\n\t\t}\n\t\tif vmIsTrailingZeros {\n\t\t\tfor {\n\t\t\t\tvmDiv10 := vm / 10\n\t\t\t\tvmMod10 := vm % 10\n\t\t\t\tif vmMod10 != 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tvpDiv10 := vp / 10\n\t\t\t\tvrDiv10 := vr / 10\n\t\t\t\tvrMod10 := vr % 10\n\t\t\t\tvrIsTrailingZeros = vrIsTrailingZeros \u0026\u0026 lastRemovedDigit == 0\n\t\t\t\tlastRemovedDigit = uint8(vrMod10)\n\t\t\t\tvr = vrDiv10\n\t\t\t\tvp = vpDiv10\n\t\t\t\tvm = vmDiv10\n\t\t\t\tremoved++\n\t\t\t}\n\t\t}\n\t\tif vrIsTrailingZeros \u0026\u0026 lastRemovedDigit == 5 \u0026\u0026 vr%2 == 0 {\n\t\t\t// Round even if the exact number is .....50..0.\n\t\t\tlastRemovedDigit = 4\n\t\t}\n\t\tout = vr\n\t\t// We need to take vr + 1 if vr is outside bounds\n\t\t// or we need to round up.\n\t\tif (vr == vm \u0026\u0026 (!acceptBounds || !vmIsTrailingZeros)) || lastRemovedDigit \u003e= 5 {\n\t\t\tout++\n\t\t}\n\t} else {\n\t\t// Specialized for the common case (~99.3%).\n\t\t// Percentages below are relative to this.\n\t\troundUp := false\n\t\tfor vp/100 \u003e vm/100 {\n\t\t\t// Optimization: remove two digits at a time (~86.2%).\n\t\t\troundUp = vr%100 \u003e= 50\n\t\t\tvr /= 100\n\t\t\tvp /= 100\n\t\t\tvm /= 100\n\t\t\tremoved += 2\n\t\t}\n\t\t// Loop iterations below (approximately), without optimization above:\n\t\t// 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%\n\t\t// Loop iterations below (approximately), with optimization above:\n\t\t// 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%\n\t\tfor vp/10 \u003e vm/10 {\n\t\t\troundUp = vr%10 \u003e= 5\n\t\t\tvr /= 10\n\t\t\tvp /= 10\n\t\t\tvm /= 10\n\t\t\tremoved++\n\t\t}\n\t\t// We need to take vr + 1 if vr is outside bounds\n\t\t// or we need to round up.\n\t\tout = vr + boolToUint64(vr == vm || roundUp)\n\t}\n\n\treturn dec64{m: out, e: e10 + removed}\n}\n\nvar powersOf10 = [...]uint64{\n\t1e0,\n\t1e1,\n\t1e2,\n\t1e3,\n\t1e4,\n\t1e5,\n\t1e6,\n\t1e7,\n\t1e8,\n\t1e9,\n\t1e10,\n\t1e11,\n\t1e12,\n\t1e13,\n\t1e14,\n\t1e15,\n\t1e16,\n\t1e17,\n\t// We only need to find the length of at most 17 digit numbers.\n}\n\nfunc decimalLen64(u uint64) int {\n\t// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10\n\tlog2 := 64 - bits.LeadingZeros64(u) - 1\n\tt := (log2 + 1) * 1233 \u003e\u003e 12\n\treturn t - boolToInt(u \u003c powersOf10[t]) + 1\n}\n\nfunc mulShift64(m uint64, mul uint128, shift int32) uint64 {\n\thihi, hilo := bits.Mul64(m, mul.hi)\n\tlohi, _ := bits.Mul64(m, mul.lo)\n\tsum := uint128{hi: hihi, lo: lohi + hilo}\n\tif sum.lo \u003c lohi {\n\t\tsum.hi++ // overflow\n\t}\n\treturn shiftRight128(sum, shift-64)\n}\n\nfunc shiftRight128(v uint128, shift int32) uint64 {\n\t// The shift value is always modulo 64.\n\t// In the current implementation of the 64-bit version\n\t// of Ryu, the shift value is always \u003c 64.\n\t// (It is in the range [2, 59].)\n\t// Check this here in case a future change requires larger shift\n\t// values. In this case this function needs to be adjusted.\n\tassert(shift \u003c 64, \"shift \u003c 64\")\n\treturn (v.hi \u003c\u003c uint64(64-shift)) | (v.lo \u003e\u003e uint(shift))\n}\n\nfunc pow5Factor64(v uint64) uint32 {\n\tfor n := uint32(0); ; n++ {\n\t\tq, r := v/5, v%5\n\t\tif r != 0 {\n\t\t\treturn n\n\t\t}\n\t\tv = q\n\t}\n}\n\nfunc multipleOfPowerOfFive64(v uint64, p uint32) bool {\n\treturn pow5Factor64(v) \u003e= p\n}\n\nfunc multipleOfPowerOfTwo64(v uint64, p uint32) bool {\n\treturn uint32(bits.TrailingZeros64(v)) \u003e= p\n}\n" + }, + { + "name": "table.gno", + "body": "// Code generated by running \"go generate\". DO NOT EDIT.\n\n// Copyright 2018 Ulf Adams\n// Modifications copyright 2019 Caleb Spare\n//\n// The contents of this file may be used under the terms of the Apache License,\n// Version 2.0.\n//\n// (See accompanying file LICENSE or copy at\n// http://www.apache.org/licenses/LICENSE-2.0)\n//\n// Unless required by applicable law or agreed to in writing, this software\n// is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n// KIND, either express or implied.\n//\n// The code in this file is part of a Go translation of the C code written by\n// Ulf Adams which may be found at https://github.com/ulfjack/ryu. That source\n// code is licensed under Apache 2.0 and this code is derivative work thereof.\n\npackage ryu\n\nconst pow5NumBits32 = 61\n\nvar pow5Split32 = [...]uint64{\n\t1152921504606846976, 1441151880758558720, 1801439850948198400, 2251799813685248000,\n\t1407374883553280000, 1759218604441600000, 2199023255552000000, 1374389534720000000,\n\t1717986918400000000, 2147483648000000000, 1342177280000000000, 1677721600000000000,\n\t2097152000000000000, 1310720000000000000, 1638400000000000000, 2048000000000000000,\n\t1280000000000000000, 1600000000000000000, 2000000000000000000, 1250000000000000000,\n\t1562500000000000000, 1953125000000000000, 1220703125000000000, 1525878906250000000,\n\t1907348632812500000, 1192092895507812500, 1490116119384765625, 1862645149230957031,\n\t1164153218269348144, 1455191522836685180, 1818989403545856475, 2273736754432320594,\n\t1421085471520200371, 1776356839400250464, 2220446049250313080, 1387778780781445675,\n\t1734723475976807094, 2168404344971008868, 1355252715606880542, 1694065894508600678,\n\t2117582368135750847, 1323488980084844279, 1654361225106055349, 2067951531382569187,\n\t1292469707114105741, 1615587133892632177, 2019483917365790221,\n}\n\nconst pow5InvNumBits32 = 59\n\nvar pow5InvSplit32 = [...]uint64{\n\t576460752303423489, 461168601842738791, 368934881474191033, 295147905179352826,\n\t472236648286964522, 377789318629571618, 302231454903657294, 483570327845851670,\n\t386856262276681336, 309485009821345069, 495176015714152110, 396140812571321688,\n\t316912650057057351, 507060240091291761, 405648192073033409, 324518553658426727,\n\t519229685853482763, 415383748682786211, 332306998946228969, 531691198313966350,\n\t425352958651173080, 340282366920938464, 544451787073501542, 435561429658801234,\n\t348449143727040987, 557518629963265579, 446014903970612463, 356811923176489971,\n\t570899077082383953, 456719261665907162, 365375409332725730,\n}\n\nconst pow5NumBits64 = 121\n\nvar pow5Split64 = [...]uint128{\n\t{0, 72057594037927936},\n\t{0, 90071992547409920},\n\t{0, 112589990684262400},\n\t{0, 140737488355328000},\n\t{0, 87960930222080000},\n\t{0, 109951162777600000},\n\t{0, 137438953472000000},\n\t{0, 85899345920000000},\n\t{0, 107374182400000000},\n\t{0, 134217728000000000},\n\t{0, 83886080000000000},\n\t{0, 104857600000000000},\n\t{0, 131072000000000000},\n\t{0, 81920000000000000},\n\t{0, 102400000000000000},\n\t{0, 128000000000000000},\n\t{0, 80000000000000000},\n\t{0, 100000000000000000},\n\t{0, 125000000000000000},\n\t{0, 78125000000000000},\n\t{0, 97656250000000000},\n\t{0, 122070312500000000},\n\t{0, 76293945312500000},\n\t{0, 95367431640625000},\n\t{0, 119209289550781250},\n\t{4611686018427387904, 74505805969238281},\n\t{10376293541461622784, 93132257461547851},\n\t{8358680908399640576, 116415321826934814},\n\t{612489549322387456, 72759576141834259},\n\t{14600669991935148032, 90949470177292823},\n\t{13639151471491547136, 113686837721616029},\n\t{3213881284082270208, 142108547152020037},\n\t{4314518811765112832, 88817841970012523},\n\t{781462496279003136, 111022302462515654},\n\t{10200200157203529728, 138777878078144567},\n\t{13292654125893287936, 86736173798840354},\n\t{7392445620511834112, 108420217248550443},\n\t{4628871007212404736, 135525271560688054},\n\t{16728102434789916672, 84703294725430033},\n\t{7075069988205232128, 105879118406787542},\n\t{18067209522111315968, 132348898008484427},\n\t{8986162942105878528, 82718061255302767},\n\t{6621017659204960256, 103397576569128459},\n\t{3664586055578812416, 129246970711410574},\n\t{16125424340018921472, 80779356694631608},\n\t{1710036351314100224, 100974195868289511},\n\t{15972603494424788992, 126217744835361888},\n\t{9982877184015493120, 78886090522101180},\n\t{12478596480019366400, 98607613152626475},\n\t{10986559581596820096, 123259516440783094},\n\t{2254913720070624656, 77037197775489434},\n\t{12042014186943056628, 96296497219361792},\n\t{15052517733678820785, 120370621524202240},\n\t{9407823583549262990, 75231638452626400},\n\t{11759779479436578738, 94039548065783000},\n\t{14699724349295723422, 117549435082228750},\n\t{4575641699882439235, 73468396926392969},\n\t{10331238143280436948, 91835496157991211},\n\t{8302361660673158281, 114794370197489014},\n\t{1154580038986672043, 143492962746861268},\n\t{9944984561221445835, 89683101716788292},\n\t{12431230701526807293, 112103877145985365},\n\t{1703980321626345405, 140129846432481707},\n\t{17205888765512323542, 87581154020301066},\n\t{12283988920035628619, 109476442525376333},\n\t{1519928094762372062, 136845553156720417},\n\t{12479170105294952299, 85528470722950260},\n\t{15598962631618690374, 106910588403687825},\n\t{5663645234241199255, 133638235504609782},\n\t{17374836326682913246, 83523897190381113},\n\t{7883487353071477846, 104404871487976392},\n\t{9854359191339347308, 130506089359970490},\n\t{10770660513014479971, 81566305849981556},\n\t{13463325641268099964, 101957882312476945},\n\t{2994098996302961243, 127447352890596182},\n\t{15706369927971514489, 79654595556622613},\n\t{5797904354682229399, 99568244445778267},\n\t{2635694424925398845, 124460305557222834},\n\t{6258995034005762182, 77787690973264271},\n\t{3212057774079814824, 97234613716580339},\n\t{17850130272881932242, 121543267145725423},\n\t{18073860448192289507, 75964541966078389},\n\t{8757267504958198172, 94955677457597987},\n\t{6334898362770359811, 118694596821997484},\n\t{13182683513586250689, 74184123013748427},\n\t{11866668373555425458, 92730153767185534},\n\t{5609963430089506015, 115912692208981918},\n\t{17341285199088104971, 72445432630613698},\n\t{12453234462005355406, 90556790788267123},\n\t{10954857059079306353, 113195988485333904},\n\t{13693571323849132942, 141494985606667380},\n\t{17781854114260483896, 88434366004167112},\n\t{3780573569116053255, 110542957505208891},\n\t{114030942967678664, 138178696881511114},\n\t{4682955357782187069, 86361685550944446},\n\t{15077066234082509644, 107952106938680557},\n\t{5011274737320973344, 134940133673350697},\n\t{14661261756894078100, 84337583545844185},\n\t{4491519140835433913, 105421979432305232},\n\t{5614398926044292391, 131777474290381540},\n\t{12732371365632458552, 82360921431488462},\n\t{6692092170185797382, 102951151789360578},\n\t{17588487249587022536, 128688939736700722},\n\t{15604490549419276989, 80430587335437951},\n\t{14893927168346708332, 100538234169297439},\n\t{14005722942005997511, 125672792711621799},\n\t{15671105866394830300, 78545495444763624},\n\t{1142138259283986260, 98181869305954531},\n\t{15262730879387146537, 122727336632443163},\n\t{7233363790403272633, 76704585395276977},\n\t{13653390756431478696, 95880731744096221},\n\t{3231680390257184658, 119850914680120277},\n\t{4325643253124434363, 74906821675075173},\n\t{10018740084832930858, 93633527093843966},\n\t{3300053069186387764, 117041908867304958},\n\t{15897591223523656064, 73151193042065598},\n\t{10648616992549794273, 91438991302581998},\n\t{4087399203832467033, 114298739128227498},\n\t{14332621041645359599, 142873423910284372},\n\t{18181260187883125557, 89295889943927732},\n\t{4279831161144355331, 111619862429909666},\n\t{14573160988285219972, 139524828037387082},\n\t{13719911636105650386, 87203017523366926},\n\t{7926517508277287175, 109003771904208658},\n\t{684774848491833161, 136254714880260823},\n\t{7345513307948477581, 85159196800163014},\n\t{18405263671790372785, 106448996000203767},\n\t{18394893571310578077, 133061245000254709},\n\t{13802651491282805250, 83163278125159193},\n\t{3418256308821342851, 103954097656448992},\n\t{4272820386026678563, 129942622070561240},\n\t{2670512741266674102, 81214138794100775},\n\t{17173198981865506339, 101517673492625968},\n\t{3019754653622331308, 126897091865782461},\n\t{4193189667727651020, 79310682416114038},\n\t{14464859121514339583, 99138353020142547},\n\t{13469387883465536574, 123922941275178184},\n\t{8418367427165960359, 77451838296986365},\n\t{15134645302384838353, 96814797871232956},\n\t{471562554271496325, 121018497339041196},\n\t{9518098633274461011, 75636560836900747},\n\t{7285937273165688360, 94545701046125934},\n\t{18330793628311886258, 118182126307657417},\n\t{4539216990053847055, 73863828942285886},\n\t{14897393274422084627, 92329786177857357},\n\t{4786683537745442072, 115412232722321697},\n\t{14520892257159371055, 72132645451451060},\n\t{18151115321449213818, 90165806814313825},\n\t{8853836096529353561, 112707258517892282},\n\t{1843923083806916143, 140884073147365353},\n\t{12681666973447792349, 88052545717103345},\n\t{2017025661527576725, 110065682146379182},\n\t{11744654113764246714, 137582102682973977},\n\t{422879793461572340, 85988814176858736},\n\t{528599741826965425, 107486017721073420},\n\t{660749677283706782, 134357522151341775},\n\t{7330497575943398595, 83973451344588609},\n\t{13774807988356636147, 104966814180735761},\n\t{3383451930163631472, 131208517725919702},\n\t{15949715511634433382, 82005323578699813},\n\t{6102086334260878016, 102506654473374767},\n\t{3015921899398709616, 128133318091718459},\n\t{18025852251620051174, 80083323807324036},\n\t{4085571240815512351, 100104154759155046},\n\t{14330336087874166247, 125130193448943807},\n\t{15873989082562435760, 78206370905589879},\n\t{15230800334775656796, 97757963631987349},\n\t{5203442363187407284, 122197454539984187},\n\t{946308467778435600, 76373409087490117},\n\t{5794571603150432404, 95466761359362646},\n\t{16466586540792816313, 119333451699203307},\n\t{7985773578781816244, 74583407312002067},\n\t{5370530955049882401, 93229259140002584},\n\t{6713163693812353001, 116536573925003230},\n\t{18030785363914884337, 72835358703127018},\n\t{13315109668038829614, 91044198378908773},\n\t{2808829029766373305, 113805247973635967},\n\t{17346094342490130344, 142256559967044958},\n\t{6229622945628943561, 88910349979403099},\n\t{3175342663608791547, 111137937474253874},\n\t{13192550366365765242, 138922421842817342},\n\t{3633657960551215372, 86826513651760839},\n\t{18377130505971182927, 108533142064701048},\n\t{4524669058754427043, 135666427580876311},\n\t{9745447189362598758, 84791517238047694},\n\t{2958436949848472639, 105989396547559618},\n\t{12921418224165366607, 132486745684449522},\n\t{12687572408530742033, 82804216052780951},\n\t{11247779492236039638, 103505270065976189},\n\t{224666310012885835, 129381587582470237},\n\t{2446259452971747599, 80863492239043898},\n\t{12281196353069460307, 101079365298804872},\n\t{15351495441336825384, 126349206623506090},\n\t{14206370669262903769, 78968254139691306},\n\t{8534591299723853903, 98710317674614133},\n\t{15279925143082205283, 123387897093267666},\n\t{14161639232853766206, 77117435683292291},\n\t{13090363022639819853, 96396794604115364},\n\t{16362953778299774816, 120495993255144205},\n\t{12532689120651053212, 75309995784465128},\n\t{15665861400813816515, 94137494730581410},\n\t{10358954714162494836, 117671868413226763},\n\t{4168503687137865320, 73544917758266727},\n\t{598943590494943747, 91931147197833409},\n\t{5360365506546067587, 114913933997291761},\n\t{11312142901609972388, 143642417496614701},\n\t{9375932322719926695, 89776510935384188},\n\t{11719915403399908368, 112220638669230235},\n\t{10038208235822497557, 140275798336537794},\n\t{10885566165816448877, 87672373960336121},\n\t{18218643725697949000, 109590467450420151},\n\t{18161618638695048346, 136988084313025189},\n\t{13656854658398099168, 85617552695640743},\n\t{12459382304570236056, 107021940869550929},\n\t{1739169825430631358, 133777426086938662},\n\t{14922039196176308311, 83610891304336663},\n\t{14040862976792997485, 104513614130420829},\n\t{3716020665709083144, 130642017663026037},\n\t{4628355925281870917, 81651261039391273},\n\t{10397130925029726550, 102064076299239091},\n\t{8384727637859770284, 127580095374048864},\n\t{5240454773662356427, 79737559608780540},\n\t{6550568467077945534, 99671949510975675},\n\t{3576524565420044014, 124589936888719594},\n\t{6847013871814915412, 77868710555449746},\n\t{17782139376623420074, 97335888194312182},\n\t{13004302183924499284, 121669860242890228},\n\t{17351060901807587860, 76043662651806392},\n\t{3242082053549933210, 95054578314757991},\n\t{17887660622219580224, 118818222893447488},\n\t{11179787888887237640, 74261389308404680},\n\t{13974734861109047050, 92826736635505850},\n\t{8245046539531533005, 116033420794382313},\n\t{16682369133275677888, 72520887996488945},\n\t{7017903361312433648, 90651109995611182},\n\t{17995751238495317868, 113313887494513977},\n\t{8659630992836983623, 141642359368142472},\n\t{5412269370523114764, 88526474605089045},\n\t{11377022731581281359, 110658093256361306},\n\t{4997906377621825891, 138322616570451633},\n\t{14652906532082110942, 86451635356532270},\n\t{9092761128247862869, 108064544195665338},\n\t{2142579373455052779, 135080680244581673},\n\t{12868327154477877747, 84425425152863545},\n\t{2250350887815183471, 105531781441079432},\n\t{2812938609768979339, 131914726801349290},\n\t{6369772649532999991, 82446704250843306},\n\t{17185587848771025797, 103058380313554132},\n\t{3035240737254230630, 128822975391942666},\n\t{6508711479211282048, 80514359619964166},\n\t{17359261385868878368, 100642949524955207},\n\t{17087390713908710056, 125803686906194009},\n\t{3762090168551861929, 78627304316371256},\n\t{4702612710689827411, 98284130395464070},\n\t{15101637925217060072, 122855162994330087},\n\t{16356052730901744401, 76784476871456304},\n\t{1998321839917628885, 95980596089320381},\n\t{7109588318324424010, 119975745111650476},\n\t{13666864735807540814, 74984840694781547},\n\t{12471894901332038114, 93731050868476934},\n\t{6366496589810271835, 117163813585596168},\n\t{3979060368631419896, 73227383490997605},\n\t{9585511479216662775, 91534229363747006},\n\t{2758517312166052660, 114417786704683758},\n\t{12671518677062341634, 143022233380854697},\n\t{1002170145522881665, 89388895863034186},\n\t{10476084718758377889, 111736119828792732},\n\t{13095105898447972362, 139670149785990915},\n\t{5878598177316288774, 87293843616244322},\n\t{16571619758500136775, 109117304520305402},\n\t{11491152661270395161, 136396630650381753},\n\t{264441385652915120, 85247894156488596},\n\t{330551732066143900, 106559867695610745},\n\t{5024875683510067779, 133199834619513431},\n\t{10058076329834874218, 83249896637195894},\n\t{3349223375438816964, 104062370796494868},\n\t{4186529219298521205, 130077963495618585},\n\t{14145795808130045513, 81298727184761615},\n\t{13070558741735168987, 101623408980952019},\n\t{11726512408741573330, 127029261226190024},\n\t{7329070255463483331, 79393288266368765},\n\t{13773023837756742068, 99241610332960956},\n\t{17216279797195927585, 124052012916201195},\n\t{8454331864033760789, 77532508072625747},\n\t{5956228811614813082, 96915635090782184},\n\t{7445286014518516353, 121144543863477730},\n\t{9264989777501460624, 75715339914673581},\n\t{16192923240304213684, 94644174893341976},\n\t{1794409976670715490, 118305218616677471},\n\t{8039035263060279037, 73940761635423419},\n\t{5437108060397960892, 92425952044279274},\n\t{16019757112352226923, 115532440055349092},\n\t{788976158365366019, 72207775034593183},\n\t{14821278253238871236, 90259718793241478},\n\t{9303225779693813237, 112824648491551848},\n\t{11629032224617266546, 141030810614439810},\n\t{11879831158813179495, 88144256634024881},\n\t{1014730893234310657, 110180320792531102},\n\t{10491785653397664129, 137725400990663877},\n\t{8863209042587234033, 86078375619164923},\n\t{6467325284806654637, 107597969523956154},\n\t{17307528642863094104, 134497461904945192},\n\t{10817205401789433815, 84060913690590745},\n\t{18133192770664180173, 105076142113238431},\n\t{18054804944902837312, 131345177641548039},\n\t{18201782118205355176, 82090736025967524},\n\t{4305483574047142354, 102613420032459406},\n\t{14605226504413703751, 128266775040574257},\n\t{2210737537617482988, 80166734400358911},\n\t{16598479977304017447, 100208418000448638},\n\t{11524727934775246001, 125260522500560798},\n\t{2591268940807140847, 78287826562850499},\n\t{17074144231291089770, 97859783203563123},\n\t{16730994270686474309, 122324729004453904},\n\t{10456871419179046443, 76452955627783690},\n\t{3847717237119032246, 95566194534729613},\n\t{9421332564826178211, 119457743168412016},\n\t{5888332853016361382, 74661089480257510},\n\t{16583788103125227536, 93326361850321887},\n\t{16118049110479146516, 116657952312902359},\n\t{16991309721690548428, 72911220195563974},\n\t{12015765115258409727, 91139025244454968},\n\t{15019706394073012159, 113923781555568710},\n\t{9551260955736489391, 142404726944460888},\n\t{5969538097335305869, 89002954340288055},\n\t{2850236603241744433, 111253692925360069},\n}\n\nconst pow5InvNumBits64 = 122\n\nvar pow5InvSplit64 = [...]uint128{\n\t{1, 288230376151711744},\n\t{3689348814741910324, 230584300921369395},\n\t{2951479051793528259, 184467440737095516},\n\t{17118578500402463900, 147573952589676412},\n\t{12632330341676300947, 236118324143482260},\n\t{10105864273341040758, 188894659314785808},\n\t{15463389048156653253, 151115727451828646},\n\t{17362724847566824558, 241785163922925834},\n\t{17579528692795369969, 193428131138340667},\n\t{6684925324752475329, 154742504910672534},\n\t{18074578149087781173, 247588007857076054},\n\t{18149011334012135262, 198070406285660843},\n\t{3451162622983977240, 158456325028528675},\n\t{5521860196774363583, 253530120045645880},\n\t{4417488157419490867, 202824096036516704},\n\t{7223339340677503017, 162259276829213363},\n\t{7867994130342094503, 259614842926741381},\n\t{2605046489531765280, 207691874341393105},\n\t{2084037191625412224, 166153499473114484},\n\t{10713157136084480204, 265845599156983174},\n\t{12259874523609494487, 212676479325586539},\n\t{13497248433629505913, 170141183460469231},\n\t{14216899864323388813, 272225893536750770},\n\t{11373519891458711051, 217780714829400616},\n\t{5409467098425058518, 174224571863520493},\n\t{4965798542738183305, 278759314981632789},\n\t{7661987648932456967, 223007451985306231},\n\t{2440241304404055250, 178405961588244985},\n\t{3904386087046488400, 285449538541191976},\n\t{17880904128604832013, 228359630832953580},\n\t{14304723302883865611, 182687704666362864},\n\t{15133127457049002812, 146150163733090291},\n\t{16834306301794583852, 233840261972944466},\n\t{9778096226693756759, 187072209578355573},\n\t{15201174610838826053, 149657767662684458},\n\t{2185786488890659746, 239452428260295134},\n\t{5437978005854438120, 191561942608236107},\n\t{15418428848909281466, 153249554086588885},\n\t{6222742084545298729, 245199286538542217},\n\t{16046240111861969953, 196159429230833773},\n\t{1768945645263844993, 156927543384667019},\n\t{10209010661905972635, 251084069415467230},\n\t{8167208529524778108, 200867255532373784},\n\t{10223115638361732810, 160693804425899027},\n\t{1599589762411131202, 257110087081438444},\n\t{4969020624670815285, 205688069665150755},\n\t{3975216499736652228, 164550455732120604},\n\t{13739044029062464211, 263280729171392966},\n\t{7301886408508061046, 210624583337114373},\n\t{13220206756290269483, 168499666669691498},\n\t{17462981995322520850, 269599466671506397},\n\t{6591687966774196033, 215679573337205118},\n\t{12652048002903177473, 172543658669764094},\n\t{9175230360419352987, 276069853871622551},\n\t{3650835473593572067, 220855883097298041},\n\t{17678063637842498946, 176684706477838432},\n\t{13527506561580357021, 282695530364541492},\n\t{3443307619780464970, 226156424291633194},\n\t{6443994910566282300, 180925139433306555},\n\t{5155195928453025840, 144740111546645244},\n\t{15627011115008661990, 231584178474632390},\n\t{12501608892006929592, 185267342779705912},\n\t{2622589484121723027, 148213874223764730},\n\t{4196143174594756843, 237142198758023568},\n\t{10735612169159626121, 189713759006418854},\n\t{12277838550069611220, 151771007205135083},\n\t{15955192865369467629, 242833611528216133},\n\t{1696107848069843133, 194266889222572907},\n\t{12424932722681605476, 155413511378058325},\n\t{1433148282581017146, 248661618204893321},\n\t{15903913885032455010, 198929294563914656},\n\t{9033782293284053685, 159143435651131725},\n\t{14454051669254485895, 254629497041810760},\n\t{11563241335403588716, 203703597633448608},\n\t{16629290697806691620, 162962878106758886},\n\t{781423413297334329, 260740604970814219},\n\t{4314487545379777786, 208592483976651375},\n\t{3451590036303822229, 166873987181321100},\n\t{5522544058086115566, 266998379490113760},\n\t{4418035246468892453, 213598703592091008},\n\t{10913125826658934609, 170878962873672806},\n\t{10082303693170474728, 273406340597876490},\n\t{8065842954536379782, 218725072478301192},\n\t{17520720807854834795, 174980057982640953},\n\t{5897060404116273733, 279968092772225526},\n\t{1028299508551108663, 223974474217780421},\n\t{15580034865808528224, 179179579374224336},\n\t{17549358155809824511, 286687326998758938},\n\t{2971440080422128639, 229349861599007151},\n\t{17134547323305344204, 183479889279205720},\n\t{13707637858644275364, 146783911423364576},\n\t{14553522944347019935, 234854258277383322},\n\t{4264120725993795302, 187883406621906658},\n\t{10789994210278856888, 150306725297525326},\n\t{9885293106962350374, 240490760476040522},\n\t{529536856086059653, 192392608380832418},\n\t{7802327114352668369, 153914086704665934},\n\t{1415676938738538420, 246262538727465495},\n\t{1132541550990830736, 197010030981972396},\n\t{15663428499760305882, 157608024785577916},\n\t{17682787970132668764, 252172839656924666},\n\t{10456881561364224688, 201738271725539733},\n\t{15744202878575200397, 161390617380431786},\n\t{17812026976236499989, 258224987808690858},\n\t{3181575136763469022, 206579990246952687},\n\t{13613306553636506187, 165263992197562149},\n\t{10713244041592678929, 264422387516099439},\n\t{12259944048016053467, 211537910012879551},\n\t{6118606423670932450, 169230328010303641},\n\t{2411072648389671274, 270768524816485826},\n\t{16686253377679378312, 216614819853188660},\n\t{13349002702143502650, 173291855882550928},\n\t{17669055508687693916, 277266969412081485},\n\t{14135244406950155133, 221813575529665188},\n\t{240149081334393137, 177450860423732151},\n\t{11452284974360759988, 283921376677971441},\n\t{5472479164746697667, 227137101342377153},\n\t{11756680961281178780, 181709681073901722},\n\t{2026647139541122378, 145367744859121378},\n\t{18000030682233437097, 232588391774594204},\n\t{18089373360528660001, 186070713419675363},\n\t{3403452244197197031, 148856570735740291},\n\t{16513570034941246220, 238170513177184465},\n\t{13210856027952996976, 190536410541747572},\n\t{3189987192878576934, 152429128433398058},\n\t{1414630693863812771, 243886605493436893},\n\t{8510402184574870864, 195109284394749514},\n\t{10497670562401807014, 156087427515799611},\n\t{9417575270359070576, 249739884025279378},\n\t{14912757845771077107, 199791907220223502},\n\t{4551508647133041040, 159833525776178802},\n\t{10971762650154775986, 255733641241886083},\n\t{16156107749607641435, 204586912993508866},\n\t{9235537384944202825, 163669530394807093},\n\t{11087511001168814197, 261871248631691349},\n\t{12559357615676961681, 209496998905353079},\n\t{13736834907283479668, 167597599124282463},\n\t{18289587036911657145, 268156158598851941},\n\t{10942320814787415393, 214524926879081553},\n\t{16132554281313752961, 171619941503265242},\n\t{11054691591134363444, 274591906405224388},\n\t{16222450902391311402, 219673525124179510},\n\t{12977960721913049122, 175738820099343608},\n\t{17075388340318968271, 281182112158949773},\n\t{2592264228029443648, 224945689727159819},\n\t{5763160197165465241, 179956551781727855},\n\t{9221056315464744386, 287930482850764568},\n\t{14755542681855616155, 230344386280611654},\n\t{15493782960226403247, 184275509024489323},\n\t{1326979923955391628, 147420407219591459},\n\t{9501865507812447252, 235872651551346334},\n\t{11290841220991868125, 188698121241077067},\n\t{1653975347309673853, 150958496992861654},\n\t{10025058185179298811, 241533595188578646},\n\t{4330697733401528726, 193226876150862917},\n\t{14532604630946953951, 154581500920690333},\n\t{1116074521063664381, 247330401473104534},\n\t{4582208431592841828, 197864321178483627},\n\t{14733813189500004432, 158291456942786901},\n\t{16195403473716186445, 253266331108459042},\n\t{5577625149489128510, 202613064886767234},\n\t{8151448934333213131, 162090451909413787},\n\t{16731667109675051333, 259344723055062059},\n\t{17074682502481951390, 207475778444049647},\n\t{6281048372501740465, 165980622755239718},\n\t{6360328581260874421, 265568996408383549},\n\t{8777611679750609860, 212455197126706839},\n\t{10711438158542398211, 169964157701365471},\n\t{9759603424184016492, 271942652322184754},\n\t{11497031554089123517, 217554121857747803},\n\t{16576322872755119460, 174043297486198242},\n\t{11764721337440549842, 278469275977917188},\n\t{16790474699436260520, 222775420782333750},\n\t{13432379759549008416, 178220336625867000},\n\t{3045063541568861850, 285152538601387201},\n\t{17193446092222730773, 228122030881109760},\n\t{13754756873778184618, 182497624704887808},\n\t{18382503128506368341, 145998099763910246},\n\t{3586563302416817083, 233596959622256395},\n\t{2869250641933453667, 186877567697805116},\n\t{17052795772514404226, 149502054158244092},\n\t{12527077977055405469, 239203286653190548},\n\t{17400360011128145022, 191362629322552438},\n\t{2852241564676785048, 153090103458041951},\n\t{15631632947708587046, 244944165532867121},\n\t{8815957543424959314, 195955332426293697},\n\t{18120812478965698421, 156764265941034957},\n\t{14235904707377476180, 250822825505655932},\n\t{4010026136418160298, 200658260404524746},\n\t{17965416168102169531, 160526608323619796},\n\t{2919224165770098987, 256842573317791675},\n\t{2335379332616079190, 205474058654233340},\n\t{1868303466092863352, 164379246923386672},\n\t{6678634360490491686, 263006795077418675},\n\t{5342907488392393349, 210405436061934940},\n\t{4274325990713914679, 168324348849547952},\n\t{10528270399884173809, 269318958159276723},\n\t{15801313949391159694, 215455166527421378},\n\t{1573004715287196786, 172364133221937103},\n\t{17274202803427156150, 275782613155099364},\n\t{17508711057483635243, 220626090524079491},\n\t{10317620031244997871, 176500872419263593},\n\t{12818843235250086271, 282401395870821749},\n\t{13944423402941979340, 225921116696657399},\n\t{14844887537095493795, 180736893357325919},\n\t{15565258844418305359, 144589514685860735},\n\t{6457670077359736959, 231343223497377177},\n\t{16234182506113520537, 185074578797901741},\n\t{9297997190148906106, 148059663038321393},\n\t{11187446689496339446, 236895460861314229},\n\t{12639306166338981880, 189516368689051383},\n\t{17490142562555006151, 151613094951241106},\n\t{2158786396894637579, 242580951921985771},\n\t{16484424376483351356, 194064761537588616},\n\t{9498190686444770762, 155251809230070893},\n\t{11507756283569722895, 248402894768113429},\n\t{12895553841597688639, 198722315814490743},\n\t{17695140702761971558, 158977852651592594},\n\t{17244178680193423523, 254364564242548151},\n\t{10105994129412828495, 203491651394038521},\n\t{4395446488788352473, 162793321115230817},\n\t{10722063196803274280, 260469313784369307},\n\t{1198952927958798777, 208375451027495446},\n\t{15716557601334680315, 166700360821996356},\n\t{17767794532651667857, 266720577315194170},\n\t{14214235626121334286, 213376461852155336},\n\t{7682039686155157106, 170701169481724269},\n\t{1223217053622520399, 273121871170758831},\n\t{15735968901865657612, 218497496936607064},\n\t{16278123936234436413, 174797997549285651},\n\t{219556594781725998, 279676796078857043},\n\t{7554342905309201445, 223741436863085634},\n\t{9732823138989271479, 178993149490468507},\n\t{815121763415193074, 286389039184749612},\n\t{11720143854957885429, 229111231347799689},\n\t{13065463898708218666, 183288985078239751},\n\t{6763022304224664610, 146631188062591801},\n\t{3442138057275642729, 234609900900146882},\n\t{13821756890046245153, 187687920720117505},\n\t{11057405512036996122, 150150336576094004},\n\t{6623802375033462826, 240240538521750407},\n\t{16367088344252501231, 192192430817400325},\n\t{13093670675402000985, 153753944653920260},\n\t{2503129006933649959, 246006311446272417},\n\t{13070549649772650937, 196805049157017933},\n\t{17835137349301941396, 157444039325614346},\n\t{2710778055689733971, 251910462920982955},\n\t{2168622444551787177, 201528370336786364},\n\t{5424246770383340065, 161222696269429091},\n\t{1300097203129523457, 257956314031086546},\n\t{15797473021471260058, 206365051224869236},\n\t{8948629602435097724, 165092040979895389},\n\t{3249760919670425388, 264147265567832623},\n\t{9978506365220160957, 211317812454266098},\n\t{15361502721659949412, 169054249963412878},\n\t{2442311466204457120, 270486799941460606},\n\t{16711244431931206989, 216389439953168484},\n\t{17058344360286875914, 173111551962534787},\n\t{12535955717491360170, 276978483140055660},\n\t{10028764573993088136, 221582786512044528},\n\t{15401709288678291155, 177266229209635622},\n\t{9885339602917624555, 283625966735416996},\n\t{4218922867592189321, 226900773388333597},\n\t{14443184738299482427, 181520618710666877},\n\t{4175850161155765295, 145216494968533502},\n\t{10370709072591134795, 232346391949653603},\n\t{15675264887556728482, 185877113559722882},\n\t{5161514280561562140, 148701690847778306},\n\t{879725219414678777, 237922705356445290},\n\t{703780175531743021, 190338164285156232},\n\t{11631070584651125387, 152270531428124985},\n\t{162968861732249003, 243632850284999977},\n\t{11198421533611530172, 194906280227999981},\n\t{5269388412147313814, 155925024182399985},\n\t{8431021459435702103, 249480038691839976},\n\t{3055468352806651359, 199584030953471981},\n\t{17201769941212962380, 159667224762777584},\n\t{16454785461715008838, 255467559620444135},\n\t{13163828369372007071, 204374047696355308},\n\t{17909760324981426303, 163499238157084246},\n\t{2830174816776909822, 261598781051334795},\n\t{2264139853421527858, 209279024841067836},\n\t{16568707141704863579, 167423219872854268},\n\t{4373838538276319787, 267877151796566830},\n\t{3499070830621055830, 214301721437253464},\n\t{6488605479238754987, 171441377149802771},\n\t{3003071137298187333, 274306203439684434},\n\t{6091805724580460189, 219444962751747547},\n\t{15941491023890099121, 175555970201398037},\n\t{10748990379256517301, 280889552322236860},\n\t{8599192303405213841, 224711641857789488},\n\t{14258051472207991719, 179769313486231590},\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "json", + "path": "gno.land/p/demo/json", + "files": [ + { + "name": "LICENSE", + "body": "# MIT License\n\nCopyright (c) 2019 Pyzhov Stepan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, + { + "name": "README.md", + "body": "# JSON Parser\n\nThe JSON parser is a package that provides functionality for parsing and processing JSON strings. This package accepts JSON strings as byte slices.\n\nCurrently, gno does not [support the `reflect` package](https://docs.gno.land/concepts/effective-gno#reflection-is-never-clear), so it cannot retrieve type information at runtime. Therefore, it is designed to infer and handle type information when parsing JSON strings using a state machine approach.\n\nAfter passing through the state machine, JSON strings are represented as the `Node` type. The `Node` type represents nodes for JSON data, including various types such as `ObjectNode`, `ArrayNode`, `StringNode`, `NumberNode`, `BoolNode`, and `NullNode`.\n\nThis package provides methods for manipulating, searching, and extracting the Node type.\n\n## State Machine\n\nTo parse JSON strings, a [finite state machine](https://en.wikipedia.org/wiki/Finite-state_machine) approach is used. The state machine transitions to the next state based on the current state and the input character while parsing the JSON string. Through this method, type information can be inferred and processed without reflect, and the amount of parser code can be significantly reduced.\n\nThe image below shows the state transitions of the state machine according to the states and input characters.\n\n```mermaid\nstateDiagram-v2\n [*] --\u003e __: Start\n __ --\u003e ST: String\n __ --\u003e MI: Number\n __ --\u003e ZE: Zero\n __ --\u003e IN: Integer\n __ --\u003e T1: Boolean (true)\n __ --\u003e F1: Boolean (false)\n __ --\u003e N1: Null\n __ --\u003e ec: Empty Object End\n __ --\u003e cc: Object End\n __ --\u003e bc: Array End\n __ --\u003e co: Object Begin\n __ --\u003e bo: Array Begin\n __ --\u003e cm: Comma\n __ --\u003e cl: Colon\n __ --\u003e OK: Success/End\n ST --\u003e OK: String Complete\n MI --\u003e OK: Number Complete\n ZE --\u003e OK: Zero Complete\n IN --\u003e OK: Integer Complete\n T1 --\u003e OK: True Complete\n F1 --\u003e OK: False Complete\n N1 --\u003e OK: Null Complete\n ec --\u003e OK: Empty Object Complete\n cc --\u003e OK: Object Complete\n bc --\u003e OK: Array Complete\n co --\u003e OB: Inside Object\n bo --\u003e AR: Inside Array\n cm --\u003e KE: Expecting New Key\n cm --\u003e VA: Expecting New Value\n cl --\u003e VA: Expecting Value\n OB --\u003e ST: String in Object (Key)\n OB --\u003e ec: Empty Object\n OB --\u003e cc: End Object\n AR --\u003e ST: String in Array\n AR --\u003e bc: End Array\n KE --\u003e ST: String as Key\n VA --\u003e ST: String as Value\n VA --\u003e MI: Number as Value\n VA --\u003e T1: True as Value\n VA --\u003e F1: False as Value\n VA --\u003e N1: Null as Value\n OK --\u003e [*]: End\n```\n\n## Examples\n\nThis package provides parsing functionality along with encoding and decoding functionality. The following examples demonstrate how to use this package.\n\n### Decoding\n\nDecoding (or Unmarshaling) is the functionality that converts an input byte slice JSON string into a `Node` type.\n\nThe converted `Node` type allows you to modify the JSON data or search and extract data that meets specific conditions.\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"gno.land/p/demo/json\"\n \"gno.land/p/demo/ufmt\"\n)\n\nfunc main() {\n node, err := json.Unmarshal([]byte(`{\"foo\": \"var\"}`))\n if err != nil {\n ufmt.Errorf(\"error: %v\", err)\n }\n\n ufmt.Sprintf(\"node: %v\", node)\n}\n```\n\n### Encoding\n\nEncoding (or Marshaling) is the functionality that converts JSON data represented as a Node type into a byte slice JSON string.\n\n\u003e ⚠️ Caution: Converting a large `Node` type into a JSON string may _impact performance_. or might be cause _unexpected behavior_.\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"gno.land/p/demo/json\"\n \"gno.land/p/demo/ufmt\"\n)\n\nfunc main() {\n node := ObjectNode(\"\", map[string]*Node{\n \"foo\": StringNode(\"foo\", \"bar\"),\n \"baz\": NumberNode(\"baz\", 100500),\n \"qux\": NullNode(\"qux\"),\n })\n\n b, err := json.Marshal(node)\n if err != nil {\n ufmt.Errorf(\"error: %v\", err)\n }\n\n ufmt.Sprintf(\"json: %s\", string(b))\n}\n```\n\n### Searching\n\nOnce the JSON data converted into a `Node` type, you can **search** and **extract** data that satisfy specific conditions. For example, you can find data with a specific type or data with a specific key.\n\nTo use this functionality, you can use methods in the `GetXXX` prefixed methods. The `MustXXX` methods also provide the same functionality as the former methods, but they will **panic** if data doesn't satisfies the condition.\n\nHere is an example of finding data with a specific key. For more examples, please refer to the [node.gno](node.gno) file.\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"gno.land/p/demo/json\"\n \"gno.land/p/demo/ufmt\"\n)\n\nfunc main() {\n root, err := Unmarshal([]byte(`{\"foo\": true, \"bar\": null}`))\n if err != nil {\n ufmt.Errorf(\"error: %v\", err)\n }\n\n value, err := root.GetKey(\"foo\")\n if err != nil {\n ufmt.Errorf(\"error occurred while getting key, %s\", err)\n }\n\n if value.MustBool() != true {\n ufmt.Errorf(\"value is not true\")\n }\n\n value, err = root.GetKey(\"bar\")\n if err != nil {\n t.Errorf(\"error occurred while getting key, %s\", err)\n }\n\n _, err = root.GetKey(\"baz\")\n if err == nil {\n t.Errorf(\"key baz is not exist. must be failed\")\n }\n}\n```\n\n## Contributing\n\nPlease submit any issues or pull requests for this package through the GitHub repository at [gnolang/gno](\u003chttps://github.com/gnolang/gno\u003e).\n" + }, + { + "name": "buffer.gno", + "body": "package json\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype buffer struct {\n\tdata []byte\n\tlength int\n\tindex int\n\n\tlast States\n\tstate States\n\tclass Classes\n}\n\n// newBuffer creates a new buffer with the given data\nfunc newBuffer(data []byte) *buffer {\n\treturn \u0026buffer{\n\t\tdata: data,\n\t\tlength: len(data),\n\t\tlast: GO,\n\t\tstate: GO,\n\t}\n}\n\n// first retrieves the first non-whitespace (or other escaped) character in the buffer.\nfunc (b *buffer) first() (byte, error) {\n\tfor ; b.index \u003c b.length; b.index++ {\n\t\tc := b.data[b.index]\n\n\t\tif !(c == whiteSpace || c == carriageReturn || c == newLine || c == tab) {\n\t\t\treturn c, nil\n\t\t}\n\t}\n\n\treturn 0, io.EOF\n}\n\n// current returns the byte of the current index.\nfunc (b *buffer) current() (byte, error) {\n\tif b.index \u003e= b.length {\n\t\treturn 0, io.EOF\n\t}\n\n\treturn b.data[b.index], nil\n}\n\n// next moves to the next byte and returns it.\nfunc (b *buffer) next() (byte, error) {\n\tb.index++\n\treturn b.current()\n}\n\n// step just moves to the next position.\nfunc (b *buffer) step() error {\n\t_, err := b.next()\n\treturn err\n}\n\n// move moves the index by the given position.\nfunc (b *buffer) move(pos int) error {\n\tnewIndex := b.index + pos\n\n\tif newIndex \u003e b.length {\n\t\treturn io.EOF\n\t}\n\n\tb.index = newIndex\n\n\treturn nil\n}\n\n// slice returns the slice from the current index to the given position.\nfunc (b *buffer) slice(pos int) ([]byte, error) {\n\tend := b.index + pos\n\n\tif end \u003e b.length {\n\t\treturn nil, io.EOF\n\t}\n\n\treturn b.data[b.index:end], nil\n}\n\n// sliceFromIndices returns a slice of the buffer's data starting from 'start' up to (but not including) 'stop'.\nfunc (b *buffer) sliceFromIndices(start, stop int) []byte {\n\tif start \u003e b.length {\n\t\tstart = b.length\n\t}\n\n\tif stop \u003e b.length {\n\t\tstop = b.length\n\t}\n\n\treturn b.data[start:stop]\n}\n\n// skip moves the index to skip the given byte.\nfunc (b *buffer) skip(bs byte) error {\n\tfor b.index \u003c b.length {\n\t\tif b.data[b.index] == bs \u0026\u0026 !b.backslash() {\n\t\t\treturn nil\n\t\t}\n\n\t\tb.index++\n\t}\n\n\treturn io.EOF\n}\n\n// skipAny moves the index until it encounters one of the given set of bytes.\nfunc (b *buffer) skipAny(endTokens map[byte]bool) error {\n\tfor b.index \u003c b.length {\n\t\tif _, exists := endTokens[b.data[b.index]]; exists {\n\t\t\treturn nil\n\t\t}\n\n\t\tb.index++\n\t}\n\n\t// build error message\n\tvar tokens []string\n\tfor token := range endTokens {\n\t\ttokens = append(tokens, string(token))\n\t}\n\n\treturn ufmt.Errorf(\n\t\t\"EOF reached before encountering one of the expected tokens: %s\",\n\t\tstrings.Join(tokens, \", \"),\n\t)\n}\n\n// skipAndReturnIndex moves the buffer index forward by one and returns the new index.\nfunc (b *buffer) skipAndReturnIndex() (int, error) {\n\terr := b.step()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn b.index, nil\n}\n\n// skipUntil moves the buffer index forward until it encounters a byte contained in the endTokens set.\nfunc (b *buffer) skipUntil(endTokens map[byte]bool) (int, error) {\n\tfor b.index \u003c b.length {\n\t\tcurrentByte, err := b.current()\n\t\tif err != nil {\n\t\t\treturn b.index, err\n\t\t}\n\n\t\t// Check if the current byte is in the set of end tokens.\n\t\tif _, exists := endTokens[currentByte]; exists {\n\t\t\treturn b.index, nil\n\t\t}\n\n\t\tb.index++\n\t}\n\n\treturn b.index, io.EOF\n}\n\n// significantTokens is a map where the keys are the significant characters in a JSON path.\n// The values in the map are all true, which allows us to use the map as a set for quick lookups.\nvar significantTokens = map[byte]bool{\n\tdot: true, // access properties of an object\n\tdollarSign: true, // root object\n\tatSign: true, // current object\n\tbracketOpen: true, // start of an array index or filter expression\n\tbracketClose: true, // end of an array index or filter expression\n}\n\n// filterTokens stores the filter expression tokens.\nvar filterTokens = map[byte]bool{\n\taesterisk: true, // wildcard\n\tandSign: true,\n\torSign: true,\n}\n\n// skipToNextSignificantToken advances the buffer index to the next significant character.\n// Significant characters are defined based on the JSON path syntax.\nfunc (b *buffer) skipToNextSignificantToken() {\n\tfor b.index \u003c b.length {\n\t\tcurrent := b.data[b.index]\n\n\t\tif _, ok := significantTokens[current]; ok {\n\t\t\tbreak\n\t\t}\n\n\t\tb.index++\n\t}\n}\n\n// backslash checks to see if the number of backslashes before the current index is odd.\n//\n// This is used to check if the current character is escaped. However, unlike the \"unescape\" function,\n// \"backslash\" only serves to check the number of backslashes.\nfunc (b *buffer) backslash() bool {\n\tif b.index == 0 {\n\t\treturn false\n\t}\n\n\tcount := 0\n\tfor i := b.index - 1; ; i-- {\n\t\tif i \u003e= b.length || b.data[i] != backSlash {\n\t\t\tbreak\n\t\t}\n\n\t\tcount++\n\n\t\tif i == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn count%2 != 0\n}\n\n// numIndex holds a map of valid numeric characters\nvar numIndex = map[byte]bool{\n\t'0': true,\n\t'1': true,\n\t'2': true,\n\t'3': true,\n\t'4': true,\n\t'5': true,\n\t'6': true,\n\t'7': true,\n\t'8': true,\n\t'9': true,\n\t'.': true,\n\t'e': true,\n\t'E': true,\n}\n\n// pathToken checks if the current token is a valid JSON path token.\nfunc (b *buffer) pathToken() error {\n\tvar stack []byte\n\n\tinToken := false\n\tinNumber := false\n\tfirst := b.index\n\n\tfor b.index \u003c b.length {\n\t\tc := b.data[b.index]\n\n\t\tswitch {\n\t\tcase c == doubleQuote || c == singleQuote:\n\t\t\tinToken = true\n\t\t\tif err := b.step(); err != nil {\n\t\t\t\treturn errors.New(\"error stepping through buffer\")\n\t\t\t}\n\n\t\t\tif err := b.skip(c); err != nil {\n\t\t\t\treturn errors.New(\"unmatched quote in path\")\n\t\t\t}\n\n\t\t\tif b.index \u003e= b.length {\n\t\t\t\treturn errors.New(\"unmatched quote in path\")\n\t\t\t}\n\n\t\tcase c == bracketOpen || c == parenOpen:\n\t\t\tinToken = true\n\t\t\tstack = append(stack, c)\n\n\t\tcase c == bracketClose || c == parenClose:\n\t\t\tinToken = true\n\t\t\tif len(stack) == 0 || (c == bracketClose \u0026\u0026 stack[len(stack)-1] != bracketOpen) || (c == parenClose \u0026\u0026 stack[len(stack)-1] != parenOpen) {\n\t\t\t\treturn errors.New(\"mismatched bracket or parenthesis\")\n\t\t\t}\n\n\t\t\tstack = stack[:len(stack)-1]\n\n\t\tcase pathStateContainsValidPathToken(c):\n\t\t\tinToken = true\n\n\t\tcase c == plus || c == minus:\n\t\t\tif inNumber || (b.index \u003e 0 \u0026\u0026 numIndex[b.data[b.index-1]]) {\n\t\t\t\tinToken = true\n\t\t\t} else if !inToken \u0026\u0026 (b.index+1 \u003c b.length \u0026\u0026 numIndex[b.data[b.index+1]]) {\n\t\t\t\tinToken = true\n\t\t\t\tinNumber = true\n\t\t\t} else if !inToken {\n\t\t\t\treturn errors.New(\"unexpected operator at start of token\")\n\t\t\t}\n\n\t\tdefault:\n\t\t\tif len(stack) != 0 || inToken {\n\t\t\t\tinToken = true\n\t\t\t} else {\n\t\t\t\tgoto end\n\t\t\t}\n\t\t}\n\n\t\tb.index++\n\t}\n\nend:\n\tif len(stack) != 0 {\n\t\treturn errors.New(\"unclosed bracket or parenthesis at end of path\")\n\t}\n\n\tif first == b.index {\n\t\treturn errors.New(\"no token found\")\n\t}\n\n\tif inNumber \u0026\u0026 !numIndex[b.data[b.index-1]] {\n\t\tinNumber = false\n\t}\n\n\treturn nil\n}\n\nfunc pathStateContainsValidPathToken(c byte) bool {\n\tif _, ok := significantTokens[c]; ok {\n\t\treturn true\n\t}\n\n\tif _, ok := filterTokens[c]; ok {\n\t\treturn true\n\t}\n\n\tif _, ok := numIndex[c]; ok {\n\t\treturn true\n\t}\n\n\tif 'A' \u003c= c \u0026\u0026 c \u003c= 'Z' || 'a' \u003c= c \u0026\u0026 c \u003c= 'z' {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nfunc (b *buffer) numeric(token bool) error {\n\tif token {\n\t\tb.last = GO\n\t}\n\n\tfor ; b.index \u003c b.length; b.index++ {\n\t\tb.class = b.getClasses(doubleQuote)\n\t\tif b.class == __ {\n\t\t\treturn errors.New(\"invalid token found while parsing path\")\n\t\t}\n\n\t\tb.state = StateTransitionTable[b.last][b.class]\n\t\tif b.state == __ {\n\t\t\tif token {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\treturn errors.New(\"invalid token found while parsing path\")\n\t\t}\n\n\t\tif b.state \u003c __ {\n\t\t\treturn nil\n\t\t}\n\n\t\tif b.state \u003c MI || b.state \u003e E3 {\n\t\t\treturn nil\n\t\t}\n\n\t\tb.last = b.state\n\t}\n\n\tif b.last != ZE \u0026\u0026 b.last != IN \u0026\u0026 b.last != FR \u0026\u0026 b.last != E3 {\n\t\treturn errors.New(\"invalid token found while parsing path\")\n\t}\n\n\treturn nil\n}\n\nfunc (b *buffer) getClasses(c byte) Classes {\n\tif b.data[b.index] \u003e= 128 {\n\t\treturn C_ETC\n\t}\n\n\tif c == singleQuote {\n\t\treturn QuoteAsciiClasses[b.data[b.index]]\n\t}\n\n\treturn AsciiClasses[b.data[b.index]]\n}\n\nfunc (b *buffer) getState() States {\n\tb.last = b.state\n\n\tb.class = b.getClasses(doubleQuote)\n\tif b.class == __ {\n\t\treturn __\n\t}\n\n\tb.state = StateTransitionTable[b.last][b.class]\n\n\treturn b.state\n}\n\n// string parses a string token from the buffer.\nfunc (b *buffer) string(search byte, token bool) error {\n\tif token {\n\t\tb.last = GO\n\t}\n\n\tfor ; b.index \u003c b.length; b.index++ {\n\t\tb.class = b.getClasses(search)\n\n\t\tif b.class == __ {\n\t\t\treturn errors.New(\"invalid token found while parsing path\")\n\t\t}\n\n\t\tb.state = StateTransitionTable[b.last][b.class]\n\t\tif b.state == __ {\n\t\t\treturn errors.New(\"invalid token found while parsing path\")\n\t\t}\n\n\t\tif b.state \u003c __ {\n\t\t\tbreak\n\t\t}\n\n\t\tb.last = b.state\n\t}\n\n\treturn nil\n}\n\nfunc (b *buffer) word(bs []byte) error {\n\tvar c byte\n\n\tmax := len(bs)\n\tindex := 0\n\n\tfor ; b.index \u003c b.length; b.index++ {\n\t\tc = b.data[b.index]\n\n\t\tif c != bs[index] {\n\t\t\treturn errors.New(\"invalid token found while parsing path\")\n\t\t}\n\n\t\tindex++\n\t\tif index \u003e= max {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif index != max {\n\t\treturn errors.New(\"invalid token found while parsing path\")\n\t}\n\n\treturn nil\n}\n\nfunc numberKind2f64(value interface{}) (result float64, err error) {\n\tswitch typed := value.(type) {\n\tcase float64:\n\t\tresult = typed\n\tcase float32:\n\t\tresult = float64(typed)\n\tcase int:\n\t\tresult = float64(typed)\n\tcase int8:\n\t\tresult = float64(typed)\n\tcase int16:\n\t\tresult = float64(typed)\n\tcase int32:\n\t\tresult = float64(typed)\n\tcase int64:\n\t\tresult = float64(typed)\n\tcase uint:\n\t\tresult = float64(typed)\n\tcase uint8:\n\t\tresult = float64(typed)\n\tcase uint16:\n\t\tresult = float64(typed)\n\tcase uint32:\n\t\tresult = float64(typed)\n\tcase uint64:\n\t\tresult = float64(typed)\n\tdefault:\n\t\terr = ufmt.Errorf(\"invalid number type: %T\", value)\n\t}\n\n\treturn\n}\n" + }, + { + "name": "buffer_test.gno", + "body": "package json\n\nimport \"testing\"\n\nfunc TestBufferCurrent(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tbuffer *buffer\n\t\texpected byte\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"Valid current byte\",\n\t\t\tbuffer: \u0026buffer{\n\t\t\t\tdata: []byte(\"test\"),\n\t\t\t\tlength: 4,\n\t\t\t\tindex: 1,\n\t\t\t},\n\t\t\texpected: 'e',\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"EOF\",\n\t\t\tbuffer: \u0026buffer{\n\t\t\t\tdata: []byte(\"test\"),\n\t\t\t\tlength: 4,\n\t\t\t\tindex: 4,\n\t\t\t},\n\t\t\texpected: 0,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := tt.buffer.current()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"buffer.current() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"buffer.current() = %v, want %v\", got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBufferStep(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tbuffer *buffer\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"Valid step\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"EOF error\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 3},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := tt.buffer.step()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"buffer.step() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBufferNext(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tbuffer *buffer\n\t\twant byte\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"Valid next byte\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\twant: 'e',\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"EOF error\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 3},\n\t\t\twant: 0,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := tt.buffer.next()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"buffer.next() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"buffer.next() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBufferSlice(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tbuffer *buffer\n\t\tpos int\n\t\twant []byte\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"Valid slice -- 0 characters\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\tpos: 0,\n\t\t\twant: nil,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Valid slice -- 1 character\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\tpos: 1,\n\t\t\twant: []byte(\"t\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Valid slice -- 2 characters\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 1},\n\t\t\tpos: 2,\n\t\t\twant: []byte(\"es\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Valid slice -- 3 characters\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\tpos: 3,\n\t\t\twant: []byte(\"tes\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Valid slice -- 4 characters\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\tpos: 4,\n\t\t\twant: []byte(\"test\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"EOF error\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 3},\n\t\t\tpos: 2,\n\t\t\twant: nil,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := tt.buffer.slice(tt.pos)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"buffer.slice() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif string(got) != string(tt.want) {\n\t\t\t\tt.Errorf(\"buffer.slice() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBufferMove(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tbuffer *buffer\n\t\tpos int\n\t\twantErr bool\n\t\twantIdx int\n\t}{\n\t\t{\n\t\t\tname: \"Valid move\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 1},\n\t\t\tpos: 2,\n\t\t\twantErr: false,\n\t\t\twantIdx: 3,\n\t\t},\n\t\t{\n\t\t\tname: \"Move beyond length\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 1},\n\t\t\tpos: 4,\n\t\t\twantErr: true,\n\t\t\twantIdx: 1,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := tt.buffer.move(tt.pos)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"buffer.move() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t\tif tt.buffer.index != tt.wantIdx {\n\t\t\t\tt.Errorf(\"buffer.move() index = %v, want %v\", tt.buffer.index, tt.wantIdx)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBufferSkip(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tbuffer *buffer\n\t\tb byte\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"Skip byte\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\tb: 'e',\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Skip to EOF\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\tb: 'x',\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := tt.buffer.skip(tt.b)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"buffer.skip() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBufferSkipAny(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tbuffer *buffer\n\t\ts map[byte]bool\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"Skip any valid byte\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\ts: map[byte]bool{'e': true, 'o': true},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Skip any to EOF\",\n\t\t\tbuffer: \u0026buffer{data: []byte(\"test\"), length: 4, index: 0},\n\t\t\ts: map[byte]bool{'x': true, 'y': true},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\terr := tt.buffer.skipAny(tt.s)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"buffer.skipAny() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSkipToNextSignificantToken(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []byte\n\t\texpected int\n\t}{\n\t\t{\"No significant chars\", []byte(\"abc\"), 3},\n\t\t{\"One significant char at start\", []byte(\".abc\"), 0},\n\t\t{\"Significant char in middle\", []byte(\"ab.c\"), 2},\n\t\t{\"Multiple significant chars\", []byte(\"a$.c\"), 1},\n\t\t{\"Significant char at end\", []byte(\"abc$\"), 3},\n\t\t{\"Only significant chars\", []byte(\"$.\"), 0},\n\t\t{\"Empty string\", []byte(\"\"), 0},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := newBuffer(tt.input)\n\t\t\tb.skipToNextSignificantToken()\n\t\t\tif b.index != tt.expected {\n\t\t\t\tt.Errorf(\"after skipToNextSignificantToken(), got index = %v, want %v\", b.index, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc mockBuffer(s string) *buffer {\n\treturn newBuffer([]byte(s))\n}\n\nfunc TestSkipAndReturnIndex(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput string\n\t\texpected int\n\t}{\n\t\t{\"StartOfString\", \"\", 0},\n\t\t{\"MiddleOfString\", \"abcdef\", 1},\n\t\t{\"EndOfString\", \"abc\", 1},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tbuf := mockBuffer(tt.input)\n\t\t\tgot, err := buf.skipAndReturnIndex()\n\t\t\tif err != nil \u0026\u0026 tt.input != \"\" { // Expect no error unless input is empty\n\t\t\t\tt.Errorf(\"skipAndReturnIndex() error = %v\", err)\n\t\t\t}\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"skipAndReturnIndex() = %v, want %v\", got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSkipUntil(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput string\n\t\ttokens map[byte]bool\n\t\texpected int\n\t}{\n\t\t{\"SkipToToken\", \"abcdefg\", map[byte]bool{'c': true}, 2},\n\t\t{\"SkipToEnd\", \"abcdefg\", map[byte]bool{'h': true}, 7},\n\t\t{\"SkipNone\", \"abcdefg\", map[byte]bool{'a': true}, 0},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tbuf := mockBuffer(tt.input)\n\t\t\tgot, err := buf.skipUntil(tt.tokens)\n\t\t\tif err != nil \u0026\u0026 got != len(tt.input) { // Expect error only if reached end without finding token\n\t\t\t\tt.Errorf(\"skipUntil() error = %v\", err)\n\t\t\t}\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"skipUntil() = %v, want %v\", got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSliceFromIndices(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput string\n\t\tstart int\n\t\tend int\n\t\texpected string\n\t}{\n\t\t{\"FullString\", \"abcdefg\", 0, 7, \"abcdefg\"},\n\t\t{\"Substring\", \"abcdefg\", 2, 5, \"cde\"},\n\t\t{\"OutOfBounds\", \"abcdefg\", 5, 10, \"fg\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tbuf := mockBuffer(tt.input)\n\t\t\tgot := buf.sliceFromIndices(tt.start, tt.end)\n\t\t\tif string(got) != tt.expected {\n\t\t\t\tt.Errorf(\"sliceFromIndices() = %v, want %v\", string(got), tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBufferToken(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tpath string\n\t\tindex int\n\t\tisErr bool\n\t}{\n\t\t{\n\t\t\tname: \"Simple valid path\",\n\t\t\tpath: \"@.length\",\n\t\t\tindex: 8,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Path with array expr\",\n\t\t\tpath: \"@['foo'].0.bar\",\n\t\t\tindex: 14,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Path with array expr and simple fomula\",\n\t\t\tpath: \"@['foo'].[(@.length - 1)].*\",\n\t\t\tindex: 27,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Path with filter expr\",\n\t\t\tpath: \"@['foo'].[?(@.bar == 1 \u0026 @.baz \u003c @.length)].*\",\n\t\t\tindex: 45,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"addition of foo and bar\",\n\t\t\tpath: \"@.foo+@.bar\",\n\t\t\tindex: 11,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"logical AND of foo and bar\",\n\t\t\tpath: \"@.foo \u0026\u0026 @.bar\",\n\t\t\tindex: 14,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"logical OR of foo and bar\",\n\t\t\tpath: \"@.foo || @.bar\",\n\t\t\tindex: 14,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"accessing third element of foo\",\n\t\t\tpath: \"@.foo,3\",\n\t\t\tindex: 7,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"accessing last element of array\",\n\t\t\tpath: \"@.length-1\",\n\t\t\tindex: 10,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"number 1\",\n\t\t\tpath: \"1\",\n\t\t\tindex: 1,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"float\",\n\t\t\tpath: \"3.1e4\",\n\t\t\tindex: 5,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"float with minus\",\n\t\t\tpath: \"3.1e-4\",\n\t\t\tindex: 6,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"float with plus\",\n\t\t\tpath: \"3.1e+4\",\n\t\t\tindex: 6,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"negative number\",\n\t\t\tpath: \"-12345\",\n\t\t\tindex: 6,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"negative float\",\n\t\t\tpath: \"-3.1e4\",\n\t\t\tindex: 6,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"negative float with minus\",\n\t\t\tpath: \"-3.1e-4\",\n\t\t\tindex: 7,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"negative float with plus\",\n\t\t\tpath: \"-3.1e+4\",\n\t\t\tindex: 7,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"string number\",\n\t\t\tpath: \"'12345'\",\n\t\t\tindex: 7,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"string with backslash\",\n\t\t\tpath: \"'foo \\\\'bar '\",\n\t\t\tindex: 12,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"string with inner double quotes\",\n\t\t\tpath: \"'foo \\\"bar \\\"'\",\n\t\t\tindex: 12,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"parenthesis 1\",\n\t\t\tpath: \"(@abc)\",\n\t\t\tindex: 6,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"parenthesis 2\",\n\t\t\tpath: \"[()]\",\n\t\t\tindex: 4,\n\t\t\tisErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"parenthesis mismatch\",\n\t\t\tpath: \"[(])\",\n\t\t\tindex: 2,\n\t\t\tisErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"parenthesis mismatch 2\",\n\t\t\tpath: \"(\",\n\t\t\tindex: 1,\n\t\t\tisErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"parenthesis mismatch 3\",\n\t\t\tpath: \"())]\",\n\t\t\tindex: 2,\n\t\t\tisErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"bracket mismatch\",\n\t\t\tpath: \"[()\",\n\t\t\tindex: 3,\n\t\t\tisErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"bracket mismatch 2\",\n\t\t\tpath: \"()]\",\n\t\t\tindex: 2,\n\t\t\tisErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"path does not close bracket\",\n\t\t\tpath: \"@.foo[)\",\n\t\t\tindex: 6,\n\t\t\tisErr: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tbuf := newBuffer([]byte(tt.path))\n\n\t\t\terr := buf.pathToken()\n\t\t\tif tt.isErr {\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Errorf(\"Expected an error for path `%s`, but got none\", tt.path)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif err == nil \u0026\u0026 tt.isErr {\n\t\t\t\tt.Errorf(\"Expected an error for path `%s`, but got none\", tt.path)\n\t\t\t}\n\n\t\t\tif buf.index != tt.index {\n\t\t\t\tt.Errorf(\"Expected final index %d, got %d (token: `%s`) for path `%s`\", tt.index, buf.index, string(buf.data[buf.index]), tt.path)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBufferFirst(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tdata []byte\n\t\texpected byte\n\t}{\n\t\t{\n\t\t\tname: \"Valid first byte\",\n\t\t\tdata: []byte(\"test\"),\n\t\t\texpected: 't',\n\t\t},\n\t\t{\n\t\t\tname: \"Empty buffer\",\n\t\t\tdata: []byte(\"\"),\n\t\t\texpected: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Whitespace buffer\",\n\t\t\tdata: []byte(\" \"),\n\t\t\texpected: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"whitespace in middle\",\n\t\t\tdata: []byte(\"hello world\"),\n\t\t\texpected: 'h',\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := newBuffer(tt.data)\n\n\t\t\tgot, err := b.first()\n\t\t\tif err != nil \u0026\u0026 tt.expected != 0 {\n\t\t\t\tt.Errorf(\"Unexpected error: %v\", err)\n\t\t\t}\n\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"Expected first byte to be %q, got %q\", tt.expected, got)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "decode.gno", + "body": "// ref: https://github.com/spyzhov/ajson/blob/master/decode.go\n\npackage json\n\nimport (\n\t\"errors\"\n\t\"io\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// This limits the max nesting depth to prevent stack overflow.\n// This is permitted by https://tools.ietf.org/html/rfc7159#section-9\nconst maxNestingDepth = 10000\n\n// Unmarshal parses the JSON-encoded data and returns a Node.\n// The data must be a valid JSON-encoded value.\n//\n// Usage:\n//\n//\tnode, err := json.Unmarshal([]byte(`{\"key\": \"value\"}`))\n//\tif err != nil {\n//\t\tufmt.Println(err)\n//\t}\n//\tprintln(node) // {\"key\": \"value\"}\nfunc Unmarshal(data []byte) (*Node, error) {\n\tbuf := newBuffer(data)\n\n\tvar (\n\t\tstate States\n\t\tkey *string\n\t\tcurrent *Node\n\t\tnesting int\n\t\tuseKey = func() **string {\n\t\t\ttmp := cptrs(key)\n\t\t\tkey = nil\n\t\t\treturn \u0026tmp\n\t\t}\n\t\terr error\n\t)\n\n\tif _, err = buf.first(); err != nil {\n\t\treturn nil, io.EOF\n\t}\n\n\tfor {\n\t\tstate = buf.getState()\n\t\tif state == __ {\n\t\t\treturn nil, unexpectedTokenError(buf.data, buf.index)\n\t\t}\n\n\t\t// region state machine\n\t\tif state \u003e= GO {\n\t\t\tswitch buf.state {\n\t\t\tcase ST: // string\n\t\t\t\tif current != nil \u0026\u0026 current.IsObject() \u0026\u0026 key == nil {\n\t\t\t\t\t// key detected\n\t\t\t\t\tif key, err = getString(buf); err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\n\t\t\t\t\tbuf.state = CO\n\t\t\t\t} else {\n\t\t\t\t\tcurrent, nesting, err = createNestedNode(current, buf, String, nesting, useKey())\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\n\t\t\t\t\terr = buf.string(doubleQuote, false)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrent, nesting = updateNode(current, buf, nesting, true)\n\t\t\t\t\tbuf.state = OK\n\t\t\t\t}\n\n\t\t\tcase MI, ZE, IN: // number\n\t\t\t\tcurrent, err = processNumericNode(current, buf, useKey())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\tcase T1, F1: // boolean\n\t\t\t\tliteral := falseLiteral\n\t\t\t\tif buf.state == T1 {\n\t\t\t\t\tliteral = trueLiteral\n\t\t\t\t}\n\n\t\t\t\tcurrent, nesting, err = processLiteralNode(current, buf, Boolean, literal, useKey(), nesting)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\tcase N1: // null\n\t\t\t\tcurrent, nesting, err = processLiteralNode(current, buf, Null, nullLiteral, useKey(), nesting)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// region action\n\t\t\tswitch state {\n\t\t\tcase ec, cc: // \u003cempty\u003e }\n\t\t\t\tif key != nil {\n\t\t\t\t\treturn nil, unexpectedTokenError(buf.data, buf.index)\n\t\t\t\t}\n\n\t\t\t\tcurrent, nesting, err = updateNodeAndSetBufferState(current, buf, nesting, Object)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\tcase bc: // ]\n\t\t\t\tcurrent, nesting, err = updateNodeAndSetBufferState(current, buf, nesting, Array)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\tcase co, bo: // { [\n\t\t\t\tvalTyp, bState := Object, OB\n\t\t\t\tif state == bo {\n\t\t\t\t\tvalTyp, bState = Array, AR\n\t\t\t\t}\n\n\t\t\t\tcurrent, nesting, err = createNestedNode(current, buf, valTyp, nesting, useKey())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tbuf.state = bState\n\n\t\t\tcase cm: // ,\n\t\t\t\tif current == nil {\n\t\t\t\t\treturn nil, unexpectedTokenError(buf.data, buf.index)\n\t\t\t\t}\n\n\t\t\t\tif !current.isContainer() {\n\t\t\t\t\treturn nil, unexpectedTokenError(buf.data, buf.index)\n\t\t\t\t}\n\n\t\t\t\tif current.IsObject() {\n\t\t\t\t\tbuf.state = KE // key expected\n\t\t\t\t} else {\n\t\t\t\t\tbuf.state = VA // value expected\n\t\t\t\t}\n\n\t\t\tcase cl: // :\n\t\t\t\tif current == nil || !current.IsObject() || key == nil {\n\t\t\t\t\treturn nil, unexpectedTokenError(buf.data, buf.index)\n\t\t\t\t}\n\n\t\t\t\tbuf.state = VA\n\n\t\t\tdefault:\n\t\t\t\treturn nil, unexpectedTokenError(buf.data, buf.index)\n\t\t\t}\n\t\t}\n\n\t\tif buf.step() != nil {\n\t\t\tbreak\n\t\t}\n\n\t\tif _, err = buf.first(); err != nil {\n\t\t\terr = nil\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif current == nil || buf.state != OK {\n\t\treturn nil, io.EOF\n\t}\n\n\troot := current.root()\n\tif !root.ready() {\n\t\treturn nil, io.EOF\n\t}\n\n\treturn root, err\n}\n\n// UnmarshalSafe parses the JSON-encoded data and returns a Node.\nfunc UnmarshalSafe(data []byte) (*Node, error) {\n\tvar safe []byte\n\tsafe = append(safe, data...)\n\treturn Unmarshal(safe)\n}\n\n// processNumericNode creates a new node, processes a numeric value,\n// sets the node's borders, and moves to the previous node.\nfunc processNumericNode(current *Node, buf *buffer, key **string) (*Node, error) {\n\tvar err error\n\tcurrent, err = createNode(current, buf, Number, key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = buf.numeric(false); err != nil {\n\t\treturn nil, err\n\t}\n\n\tcurrent.borders[1] = buf.index\n\tif current.prev != nil {\n\t\tcurrent = current.prev\n\t}\n\n\tbuf.index -= 1\n\tbuf.state = OK\n\n\treturn current, nil\n}\n\n// processLiteralNode creates a new node, processes a literal value,\n// sets the node's borders, and moves to the previous node.\nfunc processLiteralNode(\n\tcurrent *Node,\n\tbuf *buffer,\n\tliteralType ValueType,\n\tliteralValue []byte,\n\tuseKey **string,\n\tnesting int,\n) (*Node, int, error) {\n\tvar err error\n\tcurrent, nesting, err = createLiteralNode(current, buf, literalType, literalValue, useKey, nesting)\n\tif err != nil {\n\t\treturn nil, nesting, err\n\t}\n\treturn current, nesting, nil\n}\n\n// isValidContainerType checks if the current node is a valid container (object or array).\n// The container must satisfy the following conditions:\n// 1. The current node must not be nil.\n// 2. The current node must be an object or array.\n// 3. The current node must not be ready.\nfunc isValidContainerType(current *Node, nodeType ValueType) bool {\n\tswitch nodeType {\n\tcase Object:\n\t\treturn current != nil \u0026\u0026 current.IsObject() \u0026\u0026 !current.ready()\n\tcase Array:\n\t\treturn current != nil \u0026\u0026 current.IsArray() \u0026\u0026 !current.ready()\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// getString extracts a string from the buffer and advances the buffer index past the string.\nfunc getString(b *buffer) (*string, error) {\n\tstart := b.index\n\tif err := b.string(doubleQuote, false); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvalue, ok := Unquote(b.data[start:b.index+1], doubleQuote)\n\tif !ok {\n\t\treturn nil, unexpectedTokenError(b.data, start)\n\t}\n\n\treturn \u0026value, nil\n}\n\n// createNode creates a new node and sets the key if it is not nil.\nfunc createNode(\n\tcurrent *Node,\n\tbuf *buffer,\n\tnodeType ValueType,\n\tkey **string,\n) (*Node, error) {\n\tvar err error\n\tcurrent, err = NewNode(current, buf, nodeType, key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn current, nil\n}\n\n// createNestedNode creates a new nested node (array or object) and sets the key if it is not nil.\nfunc createNestedNode(\n\tcurrent *Node,\n\tbuf *buffer,\n\tnodeType ValueType,\n\tnesting int,\n\tkey **string,\n) (*Node, int, error) {\n\tvar err error\n\tif nesting, err = checkNestingDepth(nesting); err != nil {\n\t\treturn nil, nesting, err\n\t}\n\n\tif current, err = createNode(current, buf, nodeType, key); err != nil {\n\t\treturn nil, nesting, err\n\t}\n\n\treturn current, nesting, nil\n}\n\n// createLiteralNode creates a new literal node and sets the key if it is not nil.\n// The literal is a byte slice that represents a boolean or null value.\nfunc createLiteralNode(\n\tcurrent *Node,\n\tbuf *buffer,\n\tliteralType ValueType,\n\tliteral []byte,\n\tuseKey **string,\n\tnesting int,\n) (*Node, int, error) {\n\tvar err error\n\tif current, err = createNode(current, buf, literalType, useKey); err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tif err = buf.word(literal); err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tcurrent, nesting = updateNode(current, buf, nesting, false)\n\tbuf.state = OK\n\n\treturn current, nesting, nil\n}\n\n// updateNode updates the current node and returns the previous node.\nfunc updateNode(\n\tcurrent *Node, buf *buffer, nesting int, decreaseLevel bool,\n) (*Node, int) {\n\tcurrent.borders[1] = buf.index + 1\n\n\tprev := current.prev\n\tif prev == nil {\n\t\treturn current, nesting\n\t}\n\n\tcurrent = prev\n\tif decreaseLevel {\n\t\tnesting--\n\t}\n\n\treturn current, nesting\n}\n\n// updateNodeAndSetBufferState updates the current node and sets the buffer state to OK.\nfunc updateNodeAndSetBufferState(\n\tcurrent *Node,\n\tbuf *buffer,\n\tnesting int,\n\ttyp ValueType,\n) (*Node, int, error) {\n\tif !isValidContainerType(current, typ) {\n\t\treturn nil, nesting, unexpectedTokenError(buf.data, buf.index)\n\t}\n\n\tcurrent, nesting = updateNode(current, buf, nesting, true)\n\tbuf.state = OK\n\n\treturn current, nesting, nil\n}\n\n// checkNestingDepth checks if the nesting depth is within the maximum allowed depth.\nfunc checkNestingDepth(nesting int) (int, error) {\n\tif nesting \u003e= maxNestingDepth {\n\t\treturn nesting, errors.New(\"maximum nesting depth exceeded\")\n\t}\n\n\treturn nesting + 1, nil\n}\n\nfunc unexpectedTokenError(data []byte, index int) error {\n\treturn ufmt.Errorf(\"unexpected token at index %d. data %b\", index, data)\n}\n" + }, + { + "name": "decode_test.gno", + "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\ntype testNode struct {\n\tname string\n\tinput []byte\n\t_type ValueType\n\tvalue []byte\n}\n\nfunc simpleValid(test *testNode, t *testing.T) {\n\troot, err := Unmarshal(test.input)\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(%s): %s\", test.input, err.Error())\n\t} else if root == nil {\n\t\tt.Errorf(\"Error on Unmarshal(%s): root is nil\", test.name)\n\t} else if root.nodeType != test._type {\n\t\tt.Errorf(\"Error on Unmarshal(%s): wrong type\", test.name)\n\t} else if !bytes.Equal(root.source(), test.value) {\n\t\tt.Errorf(\"Error on Unmarshal(%s): %s != %s\", test.name, root.source(), test.value)\n\t}\n}\n\nfunc simpleInvalid(test *testNode, t *testing.T) {\n\troot, err := Unmarshal(test.input)\n\tif err == nil {\n\t\tt.Errorf(\"Error on Unmarshal(%s): error expected, got '%s'\", test.name, root.source())\n\t} else if root != nil {\n\t\tt.Errorf(\"Error on Unmarshal(%s): root is not nil\", test.name)\n\t}\n}\n\nfunc simpleCorrupted(name string) *testNode {\n\treturn \u0026testNode{name: name, input: []byte(name)}\n}\n\nfunc TestUnmarshal_StringSimpleSuccess(t *testing.T) {\n\ttests := []*testNode{\n\t\t{name: \"blank\", input: []byte(\"\\\"\\\"\"), _type: String, value: []byte(\"\\\"\\\"\")},\n\t\t{name: \"char\", input: []byte(\"\\\"c\\\"\"), _type: String, value: []byte(\"\\\"c\\\"\")},\n\t\t{name: \"word\", input: []byte(\"\\\"cat\\\"\"), _type: String, value: []byte(\"\\\"cat\\\"\")},\n\t\t{name: \"spaces\", input: []byte(\" \\\"good cat or dog\\\"\\r\\n \"), _type: String, value: []byte(\"\\\"good cat or dog\\\"\")},\n\t\t{name: \"backslash\", input: []byte(\"\\\"good \\\\\\\"cat\\\\\\\"\\\"\"), _type: String, value: []byte(\"\\\"good \\\\\\\"cat\\\\\\\"\\\"\")},\n\t\t{name: \"backslash 2\", input: []byte(\"\\\"good \\\\\\\\\\\\\\\"cat\\\\\\\"\\\"\"), _type: String, value: []byte(\"\\\"good \\\\\\\\\\\\\\\"cat\\\\\\\"\\\"\")},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleValid(test, t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_NumericSimpleSuccess(t *testing.T) {\n\ttests := []*testNode{\n\t\t{name: \"1\", input: []byte(\"1\"), _type: Number, value: []byte(\"1\")},\n\t\t{name: \"-1\", input: []byte(\"-1\"), _type: Number, value: []byte(\"-1\")},\n\n\t\t{name: \"1234567890\", input: []byte(\"1234567890\"), _type: Number, value: []byte(\"1234567890\")},\n\t\t{name: \"-123\", input: []byte(\"-123\"), _type: Number, value: []byte(\"-123\")},\n\n\t\t{name: \"123.456\", input: []byte(\"123.456\"), _type: Number, value: []byte(\"123.456\")},\n\t\t{name: \"-123.456\", input: []byte(\"-123.456\"), _type: Number, value: []byte(\"-123.456\")},\n\n\t\t{name: \"1e3\", input: []byte(\"1e3\"), _type: Number, value: []byte(\"1e3\")},\n\t\t{name: \"1e+3\", input: []byte(\"1e+3\"), _type: Number, value: []byte(\"1e+3\")},\n\t\t{name: \"1e-3\", input: []byte(\"1e-3\"), _type: Number, value: []byte(\"1e-3\")},\n\t\t{name: \"-1e3\", input: []byte(\"-1e3\"), _type: Number, value: []byte(\"-1e3\")},\n\t\t{name: \"-1e-3\", input: []byte(\"-1e-3\"), _type: Number, value: []byte(\"-1e-3\")},\n\n\t\t{name: \"1.123e3456\", input: []byte(\"1.123e3456\"), _type: Number, value: []byte(\"1.123e3456\")},\n\t\t{name: \"1.123e-3456\", input: []byte(\"1.123e-3456\"), _type: Number, value: []byte(\"1.123e-3456\")},\n\t\t{name: \"-1.123e3456\", input: []byte(\"-1.123e3456\"), _type: Number, value: []byte(\"-1.123e3456\")},\n\t\t{name: \"-1.123e-3456\", input: []byte(\"-1.123e-3456\"), _type: Number, value: []byte(\"-1.123e-3456\")},\n\n\t\t{name: \"1E3\", input: []byte(\"1E3\"), _type: Number, value: []byte(\"1E3\")},\n\t\t{name: \"1E-3\", input: []byte(\"1E-3\"), _type: Number, value: []byte(\"1E-3\")},\n\t\t{name: \"-1E3\", input: []byte(\"-1E3\"), _type: Number, value: []byte(\"-1E3\")},\n\t\t{name: \"-1E-3\", input: []byte(\"-1E-3\"), _type: Number, value: []byte(\"-1E-3\")},\n\n\t\t{name: \"1.123E3456\", input: []byte(\"1.123E3456\"), _type: Number, value: []byte(\"1.123E3456\")},\n\t\t{name: \"1.123E-3456\", input: []byte(\"1.123E-3456\"), _type: Number, value: []byte(\"1.123E-3456\")},\n\t\t{name: \"-1.123E3456\", input: []byte(\"-1.123E3456\"), _type: Number, value: []byte(\"-1.123E3456\")},\n\t\t{name: \"-1.123E-3456\", input: []byte(\"-1.123E-3456\"), _type: Number, value: []byte(\"-1.123E-3456\")},\n\n\t\t{name: \"-1.123E-3456 with spaces\", input: []byte(\" \\r -1.123E-3456 \\t\\n\"), _type: Number, value: []byte(\"-1.123E-3456\")},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\troot, err := Unmarshal(test.input)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Error on Unmarshal(%s): %s\", test.name, err.Error())\n\t\t\t} else if root == nil {\n\t\t\t\tt.Errorf(\"Error on Unmarshal(%s): root is nil\", test.name)\n\t\t\t} else if root.nodeType != test._type {\n\t\t\t\tt.Errorf(\"Error on Unmarshal(%s): wrong type\", test.name)\n\t\t\t} else if !bytes.Equal(root.source(), test.value) {\n\t\t\t\tt.Errorf(\"Error on Unmarshal(%s): %s != %s\", test.name, root.source(), test.value)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_StringSimpleCorrupted(t *testing.T) {\n\ttests := []*testNode{\n\t\t{name: \"white NL\", input: []byte(\"\\\"foo\\nbar\\\"\")},\n\t\t{name: \"white R\", input: []byte(\"\\\"foo\\rbar\\\"\")},\n\t\t{name: \"white Tab\", input: []byte(\"\\\"foo\\tbar\\\"\")},\n\t\t{name: \"wrong quotes\", input: []byte(\"'cat'\")},\n\t\t{name: \"double string\", input: []byte(\"\\\"Hello\\\" \\\"World\\\"\")},\n\t\t{name: \"quotes in quotes\", input: []byte(\"\\\"good \\\"cat\\\"\\\"\")},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleInvalid(test, t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_ObjectSimpleSuccess(t *testing.T) {\n\ttests := []*testNode{\n\t\t{name: \"{}\", input: []byte(\"{}\"), _type: Object, value: []byte(\"{}\")},\n\t\t{name: `{ \\r\\n }`, input: []byte(\"{ \\r\\n }\"), _type: Object, value: []byte(\"{ \\r\\n }\")},\n\t\t{name: `{\"key\":1}`, input: []byte(`{\"key\":1}`), _type: Object, value: []byte(`{\"key\":1}`)},\n\t\t{name: `{\"key\":true}`, input: []byte(`{\"key\":true}`), _type: Object, value: []byte(`{\"key\":true}`)},\n\t\t{name: `{\"key\":\"value\"}`, input: []byte(`{\"key\":\"value\"}`), _type: Object, value: []byte(`{\"key\":\"value\"}`)},\n\t\t{name: `{\"foo\":\"bar\",\"baz\":\"foo\"}`, input: []byte(`{\"foo\":\"bar\", \"baz\":\"foo\"}`), _type: Object, value: []byte(`{\"foo\":\"bar\", \"baz\":\"foo\"}`)},\n\t\t{name: \"spaces\", input: []byte(` { \"foo\" : \"bar\" , \"baz\" : \"foo\" } `), _type: Object, value: []byte(`{ \"foo\" : \"bar\" , \"baz\" : \"foo\" }`)},\n\t\t{name: \"nested\", input: []byte(`{\"foo\":{\"bar\":{\"baz\":{}}}}`), _type: Object, value: []byte(`{\"foo\":{\"bar\":{\"baz\":{}}}}`)},\n\t\t{name: \"array\", input: []byte(`{\"array\":[{},{},{\"foo\":[{\"bar\":[\"baz\"]}]}]}`), _type: Object, value: []byte(`{\"array\":[{},{},{\"foo\":[{\"bar\":[\"baz\"]}]}]}`)},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleValid(test, t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_ObjectSimpleCorrupted(t *testing.T) {\n\ttests := []*testNode{\n\t\tsimpleCorrupted(\"{{{\\\"key\\\": \\\"foo\\\"{{{{\"),\n\t\tsimpleCorrupted(\"}\"),\n\t\tsimpleCorrupted(\"{ }}}}}}}\"),\n\t\tsimpleCorrupted(\" }\"),\n\t\tsimpleCorrupted(\"{,}\"),\n\t\tsimpleCorrupted(\"{:}\"),\n\t\tsimpleCorrupted(\"{100000}\"),\n\t\tsimpleCorrupted(\"{1:1}\"),\n\t\tsimpleCorrupted(\"{'1:2,3:4'}\"),\n\t\tsimpleCorrupted(`{\"d\"}`),\n\t\tsimpleCorrupted(`{\"foo\"}`),\n\t\tsimpleCorrupted(`{\"foo\":}`),\n\t\tsimpleCorrupted(`{:\"foo\"}`),\n\t\tsimpleCorrupted(`{\"foo\":bar}`),\n\t\tsimpleCorrupted(`{\"foo\":\"bar\",}`),\n\t\tsimpleCorrupted(`{}{}`),\n\t\tsimpleCorrupted(`{},{}`),\n\t\tsimpleCorrupted(`{[},{]}`),\n\t\tsimpleCorrupted(`{[,]}`),\n\t\tsimpleCorrupted(`{[]}`),\n\t\tsimpleCorrupted(`{}1`),\n\t\tsimpleCorrupted(`1{}`),\n\t\tsimpleCorrupted(`{\"x\"::1}`),\n\t\tsimpleCorrupted(`{null:null}`),\n\t\tsimpleCorrupted(`{\"foo:\"bar\"}`),\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleInvalid(test, t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_NullSimpleCorrupted(t *testing.T) {\n\ttests := []*testNode{\n\t\t{name: \"nul\", input: []byte(\"nul\")},\n\t\t{name: \"nil\", input: []byte(\"nil\")},\n\t\t{name: \"nill\", input: []byte(\"nill\")},\n\t\t{name: \"NILL\", input: []byte(\"NILL\")},\n\t\t{name: \"Null\", input: []byte(\"Null\")},\n\t\t{name: \"NULL\", input: []byte(\"NULL\")},\n\t\t{name: \"spaces\", input: []byte(\"Nu ll\")},\n\t\t{name: \"null1\", input: []byte(\"null1\")},\n\t\t{name: \"double\", input: []byte(\"null null\")},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleInvalid(test, t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_BoolSimpleSuccess(t *testing.T) {\n\ttests := []*testNode{\n\t\t{name: \"lower true\", input: []byte(\"true\"), _type: Boolean, value: []byte(\"true\")},\n\t\t{name: \"lower false\", input: []byte(\"false\"), _type: Boolean, value: []byte(\"false\")},\n\t\t{name: \"spaces true\", input: []byte(\" true\\r\\n \"), _type: Boolean, value: []byte(\"true\")},\n\t\t{name: \"spaces false\", input: []byte(\" false\\r\\n \"), _type: Boolean, value: []byte(\"false\")},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleValid(test, t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_BoolSimpleCorrupted(t *testing.T) {\n\ttests := []*testNode{\n\t\tsimpleCorrupted(\"tru\"),\n\t\tsimpleCorrupted(\"fals\"),\n\t\tsimpleCorrupted(\"tre\"),\n\t\tsimpleCorrupted(\"fal se\"),\n\t\tsimpleCorrupted(\"true false\"),\n\t\tsimpleCorrupted(\"True\"),\n\t\tsimpleCorrupted(\"TRUE\"),\n\t\tsimpleCorrupted(\"False\"),\n\t\tsimpleCorrupted(\"FALSE\"),\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleInvalid(test, t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_ArraySimpleSuccess(t *testing.T) {\n\ttests := []*testNode{\n\t\t{name: \"[]\", input: []byte(\"[]\"), _type: Array, value: []byte(\"[]\")},\n\t\t{name: \"[1]\", input: []byte(\"[1]\"), _type: Array, value: []byte(\"[1]\")},\n\t\t{name: \"[1,2,3]\", input: []byte(\"[1,2,3]\"), _type: Array, value: []byte(\"[1,2,3]\")},\n\t\t{name: \"[1, 2, 3]\", input: []byte(\"[1, 2, 3]\"), _type: Array, value: []byte(\"[1, 2, 3]\")},\n\t\t{name: \"[1,[2],3]\", input: []byte(\"[1,[2],3]\"), _type: Array, value: []byte(\"[1,[2],3]\")},\n\t\t{name: \"[[],[],[]]\", input: []byte(\"[[],[],[]]\"), _type: Array, value: []byte(\"[[],[],[]]\")},\n\t\t{name: \"[[[[[]]]]]\", input: []byte(\"[[[[[]]]]]\"), _type: Array, value: []byte(\"[[[[[]]]]]\")},\n\t\t{name: \"[true,null,1,\\\"foo\\\",[]]\", input: []byte(\"[true,null,1,\\\"foo\\\",[]]\"), _type: Array, value: []byte(\"[true,null,1,\\\"foo\\\",[]]\")},\n\t\t{name: \"spaces\", input: []byte(\"\\n\\r [\\n1\\n ]\\r\\n\"), _type: Array, value: []byte(\"[\\n1\\n ]\")},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleValid(test, t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_ArraySimpleCorrupted(t *testing.T) {\n\ttests := []*testNode{\n\t\tsimpleCorrupted(\"[,]\"),\n\t\tsimpleCorrupted(\"[]\\\\\"),\n\t\tsimpleCorrupted(\"[1,]\"),\n\t\tsimpleCorrupted(\"[[]\"),\n\t\tsimpleCorrupted(\"[]]\"),\n\t\tsimpleCorrupted(\"1[]\"),\n\t\tsimpleCorrupted(\"[]1\"),\n\t\tsimpleCorrupted(\"[[]1]\"),\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsimpleInvalid(test, t)\n\t\t})\n\t}\n}\n\n// Examples from https://json.org/example.html\nfunc TestUnmarshal(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tvalue string\n\t}{\n\t\t{\n\t\t\tname: \"glossary\",\n\t\t\tvalue: `{\n\t\t\t\t\"glossary\": {\n\t\t\t\t\t\"title\": \"example glossary\",\n\t\t\t\t\t\"GlossDiv\": {\n\t\t\t\t\t\t\"title\": \"S\",\n\t\t\t\t\t\t\"GlossList\": {\n\t\t\t\t\t\t\t\"GlossEntry\": {\n\t\t\t\t\t\t\t\t\"ID\": \"SGML\",\n\t\t\t\t\t\t\t\t\"SortAs\": \"SGML\",\n\t\t\t\t\t\t\t\t\"GlossTerm\": \"Standard Generalized Markup Language\",\n\t\t\t\t\t\t\t\t\"Acronym\": \"SGML\",\n\t\t\t\t\t\t\t\t\"Abbrev\": \"ISO 8879:1986\",\n\t\t\t\t\t\t\t\t\"GlossDef\": {\n\t\t\t\t\t\t\t\t\t\"para\": \"A meta-markup language, used to create markup languages such as DocBook.\",\n\t\t\t\t\t\t\t\t\t\"GlossSeeAlso\": [\"GML\", \"XML\"]\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"GlossSee\": \"markup\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}`,\n\t\t},\n\t\t{\n\t\t\tname: \"menu\",\n\t\t\tvalue: `{\"menu\": {\n\t\t\t\t\"id\": \"file\",\n\t\t\t\t\"value\": \"File\",\n\t\t\t\t\"popup\": {\n\t\t\t\t \"menuitem\": [\n\t\t\t\t\t{\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\n\t\t\t\t\t{\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\n\t\t\t\t\t{\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\n\t\t\t\t ]\n\t\t\t\t}\n\t\t\t}}`,\n\t\t},\n\t\t{\n\t\t\tname: \"widget\",\n\t\t\tvalue: `{\"widget\": {\n\t\t\t\t\"debug\": \"on\",\n\t\t\t\t\"window\": {\n\t\t\t\t\t\"title\": \"Sample Konfabulator Widget\",\n\t\t\t\t\t\"name\": \"main_window\",\n\t\t\t\t\t\"width\": 500,\n\t\t\t\t\t\"height\": 500\n\t\t\t\t},\n\t\t\t\t\"image\": { \n\t\t\t\t\t\"src\": \"Images/Sun.png\",\n\t\t\t\t\t\"name\": \"sun1\",\n\t\t\t\t\t\"hOffset\": 250,\n\t\t\t\t\t\"vOffset\": 250,\n\t\t\t\t\t\"alignment\": \"center\"\n\t\t\t\t},\n\t\t\t\t\"text\": {\n\t\t\t\t\t\"data\": \"Click Here\",\n\t\t\t\t\t\"size\": 36,\n\t\t\t\t\t\"style\": \"bold\",\n\t\t\t\t\t\"name\": \"text1\",\n\t\t\t\t\t\"hOffset\": 250,\n\t\t\t\t\t\"vOffset\": 100,\n\t\t\t\t\t\"alignment\": \"center\",\n\t\t\t\t\t\"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n\t\t\t\t}\n\t\t\t}} `,\n\t\t},\n\t\t{\n\t\t\tname: \"web-app\",\n\t\t\tvalue: `{\"web-app\": {\n\t\t\t\t\"servlet\": [ \n\t\t\t\t {\n\t\t\t\t\t\"servlet-name\": \"cofaxCDS\",\n\t\t\t\t\t\"servlet-class\": \"org.cofax.cds.CDSServlet\",\n\t\t\t\t\t\"init-param\": {\n\t\t\t\t\t \"configGlossary:installationAt\": \"Philadelphia, PA\",\n\t\t\t\t\t \"configGlossary:adminEmail\": \"ksm@pobox.com\",\n\t\t\t\t\t \"configGlossary:poweredBy\": \"Cofax\",\n\t\t\t\t\t \"configGlossary:poweredByIcon\": \"/images/cofax.gif\",\n\t\t\t\t\t \"configGlossary:staticPath\": \"/content/static\",\n\t\t\t\t\t \"templateProcessorClass\": \"org.cofax.WysiwygTemplate\",\n\t\t\t\t\t \"templateLoaderClass\": \"org.cofax.FilesTemplateLoader\",\n\t\t\t\t\t \"templatePath\": \"templates\",\n\t\t\t\t\t \"templateOverridePath\": \"\",\n\t\t\t\t\t \"defaultListTemplate\": \"listTemplate.htm\",\n\t\t\t\t\t \"defaultFileTemplate\": \"articleTemplate.htm\",\n\t\t\t\t\t \"useJSP\": false,\n\t\t\t\t\t \"jspListTemplate\": \"listTemplate.jsp\",\n\t\t\t\t\t \"jspFileTemplate\": \"articleTemplate.jsp\",\n\t\t\t\t\t \"cachePackageTagsTrack\": 200,\n\t\t\t\t\t \"cachePackageTagsStore\": 200,\n\t\t\t\t\t \"cachePackageTagsRefresh\": 60,\n\t\t\t\t\t \"cacheTemplatesTrack\": 100,\n\t\t\t\t\t \"cacheTemplatesStore\": 50,\n\t\t\t\t\t \"cacheTemplatesRefresh\": 15,\n\t\t\t\t\t \"cachePagesTrack\": 200,\n\t\t\t\t\t \"cachePagesStore\": 100,\n\t\t\t\t\t \"cachePagesRefresh\": 10,\n\t\t\t\t\t \"cachePagesDirtyRead\": 10,\n\t\t\t\t\t \"searchEngineListTemplate\": \"forSearchEnginesList.htm\",\n\t\t\t\t\t \"searchEngineFileTemplate\": \"forSearchEngines.htm\",\n\t\t\t\t\t \"searchEngineRobotsDb\": \"WEB-INF/robots.db\",\n\t\t\t\t\t \"useDataStore\": true,\n\t\t\t\t\t \"dataStoreClass\": \"org.cofax.SqlDataStore\",\n\t\t\t\t\t \"redirectionClass\": \"org.cofax.SqlRedirection\",\n\t\t\t\t\t \"dataStoreName\": \"cofax\",\n\t\t\t\t\t \"dataStoreDriver\": \"com.microsoft.jdbc.sqlserver.SQLServerDriver\",\n\t\t\t\t\t \"dataStoreUrl\": \"jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon\",\n\t\t\t\t\t \"dataStoreUser\": \"sa\",\n\t\t\t\t\t \"dataStorePassword\": \"dataStoreTestQuery\",\n\t\t\t\t\t \"dataStoreTestQuery\": \"SET NOCOUNT ON;select test='test';\",\n\t\t\t\t\t \"dataStoreLogFile\": \"/usr/local/tomcat/logs/datastore.log\",\n\t\t\t\t\t \"dataStoreInitConns\": 10,\n\t\t\t\t\t \"dataStoreMaxConns\": 100,\n\t\t\t\t\t \"dataStoreConnUsageLimit\": 100,\n\t\t\t\t\t \"dataStoreLogLevel\": \"debug\",\n\t\t\t\t\t \"maxUrlLength\": 500}},\n\t\t\t\t {\n\t\t\t\t\t\"servlet-name\": \"cofaxEmail\",\n\t\t\t\t\t\"servlet-class\": \"org.cofax.cds.EmailServlet\",\n\t\t\t\t\t\"init-param\": {\n\t\t\t\t\t\"mailHost\": \"mail1\",\n\t\t\t\t\t\"mailHostOverride\": \"mail2\"}},\n\t\t\t\t {\n\t\t\t\t\t\"servlet-name\": \"cofaxAdmin\",\n\t\t\t\t\t\"servlet-class\": \"org.cofax.cds.AdminServlet\"},\n\t\t\t \n\t\t\t\t {\n\t\t\t\t\t\"servlet-name\": \"fileServlet\",\n\t\t\t\t\t\"servlet-class\": \"org.cofax.cds.FileServlet\"},\n\t\t\t\t {\n\t\t\t\t\t\"servlet-name\": \"cofaxTools\",\n\t\t\t\t\t\"servlet-class\": \"org.cofax.cms.CofaxToolsServlet\",\n\t\t\t\t\t\"init-param\": {\n\t\t\t\t\t \"templatePath\": \"toolstemplates/\",\n\t\t\t\t\t \"log\": 1,\n\t\t\t\t\t \"logLocation\": \"/usr/local/tomcat/logs/CofaxTools.log\",\n\t\t\t\t\t \"logMaxSize\": \"\",\n\t\t\t\t\t \"dataLog\": 1,\n\t\t\t\t\t \"dataLogLocation\": \"/usr/local/tomcat/logs/dataLog.log\",\n\t\t\t\t\t \"dataLogMaxSize\": \"\",\n\t\t\t\t\t \"removePageCache\": \"/content/admin/remove?cache=pages\u0026id=\",\n\t\t\t\t\t \"removeTemplateCache\": \"/content/admin/remove?cache=templates\u0026id=\",\n\t\t\t\t\t \"fileTransferFolder\": \"/usr/local/tomcat/webapps/content/fileTransferFolder\",\n\t\t\t\t\t \"lookInContext\": 1,\n\t\t\t\t\t \"adminGroupID\": 4,\n\t\t\t\t\t \"betaServer\": true}}],\n\t\t\t\t\"servlet-mapping\": {\n\t\t\t\t \"cofaxCDS\": \"/\",\n\t\t\t\t \"cofaxEmail\": \"/cofaxutil/aemail/*\",\n\t\t\t\t \"cofaxAdmin\": \"/admin/*\",\n\t\t\t\t \"fileServlet\": \"/static/*\",\n\t\t\t\t \"cofaxTools\": \"/tools/*\"},\n\t\t\t \n\t\t\t\t\"taglib\": {\n\t\t\t\t \"taglib-uri\": \"cofax.tld\",\n\t\t\t\t \"taglib-location\": \"/WEB-INF/tlds/cofax.tld\"}}}`,\n\t\t},\n\t\t{\n\t\t\tname: \"SVG Viewer\",\n\t\t\tvalue: `{\"menu\": {\n\t\t\t\t\"header\": \"SVG Viewer\",\n\t\t\t\t\"items\": [\n\t\t\t\t\t{\"id\": \"Open\"},\n\t\t\t\t\t{\"id\": \"OpenNew\", \"label\": \"Open New\"},\n\t\t\t\t\tnull,\n\t\t\t\t\t{\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},\n\t\t\t\t\t{\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},\n\t\t\t\t\t{\"id\": \"OriginalView\", \"label\": \"Original View\"},\n\t\t\t\t\tnull,\n\t\t\t\t\t{\"id\": \"Quality\"},\n\t\t\t\t\t{\"id\": \"Pause\"},\n\t\t\t\t\t{\"id\": \"Mute\"},\n\t\t\t\t\tnull,\n\t\t\t\t\t{\"id\": \"Find\", \"label\": \"Find...\"},\n\t\t\t\t\t{\"id\": \"FindAgain\", \"label\": \"Find Again\"},\n\t\t\t\t\t{\"id\": \"Copy\"},\n\t\t\t\t\t{\"id\": \"CopyAgain\", \"label\": \"Copy Again\"},\n\t\t\t\t\t{\"id\": \"CopySVG\", \"label\": \"Copy SVG\"},\n\t\t\t\t\t{\"id\": \"ViewSVG\", \"label\": \"View SVG\"},\n\t\t\t\t\t{\"id\": \"ViewSource\", \"label\": \"View Source\"},\n\t\t\t\t\t{\"id\": \"SaveAs\", \"label\": \"Save As\"},\n\t\t\t\t\tnull,\n\t\t\t\t\t{\"id\": \"Help\"},\n\t\t\t\t\t{\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}\n\t\t\t\t]\n\t\t\t}}`,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\t_, err := Unmarshal([]byte(test.value))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Error on Unmarshal: %s\", err.Error())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalSafe(t *testing.T) {\n\tjson := []byte(`{ \"store\": {\n\t\t\"book\": [ \n\t\t { \"category\": \"reference\",\n\t\t\t\"author\": \"Nigel Rees\",\n\t\t\t\"title\": \"Sayings of the Century\",\n\t\t\t\"price\": 8.95\n\t\t },\n\t\t { \"category\": \"fiction\",\n\t\t\t\"author\": \"Evelyn Waugh\",\n\t\t\t\"title\": \"Sword of Honour\",\n\t\t\t\"price\": 12.99\n\t\t },\n\t\t { \"category\": \"fiction\",\n\t\t\t\"author\": \"Herman Melville\",\n\t\t\t\"title\": \"Moby Dick\",\n\t\t\t\"isbn\": \"0-553-21311-3\",\n\t\t\t\"price\": 8.99\n\t\t },\n\t\t { \"category\": \"fiction\",\n\t\t\t\"author\": \"J. R. R. Tolkien\",\n\t\t\t\"title\": \"The Lord of the Rings\",\n\t\t\t\"isbn\": \"0-395-19395-8\",\n\t\t\t\"price\": 22.99\n\t\t }\n\t\t],\n\t\t\"bicycle\": {\n\t\t \"color\": \"red\",\n\t\t \"price\": 19.95\n\t\t}\n\t }\n\t}`)\n\tsafe, err := UnmarshalSafe(json)\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal: %s\", err.Error())\n\t} else if safe == nil {\n\t\tt.Errorf(\"Error on Unmarshal: safe is nil\")\n\t} else {\n\t\troot, err := Unmarshal(json)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error on Unmarshal: %s\", err.Error())\n\t\t} else if root == nil {\n\t\t\tt.Errorf(\"Error on Unmarshal: root is nil\")\n\t\t} else if !bytes.Equal(root.source(), safe.source()) {\n\t\t\tt.Errorf(\"Error on UnmarshalSafe: values not same\")\n\t\t}\n\t}\n}\n\n// BenchmarkGoStdUnmarshal-8 \t 61698\t 19350 ns/op\t 288 B/op\t 6 allocs/op\n// BenchmarkUnmarshal-8 \t 45620\t 26165 ns/op\t 21889 B/op\t 367 allocs/op\n//\n// type bench struct {\n// \tName string `json:\"name\"`\n// \tValue int `json:\"value\"`\n// }\n\n// func BenchmarkGoStdUnmarshal(b *testing.B) {\n// \tdata := []byte(webApp)\n// \tfor i := 0; i \u003c b.N; i++ {\n// \t\terr := json.Unmarshal(data, \u0026bench{})\n// \t\tif err != nil {\n// \t\t\tb.Fatal(err)\n// \t\t}\n// \t}\n// }\n\n// func BenchmarkUnmarshal(b *testing.B) {\n// \tdata := []byte(webApp)\n// \tfor i := 0; i \u003c b.N; i++ {\n// \t\t_, err := Unmarshal(data)\n// \t\tif err != nil {\n// \t\t\tb.Fatal(err)\n// \t\t}\n// \t}\n// }\n" + }, + { + "name": "encode.gno", + "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"math\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/json/ryu\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// Marshal returns the JSON encoding of a Node.\nfunc Marshal(node *Node) ([]byte, error) {\n\tvar (\n\t\tbuf bytes.Buffer\n\t\tsVal string\n\t\tbVal bool\n\t\tnVal float64\n\t\toVal []byte\n\t\terr error\n\t)\n\n\tif node == nil {\n\t\treturn nil, errors.New(\"node is nil\")\n\t}\n\n\tif !node.modified \u0026\u0026 !node.ready() {\n\t\treturn nil, errors.New(\"node is not ready\")\n\t}\n\n\tif !node.modified \u0026\u0026 node.ready() {\n\t\tbuf.Write(node.source())\n\t}\n\n\tif node.modified {\n\t\tswitch node.nodeType {\n\t\tcase Null:\n\t\t\tbuf.Write(nullLiteral)\n\n\t\tcase Number:\n\t\t\tnVal, err = node.GetNumeric()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\t// ufmt does not support %g. by doing so, we need to check if the number is an integer\n\t\t\t// after then, apply the correct format for each float and integer numbers.\n\t\t\tif math.Mod(nVal, 1.0) == 0 {\n\t\t\t\t// must convert float to integer. otherwise it will be overflowed.\n\t\t\t\tnum := ufmt.Sprintf(\"%d\", int(nVal))\n\t\t\t\tbuf.WriteString(num)\n\t\t\t} else {\n\t\t\t\t// use ryu algorithm to convert float to string\n\t\t\t\tnum := ryu.FormatFloat64(nVal)\n\t\t\t\tbuf.WriteString(num)\n\t\t\t}\n\n\t\tcase String:\n\t\t\tsVal, err = node.GetString()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tquoted := ufmt.Sprintf(\"%s\", strconv.Quote(sVal))\n\t\t\tbuf.WriteString(quoted)\n\n\t\tcase Boolean:\n\t\t\tbVal, err = node.GetBool()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tbStr := ufmt.Sprintf(\"%t\", bVal)\n\t\t\tbuf.WriteString(bStr)\n\n\t\tcase Array:\n\t\t\tbuf.WriteByte(bracketOpen)\n\n\t\t\tfor i := 0; i \u003c len(node.next); i++ {\n\t\t\t\tif i != 0 {\n\t\t\t\t\tbuf.WriteByte(comma)\n\t\t\t\t}\n\n\t\t\t\telem, ok := node.next[strconv.Itoa(i)]\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, ufmt.Errorf(\"array element %d is not found\", i)\n\t\t\t\t}\n\n\t\t\t\toVal, err = Marshal(elem)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tbuf.Write(oVal)\n\t\t\t}\n\n\t\t\tbuf.WriteByte(bracketClose)\n\n\t\tcase Object:\n\t\t\tbuf.WriteByte(curlyOpen)\n\n\t\t\tbVal = false\n\t\t\tfor k, v := range node.next {\n\t\t\t\tif bVal {\n\t\t\t\t\tbuf.WriteByte(comma)\n\t\t\t\t} else {\n\t\t\t\t\tbVal = true\n\t\t\t\t}\n\n\t\t\t\tkey := ufmt.Sprintf(\"%s\", strconv.Quote(k))\n\t\t\t\tbuf.WriteString(key)\n\t\t\t\tbuf.WriteByte(colon)\n\n\t\t\t\toVal, err = Marshal(v)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tbuf.Write(oVal)\n\t\t\t}\n\n\t\t\tbuf.WriteByte(curlyClose)\n\t\t}\n\t}\n\n\treturn buf.Bytes(), nil\n}\n" + }, + { + "name": "encode_test.gno", + "body": "package json\n\nimport \"testing\"\n\nfunc TestMarshal_Primitive(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tnode *Node\n\t}{\n\t\t{\n\t\t\tname: \"null\",\n\t\t\tnode: NullNode(\"\"),\n\t\t},\n\t\t{\n\t\t\tname: \"true\",\n\t\t\tnode: BoolNode(\"\", true),\n\t\t},\n\t\t{\n\t\t\tname: \"false\",\n\t\t\tnode: BoolNode(\"\", false),\n\t\t},\n\t\t{\n\t\t\tname: `\"string\"`,\n\t\t\tnode: StringNode(\"\", \"string\"),\n\t\t},\n\t\t{\n\t\t\tname: `\"one \\\"encoded\\\" string\"`,\n\t\t\tnode: StringNode(\"\", `one \"encoded\" string`),\n\t\t},\n\t\t{\n\t\t\tname: `{\"foo\":\"bar\"}`,\n\t\t\tnode: ObjectNode(\"\", map[string]*Node{\n\t\t\t\t\"foo\": StringNode(\"foo\", \"bar\"),\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"42\",\n\t\t\tnode: NumberNode(\"\", 42),\n\t\t},\n\t\t// TODO: fix output for not to use scientific notation\n\t\t{\n\t\t\tname: \"1.005e+02\",\n\t\t\tnode: NumberNode(\"\", 100.5),\n\t\t},\n\t\t{\n\t\t\tname: `[1,2,3]`,\n\t\t\tnode: ArrayNode(\"\", []*Node{\n\t\t\t\tNumberNode(\"0\", 1),\n\t\t\t\tNumberNode(\"2\", 2),\n\t\t\t\tNumberNode(\"3\", 3),\n\t\t\t}),\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tvalue, err := Marshal(test.node)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"unexpected error: %s\", err)\n\t\t\t} else if string(value) != test.name {\n\t\t\t\tt.Errorf(\"wrong result: '%s', expected '%s'\", value, test.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMarshal_Object(t *testing.T) {\n\tnode := ObjectNode(\"\", map[string]*Node{\n\t\t\"foo\": StringNode(\"foo\", \"bar\"),\n\t\t\"baz\": NumberNode(\"baz\", 100500),\n\t\t\"qux\": NullNode(\"qux\"),\n\t})\n\n\tmustKey := []string{\"foo\", \"baz\", \"qux\"}\n\n\tvalue, err := Marshal(node)\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %s\", err)\n\t}\n\n\t// the order of keys in the map is not guaranteed\n\t// so we need to unmarshal the result and check the keys\n\tdecoded, err := Unmarshal(value)\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error: %s\", err)\n\t}\n\n\tfor _, key := range mustKey {\n\t\tif node, err := decoded.GetKey(key); err != nil {\n\t\t\tt.Errorf(\"unexpected error: %s\", err)\n\t\t} else {\n\t\t\tif node == nil {\n\t\t\t\tt.Errorf(\"node is nil\")\n\t\t\t} else if node.key == nil {\n\t\t\t\tt.Errorf(\"key is nil\")\n\t\t\t} else if *node.key != key {\n\t\t\t\tt.Errorf(\"wrong key: '%s', expected '%s'\", *node.key, key)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc valueNode(prev *Node, key string, typ ValueType, val interface{}) *Node {\n\tcurr := \u0026Node{\n\t\tprev: prev,\n\t\tdata: nil,\n\t\tkey: \u0026key,\n\t\tborders: [2]int{0, 0},\n\t\tvalue: val,\n\t\tmodified: true,\n\t}\n\n\tif val != nil {\n\t\tcurr.nodeType = typ\n\t}\n\n\treturn curr\n}\n\nfunc TestMarshal_Errors(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tnode func() (node *Node)\n\t}{\n\t\t{\n\t\t\tname: \"nil\",\n\t\t\tnode: func() (node *Node) {\n\t\t\t\treturn\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"broken\",\n\t\t\tnode: func() (node *Node) {\n\t\t\t\tnode = Must(Unmarshal([]byte(`{}`)))\n\t\t\t\tnode.borders[1] = 0\n\t\t\t\treturn\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Numeric\",\n\t\t\tnode: func() (node *Node) {\n\t\t\t\treturn valueNode(nil, \"\", Number, false)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"String\",\n\t\t\tnode: func() (node *Node) {\n\t\t\t\treturn valueNode(nil, \"\", String, false)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Bool\",\n\t\t\tnode: func() (node *Node) {\n\t\t\t\treturn valueNode(nil, \"\", Boolean, 1)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Array_1\",\n\t\t\tnode: func() (node *Node) {\n\t\t\t\tnode = ArrayNode(\"\", nil)\n\t\t\t\tnode.next[\"1\"] = NullNode(\"1\")\n\t\t\t\treturn\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Array_2\",\n\t\t\tnode: func() (node *Node) {\n\t\t\t\treturn ArrayNode(\"\", []*Node{valueNode(nil, \"\", Boolean, 1)})\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Object\",\n\t\t\tnode: func() (node *Node) {\n\t\t\t\treturn ObjectNode(\"\", map[string]*Node{\"key\": valueNode(nil, \"key\", Boolean, 1)})\n\t\t\t},\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tvalue, err := Marshal(test.node())\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"expected error\")\n\t\t\t} else if len(value) != 0 {\n\t\t\t\tt.Errorf(\"wrong result\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMarshal_Nil(t *testing.T) {\n\t_, err := Marshal(nil)\n\tif err == nil {\n\t\tt.Error(\"Expected error for nil node, but got nil\")\n\t}\n}\n\nfunc TestMarshal_NotModified(t *testing.T) {\n\tnode := \u0026Node{}\n\t_, err := Marshal(node)\n\tif err == nil {\n\t\tt.Error(\"Expected error for not modified node, but got nil\")\n\t}\n}\n\nfunc TestMarshalCycleReference(t *testing.T) {\n\tnode1 := \u0026Node{\n\t\tkey: stringPtr(\"node1\"),\n\t\tnodeType: String,\n\t\tnext: map[string]*Node{\n\t\t\t\"next\": nil,\n\t\t},\n\t}\n\n\tnode2 := \u0026Node{\n\t\tkey: stringPtr(\"node2\"),\n\t\tnodeType: String,\n\t\tprev: node1,\n\t}\n\n\tnode1.next[\"next\"] = node2\n\n\t_, err := Marshal(node1)\n\tif err == nil {\n\t\tt.Error(\"Expected error for cycle reference, but got nil\")\n\t}\n}\n\nfunc TestMarshalNoCycleReference(t *testing.T) {\n\tnode1 := \u0026Node{\n\t\tkey: stringPtr(\"node1\"),\n\t\tnodeType: String,\n\t\tvalue: \"value1\",\n\t\tmodified: true,\n\t}\n\n\tnode2 := \u0026Node{\n\t\tkey: stringPtr(\"node2\"),\n\t\tnodeType: String,\n\t\tvalue: \"value2\",\n\t\tmodified: true,\n\t}\n\n\t_, err := Marshal(node1)\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error: %v\", err)\n\t}\n\n\t_, err = Marshal(node2)\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error: %v\", err)\n\t}\n}\n\nfunc stringPtr(s string) *string {\n\treturn \u0026s\n}\n" + }, + { + "name": "escape.gno", + "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"unicode/utf8\"\n)\n\nconst (\n\tsupplementalPlanesOffset = 0x10000\n\thighSurrogateOffset = 0xD800\n\tlowSurrogateOffset = 0xDC00\n\tsurrogateEnd = 0xDFFF\n\tbasicMultilingualPlaneOffset = 0xFFFF\n\tbadHex = -1\n)\n\nvar hexLookupTable = [256]int{\n\t'0': 0x0, '1': 0x1, '2': 0x2, '3': 0x3, '4': 0x4,\n\t'5': 0x5, '6': 0x6, '7': 0x7, '8': 0x8, '9': 0x9,\n\t'A': 0xA, 'B': 0xB, 'C': 0xC, 'D': 0xD, 'E': 0xE, 'F': 0xF,\n\t'a': 0xA, 'b': 0xB, 'c': 0xC, 'd': 0xD, 'e': 0xE, 'f': 0xF,\n\t// Fill unspecified index-value pairs with key and value of -1\n\t'G': -1, 'H': -1, 'I': -1, 'J': -1,\n\t'K': -1, 'L': -1, 'M': -1, 'N': -1,\n\t'O': -1, 'P': -1, 'Q': -1, 'R': -1,\n\t'S': -1, 'T': -1, 'U': -1, 'V': -1,\n\t'W': -1, 'X': -1, 'Y': -1, 'Z': -1,\n\t'g': -1, 'h': -1, 'i': -1, 'j': -1,\n\t'k': -1, 'l': -1, 'm': -1, 'n': -1,\n\t'o': -1, 'p': -1, 'q': -1, 'r': -1,\n\t's': -1, 't': -1, 'u': -1, 'v': -1,\n\t'w': -1, 'x': -1, 'y': -1, 'z': -1,\n}\n\nfunc h2i(c byte) int {\n\treturn hexLookupTable[c]\n}\n\n// Unescape takes an input byte slice, processes it to Unescape certain characters,\n// and writes the result into an output byte slice.\n//\n// it returns the processed slice and any error encountered during the Unescape operation.\nfunc Unescape(input, output []byte) ([]byte, error) {\n\t// find the index of the first backslash in the input slice.\n\tfirstBackslash := bytes.IndexByte(input, backSlash)\n\tif firstBackslash == -1 {\n\t\treturn input, nil\n\t}\n\n\t// ensure the output slice has enough capacity to hold the result.\n\tinputLen := len(input)\n\tif cap(output) \u003c inputLen {\n\t\toutput = make([]byte, inputLen)\n\t}\n\n\toutput = output[:inputLen]\n\tcopy(output, input[:firstBackslash])\n\n\tinput = input[firstBackslash:]\n\tbuf := output[firstBackslash:]\n\n\tfor len(input) \u003e 0 {\n\t\tinLen, bufLen, err := processEscapedUTF8(input, buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tinput = input[inLen:] // the number of bytes consumed in the input\n\t\tbuf = buf[bufLen:] // the number of bytes written to buf\n\n\t\t// find the next backslash in the remaining input\n\t\tnextBackslash := bytes.IndexByte(input, backSlash)\n\t\tif nextBackslash == -1 {\n\t\t\tcopy(buf, input)\n\t\t\tbuf = buf[len(input):]\n\t\t\tbreak\n\t\t}\n\n\t\tcopy(buf, input[:nextBackslash])\n\n\t\tinput = input[nextBackslash:]\n\t\tbuf = buf[nextBackslash:]\n\t}\n\n\treturn output[:len(output)-len(buf)], nil\n}\n\n// isSurrogatePair returns true if the rune is a surrogate pair.\n//\n// A surrogate pairs are used in UTF-16 encoding to encode characters\n// outside the Basic Multilingual Plane (BMP).\nfunc isSurrogatePair(r rune) bool {\n\treturn highSurrogateOffset \u003c= r \u0026\u0026 r \u003c= surrogateEnd\n}\n\n// combineSurrogates reconstruct the original unicode code points in the\n// supplemental plane by combinin the high and low surrogate.\n//\n// The hight surrogate in the range from U+D800 to U+DBFF,\n// and the low surrogate in the range from U+DC00 to U+DFFF.\n//\n// The formula to combine the surrogates is:\n// (high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000\nfunc combineSurrogates(high, low rune) rune {\n\treturn ((high - highSurrogateOffset) \u003c\u003c 10) + (low - lowSurrogateOffset) + supplementalPlanesOffset\n}\n\n// deocdeSingleUnicodeEscape decodes a unicode escape sequence (e.g., \\uXXXX) into a rune.\nfunc decodeSingleUnicodeEscape(b []byte) (rune, bool) {\n\tif len(b) \u003c 6 {\n\t\treturn utf8.RuneError, false\n\t}\n\n\t// convert hex to decimal\n\th1, h2, h3, h4 := h2i(b[2]), h2i(b[3]), h2i(b[4]), h2i(b[5])\n\tif h1 == badHex || h2 == badHex || h3 == badHex || h4 == badHex {\n\t\treturn utf8.RuneError, false\n\t}\n\n\treturn rune(h1\u003c\u003c12 + h2\u003c\u003c8 + h3\u003c\u003c4 + h4), true\n}\n\n// decodeUnicodeEscape decodes a Unicode escape sequence from a byte slice.\nfunc decodeUnicodeEscape(b []byte) (rune, int) {\n\tr, ok := decodeSingleUnicodeEscape(b)\n\tif !ok {\n\t\treturn utf8.RuneError, -1\n\t}\n\n\t// determine valid unicode escapes within the BMP\n\tif r \u003c= basicMultilingualPlaneOffset \u0026\u0026 !isSurrogatePair(r) {\n\t\treturn r, 6\n\t}\n\n\t// Decode the following escape sequence to verify a UTF-16 susergate pair.\n\tr2, ok := decodeSingleUnicodeEscape(b[6:])\n\tif !ok {\n\t\treturn utf8.RuneError, -1\n\t}\n\n\tif r2 \u003c lowSurrogateOffset {\n\t\treturn utf8.RuneError, -1\n\t}\n\n\treturn combineSurrogates(r, r2), 12\n}\n\nvar escapeByteSet = [256]byte{\n\t'\"': doubleQuote,\n\t'\\\\': backSlash,\n\t'/': slash,\n\t'b': backSpace,\n\t'f': formFeed,\n\t'n': newLine,\n\t'r': carriageReturn,\n\t't': tab,\n}\n\n// Unquote takes a byte slice and unquotes it by removing\n// the surrounding quotes and unescaping the contents.\nfunc Unquote(s []byte, border byte) (string, bool) {\n\ts, ok := unquoteBytes(s, border)\n\treturn string(s), ok\n}\n\n// unquoteBytes takes a byte slice and unquotes it by removing\n// TODO: consider to move this function to the strconv package.\nfunc unquoteBytes(s []byte, border byte) ([]byte, bool) {\n\tif len(s) \u003c 2 || s[0] != border || s[len(s)-1] != border {\n\t\treturn nil, false\n\t}\n\n\ts = s[1 : len(s)-1]\n\n\tr := 0\n\tfor r \u003c len(s) {\n\t\tc := s[r]\n\n\t\tif c == backSlash || c == border || c \u003c 0x20 {\n\t\t\tbreak\n\t\t}\n\n\t\tif c \u003c utf8.RuneSelf {\n\t\t\tr++\n\t\t\tcontinue\n\t\t}\n\n\t\trr, size := utf8.DecodeRune(s[r:])\n\t\tif rr == utf8.RuneError \u0026\u0026 size == 1 {\n\t\t\tbreak\n\t\t}\n\n\t\tr += size\n\t}\n\n\tif r == len(s) {\n\t\treturn s, true\n\t}\n\n\tutfDoubleMax := utf8.UTFMax * 2\n\tb := make([]byte, len(s)+utfDoubleMax)\n\tw := copy(b, s[0:r])\n\n\tfor r \u003c len(s) {\n\t\tif w \u003e= len(b)-utf8.UTFMax {\n\t\t\tnb := make([]byte, utfDoubleMax+(2*len(b)))\n\t\t\tcopy(nb, b)\n\t\t\tb = nb\n\t\t}\n\n\t\tc := s[r]\n\t\tif c == backSlash {\n\t\t\tr++\n\t\t\tif r \u003e= len(s) {\n\t\t\t\treturn nil, false\n\t\t\t}\n\n\t\t\tif s[r] == 'u' {\n\t\t\t\trr, res := decodeUnicodeEscape(s[r-1:])\n\t\t\t\tif res \u003c 0 {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\n\t\t\t\tw += utf8.EncodeRune(b[w:], rr)\n\t\t\t\tr += 5\n\t\t\t} else {\n\t\t\t\tdecode := escapeByteSet[s[r]]\n\t\t\t\tif decode == 0 {\n\t\t\t\t\treturn nil, false\n\t\t\t\t}\n\n\t\t\t\tif decode == doubleQuote || decode == backSlash || decode == slash {\n\t\t\t\t\tdecode = s[r]\n\t\t\t\t}\n\n\t\t\t\tb[w] = decode\n\t\t\t\tr++\n\t\t\t\tw++\n\t\t\t}\n\t\t} else if c == border || c \u003c 0x20 {\n\t\t\treturn nil, false\n\t\t} else if c \u003c utf8.RuneSelf {\n\t\t\tb[w] = c\n\t\t\tr++\n\t\t\tw++\n\t\t} else {\n\t\t\trr, size := utf8.DecodeRune(s[r:])\n\n\t\t\tif rr == utf8.RuneError \u0026\u0026 size == 1 {\n\t\t\t\treturn nil, false\n\t\t\t}\n\n\t\t\tr += size\n\t\t\tw += utf8.EncodeRune(b[w:], rr)\n\t\t}\n\t}\n\n\treturn b[:w], true\n}\n\n// processEscapedUTF8 processes the escape sequence in the given byte slice and\n// and converts them to UTF-8 characters. The function returns the length of the processed input and output.\n//\n// The input 'in' must contain the escape sequence to be processed,\n// and 'out' provides a space to store the converted characters.\n//\n// The function returns (input length, output length) if the escape sequence is correct.\n// Unicode escape sequences (e.g. \\uXXXX) are decoded to UTF-8, other default escape sequences are\n// converted to their corresponding special characters (e.g. \\n -\u003e newline).\n//\n// If the escape sequence is invalid, or if 'in' does not completely enclose the escape sequence,\n// function returns (-1, -1) to indicate an error.\nfunc processEscapedUTF8(in, out []byte) (int, int, error) {\n\tif len(in) \u003c 2 || in[0] != backSlash {\n\t\treturn -1, -1, errors.New(\"invalid escape sequence\")\n\t}\n\n\tescapeSeqLen := 2\n\tescapeChar := in[1]\n\n\tif escapeChar != 'u' {\n\t\tval := escapeByteSet[escapeChar]\n\t\tif val == 0 {\n\t\t\treturn -1, -1, errors.New(\"invalid escape sequence\")\n\t\t}\n\n\t\tout[0] = val\n\t\treturn escapeSeqLen, 1, nil\n\t}\n\n\tr, size := decodeUnicodeEscape(in)\n\tif size == -1 {\n\t\treturn -1, -1, errors.New(\"invalid escape sequence\")\n\t}\n\n\toutLen := utf8.EncodeRune(out, r)\n\n\treturn size, outLen, nil\n}\n" + }, + { + "name": "escape_test.gno", + "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"unicode/utf8\"\n)\n\nfunc TestHexToInt(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tc byte\n\t\twant int\n\t}{\n\t\t{\"Digit 0\", '0', 0},\n\t\t{\"Digit 9\", '9', 9},\n\t\t{\"Uppercase A\", 'A', 10},\n\t\t{\"Uppercase F\", 'F', 15},\n\t\t{\"Lowercase a\", 'a', 10},\n\t\t{\"Lowercase f\", 'f', 15},\n\t\t{\"Invalid character1\", 'g', badHex},\n\t\t{\"Invalid character2\", 'G', badHex},\n\t\t{\"Invalid character3\", 'z', badHex},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := h2i(tt.c); got != tt.want {\n\t\t\t\tt.Errorf(\"h2i() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsSurrogatePair(t *testing.T) {\n\ttestCases := []struct {\n\t\tname string\n\t\tr rune\n\t\texpected bool\n\t}{\n\t\t{\"high surrogate start\", 0xD800, true},\n\t\t{\"high surrogate end\", 0xDBFF, true},\n\t\t{\"low surrogate start\", 0xDC00, true},\n\t\t{\"low surrogate end\", 0xDFFF, true},\n\t\t{\"Non-surrogate\", 0x0000, false},\n\t\t{\"Non-surrogate 2\", 0xE000, false},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tif got := isSurrogatePair(tc.r); got != tc.expected {\n\t\t\t\tt.Errorf(\"isSurrogate() = %v, want %v\", got, tc.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCombineSurrogates(t *testing.T) {\n\ttestCases := []struct {\n\t\thigh, low rune\n\t\texpected rune\n\t}{\n\t\t{0xD83D, 0xDC36, 0x1F436}, // 🐶 U+1F436 DOG FACE\n\t\t{0xD83D, 0xDE00, 0x1F600}, // 😀 U+1F600 GRINNING FACE\n\t\t{0xD83C, 0xDF03, 0x1F303}, // 🌃 U+1F303 NIGHT WITH STARS\n\t}\n\n\tfor _, tc := range testCases {\n\t\tresult := combineSurrogates(tc.high, tc.low)\n\t\tif result != tc.expected {\n\t\t\tt.Errorf(\"combineSurrogates(%U, %U) = %U; want %U\", tc.high, tc.low, result, tc.expected)\n\t\t}\n\t}\n}\n\nfunc TestDecodeSingleUnicodeEscape(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput []byte\n\t\texpected rune\n\t\tisValid bool\n\t}{\n\t\t// valid unicode escape sequences\n\t\t{[]byte(`\\u0041`), 'A', true},\n\t\t{[]byte(`\\u03B1`), 'α', true},\n\t\t{[]byte(`\\u00E9`), 'é', true}, // valid non-English character\n\t\t{[]byte(`\\u0021`), '!', true}, // valid special character\n\t\t{[]byte(`\\uFF11`), '1', true},\n\t\t{[]byte(`\\uD83D`), 0xD83D, true},\n\t\t{[]byte(`\\uDE03`), 0xDE03, true},\n\n\t\t// invalid unicode escape sequences\n\t\t{[]byte(`\\u004`), utf8.RuneError, false}, // too short\n\t\t{[]byte(`\\uXYZW`), utf8.RuneError, false}, // invalid hex\n\t\t{[]byte(`\\u00G1`), utf8.RuneError, false}, // non-hex character\n\t}\n\n\tfor _, tc := range testCases {\n\t\tresult, isValid := decodeSingleUnicodeEscape(tc.input)\n\t\tif result != tc.expected || isValid != tc.isValid {\n\t\t\tt.Errorf(\"decodeSingleUnicodeEscape(%s) = (%U, %v); want (%U, %v)\", tc.input, result, isValid, tc.expected, tc.isValid)\n\t\t}\n\t}\n}\n\nfunc TestDecodeUnicodeEscape(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput string\n\t\texpected rune\n\t\tsize int\n\t}{\n\t\t{\"\\\\u0041\", 'A', 6},\n\t\t{\"\\\\u03B1\", 'α', 6},\n\t\t{\"\\\\u1F600\", 0x1F60, 6},\n\t\t{\"\\\\uD830\\\\uDE03\", 0x1C203, 12},\n\t\t{\"\\\\uD800\\\\uDC00\", 0x00010000, 12},\n\n\t\t{\"\\\\u004\", utf8.RuneError, -1},\n\t\t{\"\\\\uXYZW\", utf8.RuneError, -1},\n\t\t{\"\\\\uD83D\\\\u0041\", utf8.RuneError, -1},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tr, size := decodeUnicodeEscape([]byte(tc.input))\n\t\tif r != tc.expected || size != tc.size {\n\t\t\tt.Errorf(\"decodeUnicodeEscape(%q) = (%U, %d); want (%U, %d)\", tc.input, r, size, tc.expected, tc.size)\n\t\t}\n\t}\n}\n\nfunc TestUnescapeToUTF8(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput []byte\n\t\texpectedIn int\n\t\texpectedOut int\n\t\tisError bool\n\t}{\n\t\t// valid escape sequences\n\t\t{[]byte(`\\n`), 2, 1, false},\n\t\t{[]byte(`\\t`), 2, 1, false},\n\t\t{[]byte(`\\u0041`), 6, 1, false},\n\t\t{[]byte(`\\u03B1`), 6, 2, false},\n\t\t{[]byte(`\\uD830\\uDE03`), 12, 4, false},\n\n\t\t// invalid escape sequences\n\t\t{[]byte(`\\`), -1, -1, true}, // incomplete escape sequence\n\t\t{[]byte(`\\x`), -1, -1, true}, // invalid escape character\n\t\t{[]byte(`\\u`), -1, -1, true}, // incomplete unicode escape sequence\n\t\t{[]byte(`\\u004`), -1, -1, true}, // invalid unicode escape sequence\n\t\t{[]byte(`\\uXYZW`), -1, -1, true}, // invalid unicode escape sequence\n\t\t{[]byte(`\\uD83D\\u0041`), -1, -1, true}, // invalid unicode escape sequence\n\t}\n\n\tfor _, tc := range testCases {\n\t\tinput := make([]byte, len(tc.input))\n\t\tcopy(input, tc.input)\n\t\toutput := make([]byte, utf8.UTFMax)\n\t\tinLen, outLen, err := processEscapedUTF8(input, output)\n\t\tif (err != nil) != tc.isError {\n\t\t\tt.Errorf(\"processEscapedUTF8(%q) = %v; want %v\", tc.input, err, tc.isError)\n\t\t}\n\n\t\tif inLen != tc.expectedIn || outLen != tc.expectedOut {\n\t\t\tt.Errorf(\"processEscapedUTF8(%q) = (%d, %d); want (%d, %d)\", tc.input, inLen, outLen, tc.expectedIn, tc.expectedOut)\n\t\t}\n\t}\n}\n\nfunc TestUnescape(t *testing.T) {\n\ttestCases := []struct {\n\t\tname string\n\t\tinput []byte\n\t\texpected []byte\n\t}{\n\t\t{\"NoEscape\", []byte(\"hello world\"), []byte(\"hello world\")},\n\t\t{\"SingleEscape\", []byte(\"hello\\\\nworld\"), []byte(\"hello\\nworld\")},\n\t\t{\"MultipleEscapes\", []byte(\"line1\\\\nline2\\\\r\\\\nline3\"), []byte(\"line1\\nline2\\r\\nline3\")},\n\t\t{\"UnicodeEscape\", []byte(\"snowman:\\\\u2603\"), []byte(\"snowman:\\u2603\")},\n\t\t{\"Complex\", []byte(\"tc\\\\n\\\\u2603\\\\r\\\\nend\"), []byte(\"tc\\n\\u2603\\r\\nend\")},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\toutput, _ := Unescape(tc.input, make([]byte, len(tc.input)+10))\n\t\t\tif !bytes.Equal(output, tc.expected) {\n\t\t\t\tt.Errorf(\"unescape(%q) = %q; want %q\", tc.input, output, tc.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnquoteBytes(t *testing.T) {\n\ttests := []struct {\n\t\tinput []byte\n\t\tborder byte\n\t\texpected []byte\n\t\tok bool\n\t}{\n\t\t{[]byte(\"\\\"hello\\\"\"), '\"', []byte(\"hello\"), true},\n\t\t{[]byte(\"'hello'\"), '\\'', []byte(\"hello\"), true},\n\t\t{[]byte(\"\\\"hello\"), '\"', nil, false},\n\t\t{[]byte(\"hello\\\"\"), '\"', nil, false},\n\t\t{[]byte(\"\\\"he\\\\\\\"llo\\\"\"), '\"', []byte(\"he\\\"llo\"), true},\n\t\t{[]byte(\"\\\"he\\\\nllo\\\"\"), '\"', []byte(\"he\\nllo\"), true},\n\t\t{[]byte(\"\\\"\\\"\"), '\"', []byte(\"\"), true},\n\t\t{[]byte(\"''\"), '\\'', []byte(\"\"), true},\n\t\t{[]byte(\"\\\"\\\\u0041\\\"\"), '\"', []byte(\"A\"), true},\n\t\t{[]byte(`\"Hello, 世界\"`), '\"', []byte(\"Hello, 世界\"), true},\n\t\t{[]byte(`\"Hello, \\x80\"`), '\"', nil, false},\n\t}\n\n\tfor _, tc := range tests {\n\t\tresult, pass := unquoteBytes(tc.input, tc.border)\n\n\t\tif pass != tc.ok {\n\t\t\tt.Errorf(\"unquoteBytes(%q) = %v; want %v\", tc.input, pass, tc.ok)\n\t\t}\n\n\t\tif !bytes.Equal(result, tc.expected) {\n\t\t\tt.Errorf(\"unquoteBytes(%q) = %q; want %q\", tc.input, result, tc.expected)\n\t\t}\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/json\n\nrequire (\n\tgno.land/p/demo/json/eisel_lemire v0.0.0-latest\n\tgno.land/p/demo/json/ryu v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "indent.gno", + "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n)\n\n// indentGrowthFactor specifies the growth factor of indenting JSON input.\n// A factor no higher than 2 ensures that wasted space never exceeds 50%.\nconst indentGrowthFactor = 2\n\n// IndentJSON takes a JSON byte slice and a string for indentation,\n// then formats the JSON according to the specified indent string.\n// This function applies indentation rules as follows:\n//\n// 1. For top-level arrays and objects, no additional indentation is applied.\n//\n// 2. For nested structures like arrays within arrays or objects, indentation increases.\n//\n// 3. Indentation is applied after opening brackets ('[' or '{') and before closing brackets (']' or '}').\n//\n// 4. Commas and colons are handled appropriately to maintain valid JSON format.\n//\n// 5. Nested arrays within objects or arrays receive new lines and indentation based on their depth level.\n//\n// The function returns the formatted JSON as a byte slice and an error if any issues occurred during formatting.\nfunc Indent(data []byte, indent string) ([]byte, error) {\n\tvar (\n\t\tout bytes.Buffer\n\t\tlevel int\n\t\tinArray bool\n\t\tarrayDepth int\n\t)\n\n\tfor i := 0; i \u003c len(data); i++ {\n\t\tc := data[i] // current character\n\n\t\tswitch c {\n\t\tcase bracketOpen:\n\t\t\tarrayDepth++\n\t\t\tif arrayDepth \u003e 1 {\n\t\t\t\tlevel++ // increase the level if it's nested array\n\t\t\t\tinArray = true\n\n\t\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// case of the top-level array\n\t\t\t\tinArray = true\n\t\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase bracketClose:\n\t\t\tif inArray \u0026\u0026 arrayDepth \u003e 1 { // nested array\n\t\t\t\tlevel--\n\t\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tarrayDepth--\n\t\t\tif arrayDepth == 0 {\n\t\t\t\tinArray = false\n\t\t\t}\n\n\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\tcase curlyOpen:\n\t\t\t// check if the empty object or array\n\t\t\t// we don't need to apply the indent when it's empty containers.\n\t\t\tif i+1 \u003c len(data) \u0026\u0026 data[i+1] == curlyClose {\n\t\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\ti++ // skip next character\n\t\t\t\tif err := out.WriteByte(data[i]); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tlevel++\n\t\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase curlyClose:\n\t\t\tlevel--\n\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\tcase comma, colon:\n\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif inArray \u0026\u0026 arrayDepth \u003e 1 { // nested array\n\t\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else if c == colon {\n\t\t\t\tif err := out.WriteByte(' '); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\tdefault:\n\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out.Bytes(), nil\n}\n\nfunc writeNewlineAndIndent(out *bytes.Buffer, level int, indent string) error {\n\tif err := out.WriteByte('\\n'); err != nil {\n\t\treturn err\n\t}\n\n\tidt := strings.Repeat(indent, level*indentGrowthFactor)\n\tif _, err := out.WriteString(idt); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n" + }, + { + "name": "indent_test.gno", + "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestIndentJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tinput []byte\n\t\tindent string\n\t\texpected []byte\n\t}{\n\t\t{\n\t\t\tname: \"empty object\",\n\t\t\tinput: []byte(`{}`),\n\t\t\tindent: \" \",\n\t\t\texpected: []byte(`{}`),\n\t\t},\n\t\t{\n\t\t\tname: \"empty array\",\n\t\t\tinput: []byte(`[]`),\n\t\t\tindent: \" \",\n\t\t\texpected: []byte(`[]`),\n\t\t},\n\t\t{\n\t\t\tname: \"nested object\",\n\t\t\tinput: []byte(`{{}}`),\n\t\t\tindent: \"\\t\",\n\t\t\texpected: []byte(\"{\\n\\t\\t{}\\n}\"),\n\t\t},\n\t\t{\n\t\t\tname: \"nested array\",\n\t\t\tinput: []byte(`[[[]]]`),\n\t\t\tindent: \"\\t\",\n\t\t\texpected: []byte(\"[[\\n\\t\\t[\\n\\t\\t\\t\\t\\n\\t\\t]\\n]]\"),\n\t\t},\n\t\t{\n\t\t\tname: \"top-level array\",\n\t\t\tinput: []byte(`[\"apple\",\"banana\",\"cherry\"]`),\n\t\t\tindent: \"\\t\",\n\t\t\texpected: []byte(`[\"apple\",\"banana\",\"cherry\"]`),\n\t\t},\n\t\t{\n\t\t\tname: \"array of arrays\",\n\t\t\tinput: []byte(`[\"apple\",[\"banana\",\"cherry\"],\"date\"]`),\n\t\t\tindent: \" \",\n\t\t\texpected: []byte(\"[\\\"apple\\\",[\\n \\\"banana\\\",\\n \\\"cherry\\\"\\n],\\\"date\\\"]\"),\n\t\t},\n\n\t\t{\n\t\t\tname: \"nested array in object\",\n\t\t\tinput: []byte(`{\"fruits\":[\"apple\",[\"banana\",\"cherry\"],\"date\"]}`),\n\t\t\tindent: \" \",\n\t\t\texpected: []byte(\"{\\n \\\"fruits\\\": [\\\"apple\\\",[\\n \\\"banana\\\",\\n \\\"cherry\\\"\\n ],\\\"date\\\"]\\n}\"),\n\t\t},\n\t\t{\n\t\t\tname: \"complex nested structure\",\n\t\t\tinput: []byte(`{\"data\":{\"array\":[1,2,3],\"bool\":true,\"nestedArray\":[[\"a\",\"b\"],\"c\"]}}`),\n\t\t\tindent: \" \",\n\t\t\texpected: []byte(\"{\\n \\\"data\\\": {\\n \\\"array\\\": [1,2,3],\\\"bool\\\": true,\\\"nestedArray\\\": [[\\n \\\"a\\\",\\n \\\"b\\\"\\n ],\\\"c\\\"]\\n }\\n}\"),\n\t\t},\n\t\t{\n\t\t\tname: \"custom ident character\",\n\t\t\tinput: []byte(`{\"fruits\":[\"apple\",[\"banana\",\"cherry\"],\"date\"]}`),\n\t\t\tindent: \"*\",\n\t\t\texpected: []byte(\"{\\n**\\\"fruits\\\": [\\\"apple\\\",[\\n****\\\"banana\\\",\\n****\\\"cherry\\\"\\n**],\\\"date\\\"]\\n}\"),\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tactual, err := Indent(tt.input, tt.indent)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"IndentJSON() error = %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !bytes.Equal(actual, tt.expected) {\n\t\t\t\tt.Errorf(\"IndentJSON() = %q, want %q\", actual, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "internal.gno", + "body": "package json\n\n// Reference: https://github.com/freddierice/php_source/blob/467ed5d6edff72219afd3e644516f131118ef48e/ext/json/JSON_parser.c\n// Copyright (c) 2005 JSON.org\n\n// Go implementation is taken from: https://github.com/spyzhov/ajson/blob/master/internal/state.go\n\ntype (\n\tStates int8 // possible states of the parser\n\tClasses int8 // JSON string character types\n)\n\nconst __ = -1\n\n// enum classes\nconst (\n\tC_SPACE Classes = iota /* space */\n\tC_WHITE /* other whitespace */\n\tC_LCURB /* { */\n\tC_RCURB /* } */\n\tC_LSQRB /* [ */\n\tC_RSQRB /* ] */\n\tC_COLON /* : */\n\tC_COMMA /* , */\n\tC_QUOTE /* \" */\n\tC_BACKS /* \\ */\n\tC_SLASH /* / */\n\tC_PLUS /* + */\n\tC_MINUS /* - */\n\tC_POINT /* . */\n\tC_ZERO /* 0 */\n\tC_DIGIT /* 123456789 */\n\tC_LOW_A /* a */\n\tC_LOW_B /* b */\n\tC_LOW_C /* c */\n\tC_LOW_D /* d */\n\tC_LOW_E /* e */\n\tC_LOW_F /* f */\n\tC_LOW_L /* l */\n\tC_LOW_N /* n */\n\tC_LOW_R /* r */\n\tC_LOW_S /* s */\n\tC_LOW_T /* t */\n\tC_LOW_U /* u */\n\tC_ABCDF /* ABCDF */\n\tC_E /* E */\n\tC_ETC /* everything else */\n)\n\n// AsciiClasses array maps the 128 ASCII characters into character classes.\nvar AsciiClasses = [128]Classes{\n\t/*\n\t This array maps the 128 ASCII characters into character classes.\n\t The remaining Unicode characters should be mapped to C_ETC.\n\t Non-whitespace control characters are errors.\n\t*/\n\t__, __, __, __, __, __, __, __,\n\t__, C_WHITE, C_WHITE, __, __, C_WHITE, __, __,\n\t__, __, __, __, __, __, __, __,\n\t__, __, __, __, __, __, __, __,\n\n\tC_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,\n\tC_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,\n\tC_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,\n\n\tC_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC,\n\n\tC_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC,\n\tC_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC,\n}\n\n// QuoteAsciiClasses is a HACK for single quote from AsciiClasses\nvar QuoteAsciiClasses = [128]Classes{\n\t/*\n\t This array maps the 128 ASCII characters into character classes.\n\t The remaining Unicode characters should be mapped to C_ETC.\n\t Non-whitespace control characters are errors.\n\t*/\n\t__, __, __, __, __, __, __, __,\n\t__, C_WHITE, C_WHITE, __, __, C_WHITE, __, __,\n\t__, __, __, __, __, __, __, __,\n\t__, __, __, __, __, __, __, __,\n\n\tC_SPACE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_QUOTE,\n\tC_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,\n\tC_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,\n\tC_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,\n\n\tC_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC,\n\n\tC_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC,\n\tC_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC,\n\tC_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC,\n}\n\n/*\nThe state codes.\n*/\nconst (\n\tGO States = iota /* start */\n\tOK /* ok */\n\tOB /* object */\n\tKE /* key */\n\tCO /* colon */\n\tVA /* value */\n\tAR /* array */\n\tST /* string */\n\tES /* escape */\n\tU1 /* u1 */\n\tU2 /* u2 */\n\tU3 /* u3 */\n\tU4 /* u4 */\n\tMI /* minus */\n\tZE /* zero */\n\tIN /* integer */\n\tDT /* dot */\n\tFR /* fraction */\n\tE1 /* e */\n\tE2 /* ex */\n\tE3 /* exp */\n\tT1 /* tr */\n\tT2 /* tru */\n\tT3 /* true */\n\tF1 /* fa */\n\tF2 /* fal */\n\tF3 /* fals */\n\tF4 /* false */\n\tN1 /* nu */\n\tN2 /* nul */\n\tN3 /* null */\n)\n\n// List of action codes.\n// these constants are defining an action that should be performed under certain conditions.\nconst (\n\tcl States = -2 /* colon */\n\tcm States = -3 /* comma */\n\tqt States = -4 /* quote */\n\tbo States = -5 /* bracket open */\n\tco States = -6 /* curly bracket open */\n\tbc States = -7 /* bracket close */\n\tcc States = -8 /* curly bracket close */\n\tec States = -9 /* curly bracket empty */\n)\n\n// StateTransitionTable is the state transition table takes the current state and the current symbol, and returns either\n// a new state or an action. An action is represented as a negative number. A JSON text is accepted if at the end of the\n// text the state is OK and if the mode is DONE.\nvar StateTransitionTable = [31][31]States{\n\t/*\n\t The state transition table takes the current state and the current symbol,\n\t and returns either a new state or an action. An action is represented as a\n\t negative number. A JSON text is accepted if at the end of the text the\n\t state is OK and if the mode is DONE.\n\t white 1-9 ABCDF etc\n\t space | { } [ ] : , \" \\ / + - . 0 | a b c d e f l n r s t u | E |*/\n\t/*start GO*/ {GO, GO, co, __, bo, __, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __},\n\t/*ok OK*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*object OB*/ {OB, OB, __, ec, __, __, __, __, ST, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*key KE*/ {KE, KE, __, __, __, __, __, __, ST, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*colon CO*/ {CO, CO, __, __, __, __, cl, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*value VA*/ {VA, VA, co, __, bo, __, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __},\n\t/*array AR*/ {AR, AR, co, __, bo, bc, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __},\n\t/*string ST*/ {ST, __, ST, ST, ST, ST, ST, ST, qt, ES, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST},\n\t/*escape ES*/ {__, __, __, __, __, __, __, __, ST, ST, ST, __, __, __, __, __, __, ST, __, __, __, ST, __, ST, ST, __, ST, U1, __, __, __},\n\t/*u1 U1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U2, U2, U2, U2, U2, U2, U2, U2, __, __, __, __, __, __, U2, U2, __},\n\t/*u2 U2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U3, U3, U3, U3, U3, U3, U3, U3, __, __, __, __, __, __, U3, U3, __},\n\t/*u3 U3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U4, U4, U4, U4, U4, U4, U4, U4, __, __, __, __, __, __, U4, U4, __},\n\t/*u4 U4*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, ST, ST, ST, ST, ST, ST, ST, ST, __, __, __, __, __, __, ST, ST, __},\n\t/*minus MI*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, ZE, IN, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*zero ZE*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, DT, __, __, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __},\n\t/*int IN*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, DT, IN, IN, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __},\n\t/*dot DT*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, FR, FR, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*frac FR*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, FR, FR, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __},\n\t/*e E1*/ {__, __, __, __, __, __, __, __, __, __, __, E2, E2, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*ex E2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*exp E3*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*tr T1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, T2, __, __, __, __, __, __},\n\t/*tru T2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, T3, __, __, __},\n\t/*true T3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __, __, __},\n\t/*fa F1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F2, __, __, __, __, __, __, __, __, __, __, __, __, __, __},\n\t/*fal F2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F3, __, __, __, __, __, __, __, __},\n\t/*fals F3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F4, __, __, __, __, __},\n\t/*false F4*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __, __, __},\n\t/*nu N1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, N2, __, __, __},\n\t/*nul N2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, N3, __, __, __, __, __, __, __, __},\n\t/*null N3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __},\n}\n" + }, + { + "name": "node.gno", + "body": "package json\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// Node represents a JSON node.\ntype Node struct {\n\tprev *Node // prev is the parent node of the current node.\n\tnext map[string]*Node // next is the child nodes of the current node.\n\tkey *string // key holds the key of the current node in the parent node.\n\tdata []byte // byte slice of JSON data\n\tvalue interface{} // value holds the value of the current node.\n\tnodeType ValueType // NodeType holds the type of the current node. (Object, Array, String, Number, Boolean, Null)\n\tindex *int // index holds the index of the current node in the parent array node.\n\tborders [2]int // borders stores the start and end index of the current node in the data.\n\tmodified bool // modified indicates the current node is changed or not.\n}\n\n// NewNode creates a new node instance with the given parent node, buffer, type, and key.\nfunc NewNode(prev *Node, b *buffer, typ ValueType, key **string) (*Node, error) {\n\tcurr := \u0026Node{\n\t\tprev: prev,\n\t\tdata: b.data,\n\t\tborders: [2]int{b.index, 0},\n\t\tkey: *key,\n\t\tnodeType: typ,\n\t\tmodified: false,\n\t}\n\n\tif typ == Object || typ == Array {\n\t\tcurr.next = make(map[string]*Node)\n\t}\n\n\tif prev != nil {\n\t\tif prev.IsArray() {\n\t\t\tsize := len(prev.next)\n\t\t\tcurr.index = \u0026size\n\n\t\t\tprev.next[strconv.Itoa(size)] = curr\n\t\t} else if prev.IsObject() {\n\t\t\tif key == nil {\n\t\t\t\treturn nil, errors.New(\"key is required for object\")\n\t\t\t}\n\n\t\t\tprev.next[**key] = curr\n\t\t} else {\n\t\t\treturn nil, errors.New(\"invalid parent type\")\n\t\t}\n\t}\n\n\treturn curr, nil\n}\n\n// load retrieves the value of the current node.\nfunc (n *Node) load() interface{} {\n\treturn n.value\n}\n\n// Changed checks the current node is changed or not.\nfunc (n *Node) Changed() bool {\n\treturn n.modified\n}\n\n// Key returns the key of the current node.\nfunc (n *Node) Key() string {\n\tif n == nil || n.key == nil {\n\t\treturn \"\"\n\t}\n\n\treturn *n.key\n}\n\n// HasKey checks the current node has the given key or not.\nfunc (n *Node) HasKey(key string) bool {\n\tif n == nil {\n\t\treturn false\n\t}\n\n\t_, ok := n.next[key]\n\treturn ok\n}\n\n// GetKey returns the value of the given key from the current object node.\nfunc (n *Node) GetKey(key string) (*Node, error) {\n\tif n == nil {\n\t\treturn nil, errors.New(\"node is nil\")\n\t}\n\n\tif n.Type() != Object {\n\t\treturn nil, ufmt.Errorf(\"target node is not object type. got: %s\", n.Type().String())\n\t}\n\n\tvalue, ok := n.next[key]\n\tif !ok {\n\t\treturn nil, ufmt.Errorf(\"key not found: %s\", key)\n\t}\n\n\treturn value, nil\n}\n\n// MustKey returns the value of the given key from the current object node.\nfunc (n *Node) MustKey(key string) *Node {\n\tval, err := n.GetKey(key)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn val\n}\n\n// UniqueKeyLists traverses the current JSON nodes and collects all the unique keys.\nfunc (n *Node) UniqueKeyLists() []string {\n\tvar collectKeys func(*Node) []string\n\tcollectKeys = func(node *Node) []string {\n\t\tif node == nil || !node.IsObject() {\n\t\t\treturn nil\n\t\t}\n\n\t\tresult := make(map[string]bool)\n\t\tfor key, childNode := range node.next {\n\t\t\tresult[key] = true\n\t\t\tchildKeys := collectKeys(childNode)\n\t\t\tfor _, childKey := range childKeys {\n\t\t\t\tresult[childKey] = true\n\t\t\t}\n\t\t}\n\n\t\tkeys := make([]string, 0, len(result))\n\t\tfor key := range result {\n\t\t\tkeys = append(keys, key)\n\t\t}\n\t\treturn keys\n\t}\n\n\treturn collectKeys(n)\n}\n\n// Empty returns true if the current node is empty.\nfunc (n *Node) Empty() bool {\n\tif n == nil {\n\t\treturn false\n\t}\n\n\treturn len(n.next) == 0\n}\n\n// Type returns the type (ValueType) of the current node.\nfunc (n *Node) Type() ValueType {\n\treturn n.nodeType\n}\n\n// Value returns the value of the current node.\n//\n// Usage:\n//\n//\troot := Unmarshal([]byte(`{\"key\": \"value\"}`))\n//\tval, err := root.MustKey(\"key\").Value()\n//\tif err != nil {\n//\t\tt.Errorf(\"Value returns error: %v\", err)\n//\t}\n//\n//\tresult: \"value\"\nfunc (n *Node) Value() (value interface{}, err error) {\n\tvalue = n.load()\n\n\tif value == nil {\n\t\tswitch n.nodeType {\n\t\tcase Null:\n\t\t\treturn nil, nil\n\n\t\tcase Number:\n\t\t\tvalue, err = ParseFloatLiteral(n.source())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tn.value = value\n\n\t\tcase String:\n\t\t\tvar ok bool\n\t\t\tvalue, ok = Unquote(n.source(), doubleQuote)\n\t\t\tif !ok {\n\t\t\t\treturn \"\", errors.New(\"invalid string value\")\n\t\t\t}\n\n\t\t\tn.value = value\n\n\t\tcase Boolean:\n\t\t\tif len(n.source()) == 0 {\n\t\t\t\treturn nil, errors.New(\"empty boolean value\")\n\t\t\t}\n\n\t\t\tb := n.source()[0]\n\t\t\tvalue = b == 't' || b == 'T'\n\t\t\tn.value = value\n\n\t\tcase Array:\n\t\t\telems := make([]*Node, len(n.next))\n\n\t\t\tfor _, e := range n.next {\n\t\t\t\telems[*e.index] = e\n\t\t\t}\n\n\t\t\tvalue = elems\n\t\t\tn.value = value\n\n\t\tcase Object:\n\t\t\tobj := make(map[string]*Node, len(n.next))\n\n\t\t\tfor k, v := range n.next {\n\t\t\t\tobj[k] = v\n\t\t\t}\n\n\t\t\tvalue = obj\n\t\t\tn.value = value\n\t\t}\n\t}\n\n\treturn value, nil\n}\n\n// Delete removes the current node from the parent node.\n//\n// Usage:\n//\n//\troot := Unmarshal([]byte(`{\"key\": \"value\"}`))\n//\tif err := root.MustKey(\"key\").Delete(); err != nil {\n//\t\tt.Errorf(\"Delete returns error: %v\", err)\n//\t}\n//\n//\tresult: {} (empty object)\nfunc (n *Node) Delete() error {\n\tif n == nil {\n\t\treturn errors.New(\"can't delete nil node\")\n\t}\n\n\tif n.prev == nil {\n\t\treturn nil\n\t}\n\n\treturn n.prev.remove(n)\n}\n\n// Size returns the size (length) of the current array node.\n//\n// Usage:\n//\n//\troot := ArrayNode(\"\", []*Node{StringNode(\"\", \"foo\"), NumberNode(\"\", 1)})\n//\tif root == nil {\n//\t\tt.Errorf(\"ArrayNode returns nil\")\n//\t}\n//\n//\tif root.Size() != 2 {\n//\t\tt.Errorf(\"ArrayNode returns wrong size: %d\", root.Size())\n//\t}\nfunc (n *Node) Size() int {\n\tif n == nil {\n\t\treturn 0\n\t}\n\n\treturn len(n.next)\n}\n\n// Index returns the index of the current node in the parent array node.\n//\n// Usage:\n//\n//\troot := ArrayNode(\"\", []*Node{StringNode(\"\", \"foo\"), NumberNode(\"\", 1)})\n//\tif root == nil {\n//\t\tt.Errorf(\"ArrayNode returns nil\")\n//\t}\n//\n//\tif root.MustIndex(1).Index() != 1 {\n//\t\tt.Errorf(\"Index returns wrong index: %d\", root.MustIndex(1).Index())\n//\t}\n//\n// We can also use the index to the byte slice of the JSON data directly.\n//\n// Example:\n//\n//\troot := Unmarshal([]byte(`[\"foo\", 1]`))\n//\tif root == nil {\n//\t\tt.Errorf(\"Unmarshal returns nil\")\n//\t}\n//\n//\tif string(root.MustIndex(1).source()) != \"1\" {\n//\t\tt.Errorf(\"source returns wrong result: %s\", root.MustIndex(1).source())\n//\t}\nfunc (n *Node) Index() int {\n\tif n == nil || n.index == nil {\n\t\treturn -1\n\t}\n\n\treturn *n.index\n}\n\n// MustIndex returns the array element at the given index.\n//\n// If the index is negative, it returns the index is from the end of the array.\n// Also, it panics if the index is not found.\n//\n// check the Index method for detailed usage.\nfunc (n *Node) MustIndex(expectIdx int) *Node {\n\tval, err := n.GetIndex(expectIdx)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn val\n}\n\n// GetIndex returns the array element at the given index.\n//\n// if the index is negative, it returns the index is from the end of the array.\nfunc (n *Node) GetIndex(idx int) (*Node, error) {\n\tif n == nil {\n\t\treturn nil, errors.New(\"node is nil\")\n\t}\n\n\tif !n.IsArray() {\n\t\treturn nil, errors.New(\"node is not array\")\n\t}\n\n\tif idx \u003e n.Size() {\n\t\treturn nil, errors.New(\"input index exceeds the array size\")\n\t}\n\n\tif idx \u003c 0 {\n\t\tidx += len(n.next)\n\t}\n\n\tchild, ok := n.next[strconv.Itoa(idx)]\n\tif !ok {\n\t\treturn nil, errors.New(\"index not found\")\n\t}\n\n\treturn child, nil\n}\n\n// DeleteIndex removes the array element at the given index.\nfunc (n *Node) DeleteIndex(idx int) error {\n\tnode, err := n.GetIndex(idx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn n.remove(node)\n}\n\n// NullNode creates a new null type node.\n//\n// Usage:\n//\n//\t_ := NullNode(\"\")\nfunc NullNode(key string) *Node {\n\treturn \u0026Node{\n\t\tkey: \u0026key,\n\t\tvalue: nil,\n\t\tnodeType: Null,\n\t\tmodified: true,\n\t}\n}\n\n// NumberNode creates a new number type node.\n//\n// Usage:\n//\n//\troot := NumberNode(\"\", 1)\n//\tif root == nil {\n//\t\tt.Errorf(\"NumberNode returns nil\")\n//\t}\nfunc NumberNode(key string, value float64) *Node {\n\treturn \u0026Node{\n\t\tkey: \u0026key,\n\t\tvalue: value,\n\t\tnodeType: Number,\n\t\tmodified: true,\n\t}\n}\n\n// StringNode creates a new string type node.\n//\n// Usage:\n//\n//\troot := StringNode(\"\", \"foo\")\n//\tif root == nil {\n//\t\tt.Errorf(\"StringNode returns nil\")\n//\t}\nfunc StringNode(key string, value string) *Node {\n\treturn \u0026Node{\n\t\tkey: \u0026key,\n\t\tvalue: value,\n\t\tnodeType: String,\n\t\tmodified: true,\n\t}\n}\n\n// BoolNode creates a new given boolean value node.\n//\n// Usage:\n//\n//\troot := BoolNode(\"\", true)\n//\tif root == nil {\n//\t\tt.Errorf(\"BoolNode returns nil\")\n//\t}\nfunc BoolNode(key string, value bool) *Node {\n\treturn \u0026Node{\n\t\tkey: \u0026key,\n\t\tvalue: value,\n\t\tnodeType: Boolean,\n\t\tmodified: true,\n\t}\n}\n\n// ArrayNode creates a new array type node.\n//\n// If the given value is nil, it creates an empty array node.\n//\n// Usage:\n//\n//\troot := ArrayNode(\"\", []*Node{StringNode(\"\", \"foo\"), NumberNode(\"\", 1)})\n//\tif root == nil {\n//\t\tt.Errorf(\"ArrayNode returns nil\")\n//\t}\nfunc ArrayNode(key string, value []*Node) *Node {\n\tcurr := \u0026Node{\n\t\tkey: \u0026key,\n\t\tnodeType: Array,\n\t\tmodified: true,\n\t}\n\n\tcurr.next = make(map[string]*Node, len(value))\n\tif value != nil {\n\t\tcurr.value = value\n\n\t\tfor i, v := range value {\n\t\t\tidx := i\n\t\t\tcurr.next[strconv.Itoa(i)] = v\n\n\t\t\tv.prev = curr\n\t\t\tv.index = \u0026idx\n\t\t}\n\t}\n\n\treturn curr\n}\n\n// ObjectNode creates a new object type node.\n//\n// If the given value is nil, it creates an empty object node.\n//\n// next is a map of key and value pairs of the object.\nfunc ObjectNode(key string, value map[string]*Node) *Node {\n\tcurr := \u0026Node{\n\t\tnodeType: Object,\n\t\tkey: \u0026key,\n\t\tnext: value,\n\t\tmodified: true,\n\t}\n\n\tif value != nil {\n\t\tcurr.value = value\n\n\t\tfor key, val := range value {\n\t\t\tvkey := key\n\t\t\tval.prev = curr\n\t\t\tval.key = \u0026vkey\n\t\t}\n\t} else {\n\t\tcurr.next = make(map[string]*Node)\n\t}\n\n\treturn curr\n}\n\n// IsArray returns true if the current node is array type.\nfunc (n *Node) IsArray() bool {\n\treturn n.nodeType == Array\n}\n\n// IsObject returns true if the current node is object type.\nfunc (n *Node) IsObject() bool {\n\treturn n.nodeType == Object\n}\n\n// IsNull returns true if the current node is null type.\nfunc (n *Node) IsNull() bool {\n\treturn n.nodeType == Null\n}\n\n// IsBool returns true if the current node is boolean type.\nfunc (n *Node) IsBool() bool {\n\treturn n.nodeType == Boolean\n}\n\n// IsString returns true if the current node is string type.\nfunc (n *Node) IsString() bool {\n\treturn n.nodeType == String\n}\n\n// IsNumber returns true if the current node is number type.\nfunc (n *Node) IsNumber() bool {\n\treturn n.nodeType == Number\n}\n\n// ready checks the current node is ready or not.\n//\n// the meaning of ready is the current node is parsed and has a valid value.\nfunc (n *Node) ready() bool {\n\treturn n.borders[1] != 0\n}\n\n// source returns the source of the current node.\nfunc (n *Node) source() []byte {\n\tif n == nil {\n\t\treturn nil\n\t}\n\n\tif n.ready() \u0026\u0026 !n.modified \u0026\u0026 n.data != nil {\n\t\treturn (n.data)[n.borders[0]:n.borders[1]]\n\t}\n\n\treturn nil\n}\n\n// root returns the root node of the current node.\nfunc (n *Node) root() *Node {\n\tif n == nil {\n\t\treturn nil\n\t}\n\n\tcurr := n\n\tfor curr.prev != nil {\n\t\tcurr = curr.prev\n\t}\n\n\treturn curr\n}\n\n// GetNull returns the null value if current node is null type.\n//\n// Usage:\n//\n//\troot := Unmarshal([]byte(\"null\"))\n//\tval, err := root.GetNull()\n//\tif err != nil {\n//\t\tt.Errorf(\"GetNull returns error: %v\", err)\n//\t}\n//\tif val != nil {\n//\t\tt.Errorf(\"GetNull returns wrong result: %v\", val)\n//\t}\nfunc (n *Node) GetNull() (interface{}, error) {\n\tif n == nil {\n\t\treturn nil, errors.New(\"node is nil\")\n\t}\n\n\tif !n.IsNull() {\n\t\treturn nil, errors.New(\"node is not null\")\n\t}\n\n\treturn nil, nil\n}\n\n// MustNull returns the null value if current node is null type.\n//\n// It panics if the current node is not null type.\nfunc (n *Node) MustNull() interface{} {\n\tv, err := n.GetNull()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn v\n}\n\n// GetNumeric returns the numeric (int/float) value if current node is number type.\n//\n// Usage:\n//\n//\troot := Unmarshal([]byte(\"10.5\"))\n//\tval, err := root.GetNumeric()\n//\tif err != nil {\n//\t\tt.Errorf(\"GetNumeric returns error: %v\", err)\n//\t}\n//\tprintln(val) // 10.5\nfunc (n *Node) GetNumeric() (float64, error) {\n\tif n == nil {\n\t\treturn 0, errors.New(\"node is nil\")\n\t}\n\n\tif n.nodeType != Number {\n\t\treturn 0, errors.New(\"node is not number\")\n\t}\n\n\tval, err := n.Value()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tv, ok := val.(float64)\n\tif !ok {\n\t\treturn 0, errors.New(\"node is not number\")\n\t}\n\n\treturn v, nil\n}\n\n// MustNumeric returns the numeric (int/float) value if current node is number type.\n//\n// It panics if the current node is not number type.\nfunc (n *Node) MustNumeric() float64 {\n\tv, err := n.GetNumeric()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn v\n}\n\n// GetString returns the string value if current node is string type.\n//\n// Usage:\n//\n//\troot, err := Unmarshal([]byte(\"foo\"))\n//\tif err != nil {\n//\t\tt.Errorf(\"Error on Unmarshal(): %s\", err)\n//\t}\n//\n//\tstr, err := root.GetString()\n//\tif err != nil {\n//\t\tt.Errorf(\"should retrieve string value: %s\", err)\n//\t}\n//\n//\tprintln(str) // \"foo\"\nfunc (n *Node) GetString() (string, error) {\n\tif n == nil {\n\t\treturn \"\", errors.New(\"string node is empty\")\n\t}\n\n\tif !n.IsString() {\n\t\treturn \"\", errors.New(\"node type is not string\")\n\t}\n\n\tval, err := n.Value()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tv, ok := val.(string)\n\tif !ok {\n\t\treturn \"\", errors.New(\"node is not string\")\n\t}\n\n\treturn v, nil\n}\n\n// MustString returns the string value if current node is string type.\n//\n// It panics if the current node is not string type.\nfunc (n *Node) MustString() string {\n\tv, err := n.GetString()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn v\n}\n\n// GetBool returns the boolean value if current node is boolean type.\n//\n// Usage:\n//\n//\troot := Unmarshal([]byte(\"true\"))\n//\tval, err := root.GetBool()\n//\tif err != nil {\n//\t\tt.Errorf(\"GetBool returns error: %v\", err)\n//\t}\n//\tprintln(val) // true\nfunc (n *Node) GetBool() (bool, error) {\n\tif n == nil {\n\t\treturn false, errors.New(\"node is nil\")\n\t}\n\n\tif n.nodeType != Boolean {\n\t\treturn false, errors.New(\"node is not boolean\")\n\t}\n\n\tval, err := n.Value()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tv, ok := val.(bool)\n\tif !ok {\n\t\treturn false, errors.New(\"node is not boolean\")\n\t}\n\n\treturn v, nil\n}\n\n// MustBool returns the boolean value if current node is boolean type.\n//\n// It panics if the current node is not boolean type.\nfunc (n *Node) MustBool() bool {\n\tv, err := n.GetBool()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn v\n}\n\n// GetArray returns the array value if current node is array type.\n//\n// Usage:\n//\n//\t\troot := Must(Unmarshal([]byte(`[\"foo\", 1]`)))\n//\t\tarr, err := root.GetArray()\n//\t\tif err != nil {\n//\t\t\tt.Errorf(\"GetArray returns error: %v\", err)\n//\t\t}\n//\n//\t\tfor _, val := range arr {\n//\t\t\tprintln(val)\n//\t\t}\n//\n//\t result: \"foo\", 1\nfunc (n *Node) GetArray() ([]*Node, error) {\n\tif n == nil {\n\t\treturn nil, errors.New(\"node is nil\")\n\t}\n\n\tif n.nodeType != Array {\n\t\treturn nil, errors.New(\"node is not array\")\n\t}\n\n\tval, err := n.Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv, ok := val.([]*Node)\n\tif !ok {\n\t\treturn nil, errors.New(\"node is not array\")\n\t}\n\n\treturn v, nil\n}\n\n// MustArray returns the array value if current node is array type.\n//\n// It panics if the current node is not array type.\nfunc (n *Node) MustArray() []*Node {\n\tv, err := n.GetArray()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn v\n}\n\n// AppendArray appends the given values to the current array node.\n//\n// If the current node is not array type, it returns an error.\n//\n// Example 1:\n//\n//\troot := Must(Unmarshal([]byte(`[{\"foo\":\"bar\"}]`)))\n//\tif err := root.AppendArray(NullNode(\"\")); err != nil {\n//\t\tt.Errorf(\"should not return error: %s\", err)\n//\t}\n//\n//\tresult: [{\"foo\":\"bar\"}, null]\n//\n// Example 2:\n//\n//\troot := Must(Unmarshal([]byte(`[\"bar\", \"baz\"]`)))\n//\terr := root.AppendArray(NumberNode(\"\", 1), StringNode(\"\", \"foo\"))\n//\tif err != nil {\n//\t\tt.Errorf(\"AppendArray returns error: %v\", err)\n//\t }\n//\n//\tresult: [\"bar\", \"baz\", 1, \"foo\"]\nfunc (n *Node) AppendArray(value ...*Node) error {\n\tif !n.IsArray() {\n\t\treturn errors.New(\"can't append value to non-array node\")\n\t}\n\n\tfor _, val := range value {\n\t\tif err := n.append(nil, val); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tn.mark()\n\treturn nil\n}\n\n// ArrayEach executes the callback for each element in the JSON array.\n//\n// Usage:\n//\n//\tjsonArrayNode.ArrayEach(func(i int, valueNode *Node) {\n//\t ufmt.Println(i, valueNode)\n//\t})\nfunc (n *Node) ArrayEach(callback func(i int, target *Node)) {\n\tif n == nil || !n.IsArray() {\n\t\treturn\n\t}\n\n\tfor idx := 0; idx \u003c len(n.next); idx++ {\n\t\telement, err := n.GetIndex(idx)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tcallback(idx, element)\n\t}\n}\n\n// GetObject returns the object value if current node is object type.\n//\n// Usage:\n//\n//\troot := Must(Unmarshal([]byte(`{\"key\": \"value\"}`)))\n//\tobj, err := root.GetObject()\n//\tif err != nil {\n//\t\tt.Errorf(\"GetObject returns error: %v\", err)\n//\t}\n//\n//\tresult: map[string]*Node{\"key\": StringNode(\"key\", \"value\")}\nfunc (n *Node) GetObject() (map[string]*Node, error) {\n\tif n == nil {\n\t\treturn nil, errors.New(\"node is nil\")\n\t}\n\n\tif !n.IsObject() {\n\t\treturn nil, errors.New(\"node is not object\")\n\t}\n\n\tval, err := n.Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv, ok := val.(map[string]*Node)\n\tif !ok {\n\t\treturn nil, errors.New(\"node is not object\")\n\t}\n\n\treturn v, nil\n}\n\n// MustObject returns the object value if current node is object type.\n//\n// It panics if the current node is not object type.\nfunc (n *Node) MustObject() map[string]*Node {\n\tv, err := n.GetObject()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn v\n}\n\n// AppendObject appends the given key and value to the current object node.\n//\n// If the current node is not object type, it returns an error.\nfunc (n *Node) AppendObject(key string, value *Node) error {\n\tif !n.IsObject() {\n\t\treturn errors.New(\"can't append value to non-object node\")\n\t}\n\n\tif err := n.append(\u0026key, value); err != nil {\n\t\treturn err\n\t}\n\n\tn.mark()\n\treturn nil\n}\n\n// ObjectEach executes the callback for each key-value pair in the JSON object.\n//\n// Usage:\n//\n//\tjsonObjectNode.ObjectEach(func(key string, valueNode *Node) {\n//\t ufmt.Println(key, valueNode)\n//\t})\nfunc (n *Node) ObjectEach(callback func(key string, value *Node)) {\n\tif n == nil || !n.IsObject() {\n\t\treturn\n\t}\n\n\tfor key, child := range n.next {\n\t\tcallback(key, child)\n\t}\n}\n\n// String converts the node to a string representation.\nfunc (n *Node) String() string {\n\tif n == nil {\n\t\treturn \"\"\n\t}\n\n\tif n.ready() \u0026\u0026 !n.modified {\n\t\treturn string(n.source())\n\t}\n\n\tval, err := Marshal(n)\n\tif err != nil {\n\t\treturn \"error: \" + err.Error()\n\t}\n\n\treturn string(val)\n}\n\n// Path builds the path of the current node.\n//\n// For example:\n//\n//\t{ \"key\": { \"sub\": [ \"val1\", \"val2\" ] }}\n//\n// The path of \"val2\" is: $.key.sub[1]\nfunc (n *Node) Path() string {\n\tif n == nil {\n\t\treturn \"\"\n\t}\n\n\tvar sb strings.Builder\n\n\tif n.prev == nil {\n\t\tsb.WriteString(\"$\")\n\t} else {\n\t\tsb.WriteString(n.prev.Path())\n\n\t\tif n.key != nil {\n\t\t\tsb.WriteString(\"['\" + n.Key() + \"']\")\n\t\t} else {\n\t\t\tsb.WriteString(\"[\" + strconv.Itoa(n.Index()) + \"]\")\n\t\t}\n\t}\n\n\treturn sb.String()\n}\n\n// mark marks the current node as modified.\nfunc (n *Node) mark() {\n\tnode := n\n\tfor node != nil \u0026\u0026 !node.modified {\n\t\tnode.modified = true\n\t\tnode = node.prev\n\t}\n}\n\n// isContainer checks the current node type is array or object.\nfunc (n *Node) isContainer() bool {\n\treturn n.IsArray() || n.IsObject()\n}\n\n// remove removes the value from the current container type node.\nfunc (n *Node) remove(v *Node) error {\n\tif !n.isContainer() {\n\t\treturn ufmt.Errorf(\n\t\t\t\"can't remove value from non-array or non-object node. got=%s\",\n\t\t\tn.Type().String(),\n\t\t)\n\t}\n\n\tif v.prev != n {\n\t\treturn errors.New(\"invalid parent node\")\n\t}\n\n\tn.mark()\n\tif n.IsArray() {\n\t\tdelete(n.next, strconv.Itoa(*v.index))\n\t\tn.dropIndex(*v.index)\n\t} else {\n\t\tdelete(n.next, *v.key)\n\t}\n\n\tv.prev = nil\n\treturn nil\n}\n\n// dropIndex rebase the index of current array node values.\nfunc (n *Node) dropIndex(idx int) {\n\tfor i := idx + 1; i \u003c= len(n.next); i++ {\n\t\tprv := i - 1\n\t\tif curr, ok := n.next[strconv.Itoa(i)]; ok {\n\t\t\tcurr.index = \u0026prv\n\t\t\tn.next[strconv.Itoa(prv)] = curr\n\t\t}\n\n\t\tdelete(n.next, strconv.Itoa(i))\n\t}\n}\n\n// append is a helper function to append the given value to the current container type node.\nfunc (n *Node) append(key *string, val *Node) error {\n\tif n.isSameOrParentNode(val) {\n\t\treturn errors.New(\"can't append same or parent node\")\n\t}\n\n\tif val.prev != nil {\n\t\tif err := val.prev.remove(val); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tval.prev = n\n\tval.key = key\n\n\tif key == nil {\n\t\tsize := len(n.next)\n\t\tval.index = \u0026size\n\t\tn.next[strconv.Itoa(size)] = val\n\t} else {\n\t\tif old, ok := n.next[*key]; ok {\n\t\t\tif err := n.remove(old); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tn.next[*key] = val\n\t}\n\n\treturn nil\n}\n\nfunc (n *Node) isSameOrParentNode(nd *Node) bool {\n\treturn n == nd || n.isParentNode(nd)\n}\n\nfunc (n *Node) isParentNode(nd *Node) bool {\n\tif n == nil {\n\t\treturn false\n\t}\n\n\tfor curr := nd.prev; curr != nil; curr = curr.prev {\n\t\tif curr == n {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// cptrs returns the pointer of the given string value.\nfunc cptrs(cpy *string) *string {\n\tif cpy == nil {\n\t\treturn nil\n\t}\n\n\tval := *cpy\n\n\treturn \u0026val\n}\n\n// cptri returns the pointer of the given integer value.\nfunc cptri(i *int) *int {\n\tif i == nil {\n\t\treturn nil\n\t}\n\n\tval := *i\n\treturn \u0026val\n}\n\n// Must panics if the given node is not fulfilled the expectation.\n// Usage:\n//\n//\tnode := Must(Unmarshal([]byte(`{\"key\": \"value\"}`))\nfunc Must(root *Node, expect error) *Node {\n\tif expect != nil {\n\t\tpanic(expect)\n\t}\n\n\treturn root\n}\n" + }, + { + "name": "node_test.gno", + "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar (\n\tnilKey *string\n\tdummyKey = \"key\"\n)\n\ntype _args struct {\n\tprev *Node\n\tbuf *buffer\n\ttyp ValueType\n\tkey **string\n}\n\ntype simpleNode struct {\n\tname string\n\tnode *Node\n}\n\nfunc TestNode_CreateNewNode(t *testing.T) {\n\trel := \u0026dummyKey\n\n\ttests := []struct {\n\t\tname string\n\t\targs _args\n\t\texpectCurr *Node\n\t\texpectErr bool\n\t\texpectPanic bool\n\t}{\n\t\t{\n\t\t\tname: \"child for non container type\",\n\t\t\targs: _args{\n\t\t\t\tprev: BoolNode(\"\", true),\n\t\t\t\tbuf: newBuffer(make([]byte, 10)),\n\t\t\t\ttyp: Boolean,\n\t\t\t\tkey: \u0026rel,\n\t\t\t},\n\t\t\texpectCurr: nil,\n\t\t\texpectErr: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tif tt.expectPanic {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tt.Errorf(\"%s panic occurred when not expected: %v\", tt.name, r)\n\t\t\t\t} else if tt.expectPanic {\n\t\t\t\t\tt.Errorf(\"%s expected panic but didn't occur\", tt.name)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tgot, err := NewNode(tt.args.prev, tt.args.buf, tt.args.typ, tt.args.key)\n\t\t\tif (err != nil) != tt.expectErr {\n\t\t\t\tt.Errorf(\"%s error = %v, expect error %v\", tt.name, err, tt.expectErr)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif tt.expectErr {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !compareNodes(got, tt.expectCurr) {\n\t\t\t\tt.Errorf(\"%s got = %v, want %v\", tt.name, got, tt.expectCurr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_Value(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tdata []byte\n\t\t_type ValueType\n\t\texpected interface{}\n\t\terrExpected bool\n\t}{\n\t\t{name: \"null\", data: []byte(\"null\"), _type: Null, expected: nil},\n\t\t{name: \"1\", data: []byte(\"1\"), _type: Number, expected: float64(1)},\n\t\t{name: \".1\", data: []byte(\".1\"), _type: Number, expected: float64(.1)},\n\t\t{name: \"-.1e1\", data: []byte(\"-.1e1\"), _type: Number, expected: float64(-1)},\n\t\t{name: \"string\", data: []byte(\"\\\"foo\\\"\"), _type: String, expected: \"foo\"},\n\t\t{name: \"space\", data: []byte(\"\\\"foo bar\\\"\"), _type: String, expected: \"foo bar\"},\n\t\t{name: \"true\", data: []byte(\"true\"), _type: Boolean, expected: true},\n\t\t{name: \"invalid true\", data: []byte(\"tru\"), _type: Unknown, errExpected: true},\n\t\t{name: \"invalid false\", data: []byte(\"fals\"), _type: Unknown, errExpected: true},\n\t\t{name: \"false\", data: []byte(\"false\"), _type: Boolean, expected: false},\n\t\t{name: \"e1\", data: []byte(\"e1\"), _type: Unknown, errExpected: true},\n\t\t{name: \"1a\", data: []byte(\"1a\"), _type: Unknown, errExpected: true},\n\t\t{name: \"string error\", data: []byte(\"\\\"foo\\nbar\\\"\"), _type: String, errExpected: true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcurr := \u0026Node{\n\t\t\t\tdata: tt.data,\n\t\t\t\tnodeType: tt._type,\n\t\t\t\tborders: [2]int{0, len(tt.data)},\n\t\t\t}\n\n\t\t\tgot, err := curr.Value()\n\t\t\tif err != nil {\n\t\t\t\tif !tt.errExpected {\n\t\t\t\t\tt.Errorf(\"%s error = %v, expect error %v\", tt.name, err, tt.errExpected)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"%s got = %v, want %v\", tt.name, got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_Delete(t *testing.T) {\n\troot := Must(Unmarshal([]byte(`{\"foo\":\"bar\"}`)))\n\tif err := root.Delete(); err != nil {\n\t\tt.Errorf(\"Delete returns error: %v\", err)\n\t}\n\n\tif value, err := Marshal(root); err != nil {\n\t\tt.Errorf(\"Marshal returns error: %v\", err)\n\t} else if string(value) != `{\"foo\":\"bar\"}` {\n\t\tt.Errorf(\"Marshal returns wrong value: %s\", string(value))\n\t}\n\n\tfoo := root.MustKey(\"foo\")\n\tif err := foo.Delete(); err != nil {\n\t\tt.Errorf(\"Delete returns error while handling foo: %v\", err)\n\t}\n\n\tif value, err := Marshal(root); err != nil {\n\t\tt.Errorf(\"Marshal returns error: %v\", err)\n\t} else if string(value) != `{}` {\n\t\tt.Errorf(\"Marshal returns wrong value: %s\", string(value))\n\t}\n\n\tif value, err := Marshal(foo); err != nil {\n\t\tt.Errorf(\"Marshal returns error: %v\", err)\n\t} else if string(value) != `\"bar\"` {\n\t\tt.Errorf(\"Marshal returns wrong value: %s\", string(value))\n\t}\n\n\tif foo.prev != nil {\n\t\tt.Errorf(\"foo.prev should be nil\")\n\t}\n}\n\nfunc TestNode_ObjectNode(t *testing.T) {\n\tobjs := map[string]*Node{\n\t\t\"key1\": NullNode(\"null\"),\n\t\t\"key2\": NumberNode(\"answer\", 42),\n\t\t\"key3\": StringNode(\"string\", \"foobar\"),\n\t\t\"key4\": BoolNode(\"bool\", true),\n\t}\n\n\tnode := ObjectNode(\"test\", objs)\n\n\tif len(node.next) != len(objs) {\n\t\tt.Errorf(\"ObjectNode: want %v got %v\", len(objs), len(node.next))\n\t}\n\n\tfor k, v := range objs {\n\t\tif node.next[k] == nil {\n\t\t\tt.Errorf(\"ObjectNode: want %v got %v\", v, node.next[k])\n\t\t}\n\t}\n}\n\nfunc TestNode_AppendObject(t *testing.T) {\n\tif err := Must(Unmarshal([]byte(`{\"foo\":\"bar\",\"baz\":null}`))).AppendObject(\"biz\", NullNode(\"\")); err != nil {\n\t\tt.Errorf(\"AppendArray should return error\")\n\t}\n\n\troot := Must(Unmarshal([]byte(`{\"foo\":\"bar\"}`)))\n\tif err := root.AppendObject(\"baz\", NullNode(\"\")); err != nil {\n\t\tt.Errorf(\"AppendObject should not return error: %s\", err)\n\t}\n\n\tif value, err := Marshal(root); err != nil {\n\t\tt.Errorf(\"Marshal returns error: %v\", err)\n\t} else if isSameObject(string(value), `\"{\"foo\":\"bar\",\"baz\":null}\"`) {\n\t\tt.Errorf(\"Marshal returns wrong value: %s\", string(value))\n\t}\n\n\t// FIXME: this may fail if execute test in more than 3 times in a row.\n\tif err := root.AppendObject(\"biz\", NumberNode(\"\", 42)); err != nil {\n\t\tt.Errorf(\"AppendObject returns error: %v\", err)\n\t}\n\n\tval, err := Marshal(root)\n\tif err != nil {\n\t\tt.Errorf(\"Marshal returns error: %v\", err)\n\t}\n\n\t// FIXME: this may fail if execute test in more than 3 times in a row.\n\tif isSameObject(string(val), `\"{\"foo\":\"bar\",\"baz\":null,\"biz\":42}\"`) {\n\t\tt.Errorf(\"Marshal returns wrong value: %s\", string(val))\n\t}\n}\n\nfunc TestNode_ArrayNode(t *testing.T) {\n\tarr := []*Node{\n\t\tNullNode(\"nil\"),\n\t\tNumberNode(\"num\", 42),\n\t\tStringNode(\"str\", \"foobar\"),\n\t\tBoolNode(\"bool\", true),\n\t}\n\n\tnode := ArrayNode(\"test\", arr)\n\n\tif len(node.next) != len(arr) {\n\t\tt.Errorf(\"ArrayNode: want %v got %v\", len(arr), len(node.next))\n\t}\n\n\tfor i, v := range arr {\n\t\tif node.next[strconv.Itoa(i)] == nil {\n\t\t\tt.Errorf(\"ArrayNode: want %v got %v\", v, node.next[strconv.Itoa(i)])\n\t\t}\n\t}\n}\n\nfunc TestNode_AppendArray(t *testing.T) {\n\tif err := Must(Unmarshal([]byte(`[{\"foo\":\"bar\"}]`))).AppendArray(NullNode(\"\")); err != nil {\n\t\tt.Errorf(\"should return error\")\n\t}\n\n\troot := Must(Unmarshal([]byte(`[{\"foo\":\"bar\"}]`)))\n\tif err := root.AppendArray(NullNode(\"\")); err != nil {\n\t\tt.Errorf(\"should not return error: %s\", err)\n\t}\n\n\tif value, err := Marshal(root); err != nil {\n\t\tt.Errorf(\"Marshal returns error: %v\", err)\n\t} else if string(value) != `[{\"foo\":\"bar\"},null]` {\n\t\tt.Errorf(\"Marshal returns wrong value: %s\", string(value))\n\t}\n\n\tif err := root.AppendArray(\n\t\tNumberNode(\"\", 1),\n\t\tStringNode(\"\", \"foo\"),\n\t\tMust(Unmarshal([]byte(`[0,1,null,true,\"example\"]`))),\n\t\tMust(Unmarshal([]byte(`{\"foo\": true, \"bar\": null, \"baz\": 123}`))),\n\t); err != nil {\n\t\tt.Errorf(\"AppendArray returns error: %v\", err)\n\t}\n\n\tif value, err := Marshal(root); err != nil {\n\t\tt.Errorf(\"Marshal returns error: %v\", err)\n\t} else if string(value) != `[{\"foo\":\"bar\"},null,1,\"foo\",[0,1,null,true,\"example\"],{\"foo\": true, \"bar\": null, \"baz\": 123}]` {\n\t\tt.Errorf(\"Marshal returns wrong value: %s\", string(value))\n\t}\n}\n\n/******** value getter ********/\n\nfunc TestNode_GetBool(t *testing.T) {\n\troot, err := Unmarshal([]byte(`true`))\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err.Error())\n\t\treturn\n\t}\n\n\tvalue, err := root.GetBool()\n\tif err != nil {\n\t\tt.Errorf(\"Error on root.GetBool(): %s\", err.Error())\n\t}\n\n\tif !value {\n\t\tt.Errorf(\"root.GetBool() is corrupted\")\n\t}\n}\n\nfunc TestNode_GetBool_Fail(t *testing.T) {\n\ttests := []simpleNode{\n\t\t{\"nil node\", (*Node)(nil)},\n\t\t{\"literally null node\", NullNode(\"\")},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, err := tt.node.GetBool(); err == nil {\n\t\t\t\tt.Errorf(\"%s should be an error\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_IsBool(t *testing.T) {\n\ttests := []simpleNode{\n\t\t{\"true\", BoolNode(\"\", true)},\n\t\t{\"false\", BoolNode(\"\", false)},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif !tt.node.IsBool() {\n\t\t\t\tt.Errorf(\"%s should be a bool\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_IsBool_With_Unmarshal(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tjson []byte\n\t\twant bool\n\t}{\n\t\t{\"true\", []byte(\"true\"), true},\n\t\t{\"false\", []byte(\"false\"), true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\troot, err := Unmarshal(tt.json)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Error on Unmarshal(): %s\", err.Error())\n\t\t\t}\n\n\t\t\tif root.IsBool() != tt.want {\n\t\t\t\tt.Errorf(\"%s should be a bool\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar nullJson = []byte(`null`)\n\nfunc TestNode_GetNull(t *testing.T) {\n\troot, err := Unmarshal(nullJson)\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err.Error())\n\t}\n\n\tvalue, err := root.GetNull()\n\tif err != nil {\n\t\tt.Errorf(\"error occurred while getting null, %s\", err)\n\t}\n\n\tif value != nil {\n\t\tt.Errorf(\"value is not matched. expected: nil, got: %v\", value)\n\t}\n}\n\nfunc TestNode_GetNull_Fail(t *testing.T) {\n\ttests := []simpleNode{\n\t\t{\"nil node\", (*Node)(nil)},\n\t\t{\"number node is null\", NumberNode(\"\", 42)},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, err := tt.node.GetNull(); err == nil {\n\t\t\t\tt.Errorf(\"%s should be an error\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_MustNull(t *testing.T) {\n\troot, err := Unmarshal(nullJson)\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err.Error())\n\t}\n\n\tvalue := root.MustNull()\n\tif value != nil {\n\t\tt.Errorf(\"value is not matched. expected: nil, got: %v\", value)\n\t}\n}\n\nfunc TestNode_GetNumeric_Float(t *testing.T) {\n\troot, err := Unmarshal([]byte(`123.456`))\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err)\n\t\treturn\n\t}\n\n\tvalue, err := root.GetNumeric()\n\tif err != nil {\n\t\tt.Errorf(\"Error on root.GetNumeric(): %s\", err)\n\t}\n\n\tif value != float64(123.456) {\n\t\tt.Errorf(ufmt.Sprintf(\"value is not matched. expected: 123.456, got: %v\", value))\n\t}\n}\n\nfunc TestNode_GetNumeric_Scientific_Notation(t *testing.T) {\n\troot, err := Unmarshal([]byte(`1e3`))\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err)\n\t\treturn\n\t}\n\n\tvalue, err := root.GetNumeric()\n\tif err != nil {\n\t\tt.Errorf(\"Error on root.GetNumeric(): %s\", err)\n\t}\n\n\tif value != float64(1000) {\n\t\tt.Errorf(ufmt.Sprintf(\"value is not matched. expected: 1000, got: %v\", value))\n\t}\n}\n\nfunc TestNode_GetNumeric_With_Unmarshal(t *testing.T) {\n\troot, err := Unmarshal([]byte(`123`))\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err)\n\t\treturn\n\t}\n\n\tvalue, err := root.GetNumeric()\n\tif err != nil {\n\t\tt.Errorf(\"Error on root.GetNumeric(): %s\", err)\n\t}\n\n\tif value != float64(123) {\n\t\tt.Errorf(ufmt.Sprintf(\"value is not matched. expected: 123, got: %v\", value))\n\t}\n}\n\nfunc TestNode_GetNumeric_Fail(t *testing.T) {\n\ttests := []simpleNode{\n\t\t{\"nil node\", (*Node)(nil)},\n\t\t{\"null node\", NullNode(\"\")},\n\t\t{\"string node\", StringNode(\"\", \"123\")},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, err := tt.node.GetNumeric(); err == nil {\n\t\t\t\tt.Errorf(\"%s should be an error\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_GetString(t *testing.T) {\n\troot, err := Unmarshal([]byte(`\"123foobar 3456\"`))\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err)\n\t}\n\n\tvalue, err := root.GetString()\n\tif err != nil {\n\t\tt.Errorf(\"Error on root.GetString(): %s\", err)\n\t}\n\n\tif value != \"123foobar 3456\" {\n\t\tt.Errorf(ufmt.Sprintf(\"value is not matched. expected: 123, got: %s\", value))\n\t}\n}\n\nfunc TestNode_GetString_Fail(t *testing.T) {\n\ttests := []simpleNode{\n\t\t{\"nil node\", (*Node)(nil)},\n\t\t{\"null node\", NullNode(\"\")},\n\t\t{\"number node\", NumberNode(\"\", 123)},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, err := tt.node.GetString(); err == nil {\n\t\t\t\tt.Errorf(\"%s should be an error\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_MustString(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tdata []byte\n\t}{\n\t\t{\"foo\", []byte(`\"foo\"`)},\n\t\t{\"foo bar\", []byte(`\"foo bar\"`)},\n\t\t{\"\", []byte(`\"\"`)},\n\t\t{\"안녕하세요\", []byte(`\"안녕하세요\"`)},\n\t\t{\"こんにちは\", []byte(`\"こんにちは\"`)},\n\t\t{\"你好\", []byte(`\"你好\"`)},\n\t\t{\"one \\\"encoded\\\" string\", []byte(`\"one \\\"encoded\\\" string\"`)},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\troot, err := Unmarshal(tt.data)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Error on Unmarshal(): %s\", err)\n\t\t\t}\n\n\t\t\tvalue := root.MustString()\n\t\t\tif value != tt.name {\n\t\t\t\tt.Errorf(\"value is not matched. expected: %s, got: %s\", tt.name, value)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal_Array(t *testing.T) {\n\troot, err := Unmarshal([]byte(\" [1,[\\\"1\\\",[1,[1,2,3]]]]\\r\\n\"))\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal: %s\", err.Error())\n\t}\n\n\tif root == nil {\n\t\tt.Errorf(\"Error on Unmarshal: root is nil\")\n\t}\n\n\tif root.Type() != Array {\n\t\tt.Errorf(\"Error on Unmarshal: wrong type\")\n\t}\n\n\tarray, err := root.GetArray()\n\tif err != nil {\n\t\tt.Errorf(\"error occurred while getting array, %s\", err)\n\t} else if len(array) != 2 {\n\t\tt.Errorf(\"expected 2 elements, got %d\", len(array))\n\t} else if val, err := array[0].GetNumeric(); err != nil {\n\t\tt.Errorf(\"value of array[0] is not numeric. got: %v\", array[0].value)\n\t} else if val != 1 {\n\t\tt.Errorf(\"Error on array[0].GetNumeric(): expected to be '1', got: %v\", val)\n\t} else if val, err := array[1].GetArray(); err != nil {\n\t\tt.Errorf(\"error occurred while getting array, %s\", err.Error())\n\t} else if len(val) != 2 {\n\t\tt.Errorf(\"Error on array[1].GetArray(): expected 2 elements, got %d\", len(val))\n\t} else if el, err := val[0].GetString(); err != nil {\n\t\tt.Errorf(\"error occurred while getting string, %s\", err.Error())\n\t} else if el != \"1\" {\n\t\tt.Errorf(\"Error on val[0].GetString(): expected to be '1', got: %s\", el)\n\t}\n}\n\nvar sampleArr = []byte(`[-1, 2, 3, 4, 5, 6]`)\n\nfunc TestNode_GetArray(t *testing.T) {\n\troot, err := Unmarshal(sampleArr)\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err)\n\t\treturn\n\t}\n\n\tarray, err := root.GetArray()\n\tif err != nil {\n\t\tt.Errorf(\"Error on root.GetArray(): %s\", err)\n\t}\n\n\tif len(array) != 6 {\n\t\tt.Errorf(ufmt.Sprintf(\"length is not matched. expected: 3, got: %d\", len(array)))\n\t}\n\n\tfor i, node := range array {\n\t\tfor j, val := range []int{-1, 2, 3, 4, 5, 6} {\n\t\t\tif i == j {\n\t\t\t\tif v, err := node.GetNumeric(); err != nil {\n\t\t\t\t\tt.Errorf(ufmt.Sprintf(\"Error on node.GetNumeric(): %s\", err))\n\t\t\t\t} else if v != float64(val) {\n\t\t\t\t\tt.Errorf(ufmt.Sprintf(\"value is not matched. expected: %d, got: %v\", val, v))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestNode_GetArray_Fail(t *testing.T) {\n\ttests := []simpleNode{\n\t\t{\"nil node\", (*Node)(nil)},\n\t\t{\"null node\", NullNode(\"\")},\n\t\t{\"number node\", NumberNode(\"\", 123)},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, err := tt.node.GetArray(); err == nil {\n\t\t\t\tt.Errorf(\"%s should be an error\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_IsArray(t *testing.T) {\n\troot, err := Unmarshal(sampleArr)\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err)\n\t\treturn\n\t}\n\n\tif root.Type() != Array {\n\t\tt.Errorf(ufmt.Sprintf(\"Must be an array. got: %s\", root.Type().String()))\n\t}\n}\n\nfunc TestNode_ArrayEach(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tjson string\n\t\texpected []int\n\t}{\n\t\t{\n\t\t\tname: \"empty array\",\n\t\t\tjson: `[]`,\n\t\t\texpected: []int{},\n\t\t},\n\t\t{\n\t\t\tname: \"single element\",\n\t\t\tjson: `[42]`,\n\t\t\texpected: []int{42},\n\t\t},\n\t\t{\n\t\t\tname: \"multiple elements\",\n\t\t\tjson: `[1, 2, 3, 4, 5]`,\n\t\t\texpected: []int{1, 2, 3, 4, 5},\n\t\t},\n\t\t{\n\t\t\tname: \"multiple elements but all values are same\",\n\t\t\tjson: `[1, 1, 1, 1, 1]`,\n\t\t\texpected: []int{1, 1, 1, 1, 1},\n\t\t},\n\t\t{\n\t\t\tname: \"multiple elements with non-numeric values\",\n\t\t\tjson: `[\"a\", \"b\", \"c\", \"d\", \"e\"]`,\n\t\t\texpected: []int{},\n\t\t},\n\t\t{\n\t\t\tname: \"non-array node\",\n\t\t\tjson: `{\"not\": \"an array\"}`,\n\t\t\texpected: []int{},\n\t\t},\n\t\t{\n\t\t\tname: \"array containing numeric and non-numeric elements\",\n\t\t\tjson: `[\"1\", 2, 3, \"4\", 5, \"6\"]`,\n\t\t\texpected: []int{2, 3, 5},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\troot, err := Unmarshal([]byte(tc.json))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Unmarshal failed: %v\", err)\n\t\t\t}\n\n\t\t\tvar result []int // callback result\n\t\t\troot.ArrayEach(func(index int, element *Node) {\n\t\t\t\tif val, err := strconv.Atoi(element.String()); err == nil {\n\t\t\t\t\tresult = append(result, val)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tif len(result) != len(tc.expected) {\n\t\t\t\tt.Errorf(\"%s: expected %d elements, got %d\", tc.name, len(tc.expected), len(result))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfor i, val := range result {\n\t\t\t\tif val != tc.expected[i] {\n\t\t\t\t\tt.Errorf(\"%s: expected value at index %d to be %d, got %d\", tc.name, i, tc.expected[i], val)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_Key(t *testing.T) {\n\troot, err := Unmarshal([]byte(`{\"foo\": true, \"bar\": null, \"baz\": 123, \"biz\": [1,2,3]}`))\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err.Error())\n\t}\n\n\tobj := root.MustObject()\n\tfor key, node := range obj {\n\t\tif key != node.Key() {\n\t\t\tt.Errorf(\"Key() = %v, want %v\", node.Key(), key)\n\t\t}\n\t}\n\n\tkeys := []string{\"foo\", \"bar\", \"baz\", \"biz\"}\n\tfor _, key := range keys {\n\t\tif obj[key].Key() != key {\n\t\t\tt.Errorf(\"Key() = %v, want %v\", obj[key].Key(), key)\n\t\t}\n\t}\n\n\t// TODO: resolve stack overflow\n\t// if root.MustKey(\"foo\").Clone().Key() != \"\" {\n\t// \tt.Errorf(\"wrong key found for cloned key\")\n\t// }\n\n\tif (*Node)(nil).Key() != \"\" {\n\t\tt.Errorf(\"wrong key found for nil node\")\n\t}\n}\n\nfunc TestNode_Size(t *testing.T) {\n\troot, err := Unmarshal(sampleArr)\n\tif err != nil {\n\t\tt.Errorf(\"error occurred while unmarshal\")\n\t}\n\n\tsize := root.Size()\n\tif size != 6 {\n\t\tt.Errorf(ufmt.Sprintf(\"Size() must be 6. got: %v\", size))\n\t}\n\n\tif (*Node)(nil).Size() != 0 {\n\t\tt.Errorf(ufmt.Sprintf(\"Size() must be 0. got: %v\", (*Node)(nil).Size()))\n\t}\n}\n\nfunc TestNode_Index(t *testing.T) {\n\troot, err := Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`))\n\tif err != nil {\n\t\tt.Error(\"error occurred while unmarshal\")\n\t}\n\n\tarr := root.MustArray()\n\tfor i, node := range arr {\n\t\tif i != node.Index() {\n\t\t\tt.Errorf(ufmt.Sprintf(\"Index() must be nil. got: %v\", i))\n\t\t}\n\t}\n}\n\nfunc TestNode_Index_Fail(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tnode *Node\n\t\twant int\n\t}{\n\t\t{\"nil node\", (*Node)(nil), -1},\n\t\t{\"null node\", NullNode(\"\"), -1},\n\t\t{\"object node\", ObjectNode(\"\", nil), -1},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.node.Index(); got != tt.want {\n\t\t\t\tt.Errorf(\"Index() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_GetIndex(t *testing.T) {\n\troot := Must(Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`)))\n\texpected := []int{1, 2, 3, 4, 5, 6}\n\n\tif len(expected) != root.Size() {\n\t\tt.Errorf(\"length is not matched. expected: %d, got: %d\", len(expected), root.Size())\n\t}\n\n\t// TODO: if length exceeds, stack overflow occurs. need to fix\n\tfor i, v := range expected {\n\t\tval, err := root.GetIndex(i)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"error occurred while getting index %d, %s\", i, err)\n\t\t}\n\n\t\tif val.MustNumeric() != float64(v) {\n\t\t\tt.Errorf(\"value is not matched. expected: %d, got: %v\", v, val.MustNumeric())\n\t\t}\n\t}\n}\n\nfunc TestNode_GetIndex_InputIndex_Exceed_Original_Node_Index(t *testing.T) {\n\troot, err := Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`))\n\tif err != nil {\n\t\tt.Errorf(\"error occurred while unmarshal\")\n\t}\n\n\t_, err = root.GetIndex(10)\n\tif err == nil {\n\t\tt.Errorf(\"GetIndex should return error\")\n\t}\n}\n\nfunc TestNode_DeleteIndex(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\texpected string\n\t\tindex int\n\t\tok bool\n\t}{\n\t\t{`null`, ``, 0, false},\n\t\t{`1`, ``, 0, false},\n\t\t{`{}`, ``, 0, false},\n\t\t{`{\"foo\":\"bar\"}`, ``, 0, false},\n\t\t{`true`, ``, 0, false},\n\t\t{`[]`, ``, 0, false},\n\t\t{`[]`, ``, -1, false},\n\t\t{`[1]`, `[]`, 0, true},\n\t\t{`[{}]`, `[]`, 0, true},\n\t\t{`[{}, [], 42]`, `[{}, []]`, -1, true},\n\t\t{`[{}, [], 42]`, `[[], 42]`, 0, true},\n\t\t{`[{}, [], 42]`, `[{}, 42]`, 1, true},\n\t\t{`[{}, [], 42]`, `[{}, []]`, 2, true},\n\t\t{`[{}, [], 42]`, ``, 10, false},\n\t\t{`[{}, [], 42]`, ``, -10, false},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\troot := Must(Unmarshal([]byte(tt.name)))\n\t\t\terr := root.DeleteIndex(tt.index)\n\t\t\tif err != nil \u0026\u0026 tt.ok {\n\t\t\t\tt.Errorf(\"DeleteIndex returns error: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_GetKey(t *testing.T) {\n\troot, err := Unmarshal([]byte(`{\"foo\": true, \"bar\": null}`))\n\tif err != nil {\n\t\tt.Error(\"error occurred while unmarshal\")\n\t}\n\n\tvalue, err := root.GetKey(\"foo\")\n\tif err != nil {\n\t\tt.Errorf(\"error occurred while getting key, %s\", err)\n\t}\n\n\tif value.MustBool() != true {\n\t\tt.Errorf(\"value is not matched. expected: true, got: %v\", value.MustBool())\n\t}\n\n\tvalue, err = root.GetKey(\"bar\")\n\tif err != nil {\n\t\tt.Errorf(\"error occurred while getting key, %s\", err)\n\t}\n\n\t_, err = root.GetKey(\"baz\")\n\tif err == nil {\n\t\tt.Errorf(\"key baz is not exist. must be failed\")\n\t}\n\n\tif value.MustNull() != nil {\n\t\tt.Errorf(\"value is not matched. expected: nil, got: %v\", value.MustNull())\n\t}\n}\n\nfunc TestNode_GetKey_Fail(t *testing.T) {\n\ttests := []simpleNode{\n\t\t{\"nil node\", (*Node)(nil)},\n\t\t{\"null node\", NullNode(\"\")},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, err := tt.node.GetKey(\"\"); err == nil {\n\t\t\t\tt.Errorf(\"%s should be an error\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_GetUniqueKeyList(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tjson string\n\t\texpected []string\n\t}{\n\t\t{\n\t\t\tname: \"simple foo/bar\",\n\t\t\tjson: `{\"foo\": true, \"bar\": null}`,\n\t\t\texpected: []string{\"foo\", \"bar\"},\n\t\t},\n\t\t{\n\t\t\tname: \"empty object\",\n\t\t\tjson: `{}`,\n\t\t\texpected: []string{},\n\t\t},\n\t\t{\n\t\t\tname: \"nested object\",\n\t\t\tjson: `{\n\t\t\t\t\"outer\": {\n\t\t\t\t\t\"inner\": {\n\t\t\t\t\t\t\"key\": \"value\"\n\t\t\t\t\t},\n\t\t\t\t\t\"array\": [1, 2, 3]\n\t\t\t\t},\n\t\t\t\t\"another\": \"item\"\n\t\t\t}`,\n\t\t\texpected: []string{\"outer\", \"inner\", \"key\", \"array\", \"another\"},\n\t\t},\n\t\t{\n\t\t\tname: \"complex object\",\n\t\t\tjson: `{\n\t\t\t\t\"Image\": {\n\t\t\t\t\t\"Width\": 800,\n\t\t\t\t\t\"Height\": 600,\n\t\t\t\t\t\"Title\": \"View from 15th Floor\",\n\t\t\t\t\t\"Thumbnail\": {\n\t\t\t\t\t\t\"Url\": \"http://www.example.com/image/481989943\",\n\t\t\t\t\t\t\"Height\": 125,\n\t\t\t\t\t\t\"Width\": 100\n\t\t\t\t\t},\n\t\t\t\t\t\"Animated\": false,\n\t\t\t\t\t\"IDs\": [116, 943, 234, 38793]\n\t\t\t\t}\n\t\t\t}`,\n\t\t\texpected: []string{\"Image\", \"Width\", \"Height\", \"Title\", \"Thumbnail\", \"Url\", \"Animated\", \"IDs\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\troot, err := Unmarshal([]byte(tt.json))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"error occurred while unmarshal\")\n\t\t\t}\n\n\t\t\tvalue := root.UniqueKeyLists()\n\t\t\tif len(value) != len(tt.expected) {\n\t\t\t\tt.Errorf(\"%s length must be %v. got: %v. retrieved keys: %s\", tt.name, len(tt.expected), len(value), value)\n\t\t\t}\n\n\t\t\tfor _, key := range value {\n\t\t\t\tif !contains(tt.expected, key) {\n\t\t\t\t\tt.Errorf(\"EachKey() must be in %v. got: %v\", tt.expected, key)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TODO: resolve stack overflow\nfunc TestNode_IsEmpty(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tnode *Node\n\t\texpected bool\n\t}{\n\t\t{\"nil node\", (*Node)(nil), false}, // nil node is not empty.\n\t\t// {\"null node\", NullNode(\"\"), true},\n\t\t{\"empty object\", ObjectNode(\"\", nil), true},\n\t\t{\"empty array\", ArrayNode(\"\", nil), true},\n\t\t{\"non-empty object\", ObjectNode(\"\", map[string]*Node{\"foo\": BoolNode(\"foo\", true)}), false},\n\t\t{\"non-empty array\", ArrayNode(\"\", []*Node{BoolNode(\"0\", true)}), false},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.node.Empty(); got != tt.expected {\n\t\t\t\tt.Errorf(\"%s = %v, want %v\", tt.name, got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_Index_EmptyList(t *testing.T) {\n\troot, err := Unmarshal([]byte(`[]`))\n\tif err != nil {\n\t\tt.Errorf(\"error occurred while unmarshal\")\n\t}\n\n\tarray := root.MustArray()\n\tfor i, node := range array {\n\t\tif i != node.Index() {\n\t\t\tt.Errorf(ufmt.Sprintf(\"Index() must be nil. got: %v\", i))\n\t\t}\n\t}\n}\n\nfunc TestNode_GetObject(t *testing.T) {\n\troot, err := Unmarshal([]byte(`{\"foo\": true,\"bar\": null}`))\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err.Error())\n\t\treturn\n\t}\n\n\tvalue, err := root.GetObject()\n\tif err != nil {\n\t\tt.Errorf(\"Error on root.GetObject(): %s\", err.Error())\n\t}\n\n\tif _, ok := value[\"foo\"]; !ok {\n\t\tt.Errorf(\"root.GetObject() is corrupted: foo\")\n\t}\n\n\tif _, ok := value[\"bar\"]; !ok {\n\t\tt.Errorf(\"root.GetObject() is corrupted: bar\")\n\t}\n}\n\nfunc TestNode_GetObject_Fail(t *testing.T) {\n\ttests := []simpleNode{\n\t\t{\"nil node\", (*Node)(nil)},\n\t\t{\"get object from null node\", NullNode(\"\")},\n\t\t{\"not object node\", NumberNode(\"\", 123)},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif _, err := tt.node.GetObject(); err == nil {\n\t\t\t\tt.Errorf(\"%s should be an error\", tt.name)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_ObjectEach(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tjson string\n\t\texpected map[string]int\n\t}{\n\t\t{\n\t\t\tname: \"empty object\",\n\t\t\tjson: `{}`,\n\t\t\texpected: make(map[string]int),\n\t\t},\n\t\t{\n\t\t\tname: \"single key-value pair\",\n\t\t\tjson: `{\"key\": 42}`,\n\t\t\texpected: map[string]int{\"key\": 42},\n\t\t},\n\t\t{\n\t\t\tname: \"multiple key-value pairs\",\n\t\t\tjson: `{\"one\": 1, \"two\": 2, \"three\": 3}`,\n\t\t\texpected: map[string]int{\"one\": 1, \"two\": 2, \"three\": 3},\n\t\t},\n\t\t{\n\t\t\tname: \"multiple key-value pairs with some non-numeric values\",\n\t\t\tjson: `{\"one\": 1, \"two\": \"2\", \"three\": 3, \"four\": \"4\"}`,\n\t\t\texpected: map[string]int{\"one\": 1, \"three\": 3},\n\t\t},\n\t\t{\n\t\t\tname: \"non-object node\",\n\t\t\tjson: `[\"not\", \"an\", \"object\"]`,\n\t\t\texpected: make(map[string]int),\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\troot, err := Unmarshal([]byte(tc.json))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Unmarshal failed: %v\", err)\n\t\t\t}\n\n\t\t\tresult := make(map[string]int)\n\t\t\troot.ObjectEach(func(key string, value *Node) {\n\t\t\t\t// extract integer values from the object\n\t\t\t\tif val, err := strconv.Atoi(value.String()); err == nil {\n\t\t\t\t\tresult[key] = val\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tif len(result) != len(tc.expected) {\n\t\t\t\tt.Errorf(\"%s: expected %d key-value pairs, got %d\", tc.name, len(tc.expected), len(result))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfor key, val := range tc.expected {\n\t\t\t\tif result[key] != val {\n\t\t\t\t\tt.Errorf(\"%s: expected value for key %s to be %d, got %d\", tc.name, key, val, result[key])\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_ExampleMust(t *testing.T) {\n\tdata := []byte(`{\n \"Image\": {\n \"Width\": 800,\n \"Height\": 600,\n \"Title\": \"View from 15th Floor\",\n \"Thumbnail\": {\n \"Url\": \"http://www.example.com/image/481989943\",\n \"Height\": 125,\n \"Width\": 100\n },\n \"Animated\" : false,\n \"IDs\": [116, 943, 234, 38793]\n }\n }`)\n\n\troot := Must(Unmarshal(data))\n\tif root.Size() != 1 {\n\t\tt.Errorf(\"root.Size() must be 1. got: %v\", root.Size())\n\t}\n\n\tufmt.Sprintf(\"Object has %d inheritors inside\", root.Size())\n\t// Output:\n\t// Object has 1 inheritors inside\n}\n\n// Calculate AVG price from different types of objects, JSON from: https://goessner.net/articles/JsonPath/index.html#e3\nfunc TestExampleUnmarshal(t *testing.T) {\n\tdata := []byte(`{ \"store\": {\n \"book\": [ \n { \"category\": \"reference\",\n \"author\": \"Nigel Rees\",\n \"title\": \"Sayings of the Century\",\n \"price\": 8.95\n },\n { \"category\": \"fiction\",\n \"author\": \"Evelyn Waugh\",\n \"title\": \"Sword of Honour\",\n \"price\": 12.99\n },\n { \"category\": \"fiction\",\n \"author\": \"Herman Melville\",\n \"title\": \"Moby Dick\",\n \"isbn\": \"0-553-21311-3\",\n \"price\": 8.99\n },\n { \"category\": \"fiction\",\n \"author\": \"J. R. R. Tolkien\",\n \"title\": \"The Lord of the Rings\",\n \"isbn\": \"0-395-19395-8\",\n \"price\": 22.99\n }\n ],\n \"bicycle\": { \"color\": \"red\",\n \"price\": 19.95\n },\n \"tools\": null\n }\n}`)\n\n\troot, err := Unmarshal(data)\n\tif err != nil {\n\t\tt.Errorf(\"error occurred when unmarshal\")\n\t}\n\n\tstore := root.MustKey(\"store\").MustObject()\n\n\tvar prices float64\n\tsize := 0\n\tfor _, objects := range store {\n\t\tif objects.IsArray() \u0026\u0026 objects.Size() \u003e 0 {\n\t\t\tsize += objects.Size()\n\t\t\tfor _, object := range objects.MustArray() {\n\t\t\t\tprices += object.MustKey(\"price\").MustNumeric()\n\t\t\t}\n\t\t} else if objects.IsObject() \u0026\u0026 objects.HasKey(\"price\") {\n\t\t\tsize++\n\t\t\tprices += objects.MustKey(\"price\").MustNumeric()\n\t\t}\n\t}\n\n\tresult := int(prices / float64(size))\n\tufmt.Sprintf(\"AVG price: %d\", result)\n}\n\nfunc TestNode_ExampleMust_panic(t *testing.T) {\n\tdefer func() {\n\t\tif r := recover(); r == nil {\n\t\t\tt.Errorf(\"The code did not panic\")\n\t\t}\n\t}()\n\tdata := []byte(`{]`)\n\troot := Must(Unmarshal(data))\n\tufmt.Sprintf(\"Object has %d inheritors inside\", root.Size())\n}\n\nfunc TestNode_Path(t *testing.T) {\n\tdata := []byte(`{\n \"Image\": {\n \"Width\": 800,\n \"Height\": 600,\n \"Title\": \"View from 15th Floor\",\n \"Thumbnail\": {\n \"Url\": \"http://www.example.com/image/481989943\",\n \"Height\": 125,\n \"Width\": 100\n },\n \"Animated\" : false,\n \"IDs\": [116, 943, 234, 38793]\n }\n }`)\n\n\troot, err := Unmarshal(data)\n\tif err != nil {\n\t\tt.Errorf(\"Error on Unmarshal(): %s\", err.Error())\n\t\treturn\n\t}\n\n\tif root.Path() != \"$\" {\n\t\tt.Errorf(\"Wrong root.Path()\")\n\t}\n\n\telement := root.MustKey(\"Image\").MustKey(\"Thumbnail\").MustKey(\"Url\")\n\tif element.Path() != \"$['Image']['Thumbnail']['Url']\" {\n\t\tt.Errorf(\"Wrong path found: %s\", element.Path())\n\t}\n\n\tif (*Node)(nil).Path() != \"\" {\n\t\tt.Errorf(\"Wrong (nil).Path()\")\n\t}\n}\n\nfunc TestNode_Path2(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tnode *Node\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"Node with key\",\n\t\t\tnode: \u0026Node{\n\t\t\t\tprev: \u0026Node{},\n\t\t\t\tkey: func() *string { s := \"key\"; return \u0026s }(),\n\t\t\t},\n\t\t\twant: \"$['key']\",\n\t\t},\n\t\t{\n\t\t\tname: \"Node with index\",\n\t\t\tnode: \u0026Node{\n\t\t\t\tprev: \u0026Node{},\n\t\t\t\tindex: func() *int { i := 1; return \u0026i }(),\n\t\t\t},\n\t\t\twant: \"$[1]\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.node.Path(); got != tt.want {\n\t\t\t\tt.Errorf(\"Path() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNode_Root(t *testing.T) {\n\troot := \u0026Node{}\n\tchild := \u0026Node{prev: root}\n\tgrandChild := \u0026Node{prev: child}\n\n\ttests := []struct {\n\t\tname string\n\t\tnode *Node\n\t\twant *Node\n\t}{\n\t\t{\n\t\t\tname: \"Root node\",\n\t\t\tnode: root,\n\t\t\twant: root,\n\t\t},\n\t\t{\n\t\t\tname: \"Child node\",\n\t\t\tnode: child,\n\t\t\twant: root,\n\t\t},\n\t\t{\n\t\t\tname: \"Grandchild node\",\n\t\t\tnode: grandChild,\n\t\t\twant: root,\n\t\t},\n\t\t{\n\t\t\tname: \"Node is nil\",\n\t\t\tnode: nil,\n\t\t\twant: nil,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.node.root(); got != tt.want {\n\t\t\t\tt.Errorf(\"root() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc contains(slice []string, item string) bool {\n\tfor _, a := range slice {\n\t\tif a == item {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// ignore the sequence of keys by ordering them.\n// need to avoid import encoding/json and reflect package.\n// because gno does not support them for now.\n// TODO: use encoding/json to compare the result after if possible in gno.\nfunc isSameObject(a, b string) bool {\n\taPairs := strings.Split(strings.Trim(a, \"{}\"), \",\")\n\tbPairs := strings.Split(strings.Trim(b, \"{}\"), \",\")\n\n\taMap := make(map[string]string)\n\tbMap := make(map[string]string)\n\tfor _, pair := range aPairs {\n\t\tkv := strings.Split(pair, \":\")\n\t\tkey := strings.Trim(kv[0], `\"`)\n\t\tvalue := strings.Trim(kv[1], `\"`)\n\t\taMap[key] = value\n\t}\n\tfor _, pair := range bPairs {\n\t\tkv := strings.Split(pair, \":\")\n\t\tkey := strings.Trim(kv[0], `\"`)\n\t\tvalue := strings.Trim(kv[1], `\"`)\n\t\tbMap[key] = value\n\t}\n\n\taKeys := make([]string, 0, len(aMap))\n\tbKeys := make([]string, 0, len(bMap))\n\tfor k := range aMap {\n\t\taKeys = append(aKeys, k)\n\t}\n\n\tfor k := range bMap {\n\t\tbKeys = append(bKeys, k)\n\t}\n\n\tsort.Strings(aKeys)\n\tsort.Strings(bKeys)\n\n\tif len(aKeys) != len(bKeys) {\n\t\treturn false\n\t}\n\n\tfor i := range aKeys {\n\t\tif aKeys[i] != bKeys[i] {\n\t\t\treturn false\n\t\t}\n\n\t\tif aMap[aKeys[i]] != bMap[bKeys[i]] {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc compareNodes(n1, n2 *Node) bool {\n\tif n1 == nil || n2 == nil {\n\t\treturn n1 == n2\n\t}\n\n\tif n1.key != n2.key {\n\t\treturn false\n\t}\n\n\tif !bytes.Equal(n1.data, n2.data) {\n\t\treturn false\n\t}\n\n\tif n1.index != n2.index {\n\t\treturn false\n\t}\n\n\tif n1.borders != n2.borders {\n\t\treturn false\n\t}\n\n\tif n1.modified != n2.modified {\n\t\treturn false\n\t}\n\n\tif n1.nodeType != n2.nodeType {\n\t\treturn false\n\t}\n\n\tif !compareNodes(n1.prev, n2.prev) {\n\t\treturn false\n\t}\n\n\tif len(n1.next) != len(n2.next) {\n\t\treturn false\n\t}\n\n\tfor k, v := range n1.next {\n\t\tif !compareNodes(v, n2.next[k]) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n" + }, + { + "name": "parser.gno", + "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"strconv\"\n\n\tel \"gno.land/p/demo/json/eisel_lemire\"\n)\n\nconst (\n\tabsMinInt64 = 1 \u003c\u003c 63\n\tmaxInt64 = absMinInt64 - 1\n\tmaxUint64 = 1\u003c\u003c64 - 1\n)\n\nconst unescapeStackBufSize = 64\n\n// PaseStringLiteral parses a string from the given byte slice.\nfunc ParseStringLiteral(data []byte) (string, error) {\n\tvar buf [unescapeStackBufSize]byte\n\n\tbf, err := Unescape(data, buf[:])\n\tif err != nil {\n\t\treturn \"\", errors.New(\"invalid string input found while parsing string value\")\n\t}\n\n\treturn string(bf), nil\n}\n\n// ParseBoolLiteral parses a boolean value from the given byte slice.\nfunc ParseBoolLiteral(data []byte) (bool, error) {\n\tswitch {\n\tcase bytes.Equal(data, trueLiteral):\n\t\treturn true, nil\n\tcase bytes.Equal(data, falseLiteral):\n\t\treturn false, nil\n\tdefault:\n\t\treturn false, errors.New(\"JSON Error: malformed boolean value found while parsing boolean value\")\n\t}\n}\n\n// PaseFloatLiteral parses a float64 from the given byte slice.\n//\n// It utilizes double-precision (64-bit) floating-point format as defined\n// by the IEEE 754 standard, providing a decimal precision of approximately 15 digits.\nfunc ParseFloatLiteral(bytes []byte) (float64, error) {\n\tif len(bytes) == 0 {\n\t\treturn -1, errors.New(\"JSON Error: empty byte slice found while parsing float value\")\n\t}\n\n\tneg, bytes := trimNegativeSign(bytes)\n\n\tvar exponentPart []byte\n\tfor i, c := range bytes {\n\t\tif lower(c) == 'e' {\n\t\t\texponentPart = bytes[i+1:]\n\t\t\tbytes = bytes[:i]\n\t\t\tbreak\n\t\t}\n\t}\n\n\tman, exp10, err := extractMantissaAndExp10(bytes)\n\tif err != nil {\n\t\treturn -1, err\n\t}\n\n\tif len(exponentPart) \u003e 0 {\n\t\texp, err := strconv.Atoi(string(exponentPart))\n\t\tif err != nil {\n\t\t\treturn -1, errors.New(\"JSON Error: invalid exponent value found while parsing float value\")\n\t\t}\n\t\texp10 += exp\n\t}\n\n\t// for fast float64 conversion\n\tf, success := el.EiselLemire64(man, exp10, neg)\n\tif !success {\n\t\treturn 0, nil\n\t}\n\n\treturn f, nil\n}\n\nfunc ParseIntLiteral(bytes []byte) (int64, error) {\n\tif len(bytes) == 0 {\n\t\treturn 0, errors.New(\"JSON Error: empty byte slice found while parsing integer value\")\n\t}\n\n\tneg, bytes := trimNegativeSign(bytes)\n\n\tvar n uint64 = 0\n\tfor _, c := range bytes {\n\t\tif notDigit(c) {\n\t\t\treturn 0, errors.New(\"JSON Error: non-digit characters found while parsing integer value\")\n\t\t}\n\n\t\tif n \u003e maxUint64/10 {\n\t\t\treturn 0, errors.New(\"JSON Error: numeric value exceeds the range limit\")\n\t\t}\n\n\t\tn *= 10\n\n\t\tn1 := n + uint64(c-'0')\n\t\tif n1 \u003c n {\n\t\t\treturn 0, errors.New(\"JSON Error: numeric value exceeds the range limit\")\n\t\t}\n\n\t\tn = n1\n\t}\n\n\tif n \u003e maxInt64 {\n\t\tif neg \u0026\u0026 n == absMinInt64 {\n\t\t\treturn -absMinInt64, nil\n\t\t}\n\n\t\treturn 0, errors.New(\"JSON Error: numeric value exceeds the range limit\")\n\t}\n\n\tif neg {\n\t\treturn -int64(n), nil\n\t}\n\n\treturn int64(n), nil\n}\n\n// extractMantissaAndExp10 parses a byte slice representing a decimal number and extracts the mantissa and the exponent of its base-10 representation.\n// It iterates through the bytes, constructing the mantissa by treating each byte as a digit.\n// If a decimal point is encountered, the function keeps track of the position of the decimal point to calculate the exponent.\n// The function ensures that:\n// - The number contains at most one decimal point.\n// - All characters in the byte slice are digits or a single decimal point.\n// - The resulting mantissa does not overflow a uint64.\nfunc extractMantissaAndExp10(bytes []byte) (uint64, int, error) {\n\tvar (\n\t\tman uint64\n\t\texp10 int\n\t\tdecimalFound bool\n\t)\n\n\tfor _, c := range bytes {\n\t\tif c == dot {\n\t\t\tif decimalFound {\n\t\t\t\treturn 0, 0, errors.New(\"JSON Error: multiple decimal points found while parsing float value\")\n\t\t\t}\n\t\t\tdecimalFound = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif notDigit(c) {\n\t\t\treturn 0, 0, errors.New(\"JSON Error: non-digit characters found while parsing integer value\")\n\t\t}\n\n\t\tdigit := uint64(c - '0')\n\n\t\tif man \u003e (maxUint64-digit)/10 {\n\t\t\treturn 0, 0, errors.New(\"JSON Error: numeric value exceeds the range limit\")\n\t\t}\n\n\t\tman = man*10 + digit\n\n\t\tif decimalFound {\n\t\t\texp10--\n\t\t}\n\t}\n\n\treturn man, exp10, nil\n}\n\nfunc trimNegativeSign(bytes []byte) (bool, []byte) {\n\tif bytes[0] == minus {\n\t\treturn true, bytes[1:]\n\t}\n\n\treturn false, bytes\n}\n\nfunc notDigit(c byte) bool {\n\treturn (c \u0026 0xF0) != 0x30\n}\n\n// lower converts a byte to lower case if it is an uppercase letter.\nfunc lower(c byte) byte {\n\treturn c | 0x20\n}\n" + }, + { + "name": "parser_test.gno", + "body": "package json\n\nimport \"testing\"\n\nfunc TestParseStringLiteral(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\texpected string\n\t\tisError bool\n\t}{\n\t\t{`\"Hello, World!\"`, \"\\\"Hello, World!\\\"\", false},\n\t\t{`\\uFF11`, \"\\uFF11\", false},\n\t\t{`\\uFFFF`, \"\\uFFFF\", false},\n\t\t{`true`, \"true\", false},\n\t\t{`false`, \"false\", false},\n\t\t{`\\uDF00`, \"\", true},\n\t}\n\n\tfor i, tt := range tests {\n\t\ts, err := ParseStringLiteral([]byte(tt.input))\n\n\t\tif !tt.isError \u0026\u0026 err != nil {\n\t\t\tt.Errorf(\"%d. unexpected error: %s\", i, err)\n\t\t}\n\n\t\tif tt.isError \u0026\u0026 err == nil {\n\t\t\tt.Errorf(\"%d. expected error, but not error\", i)\n\t\t}\n\n\t\tif s != tt.expected {\n\t\t\tt.Errorf(\"%d. expected=%s, but actual=%s\", i, tt.expected, s)\n\t\t}\n\t}\n}\n\nfunc TestParseBoolLiteral(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\texpected bool\n\t\tisError bool\n\t}{\n\t\t{`true`, true, false},\n\t\t{`false`, false, false},\n\t\t{`TRUE`, false, true},\n\t\t{`FALSE`, false, true},\n\t\t{`foo`, false, true},\n\t\t{`\"true\"`, false, true},\n\t\t{`\"false\"`, false, true},\n\t}\n\n\tfor i, tt := range tests {\n\t\tb, err := ParseBoolLiteral([]byte(tt.input))\n\n\t\tif !tt.isError \u0026\u0026 err != nil {\n\t\t\tt.Errorf(\"%d. unexpected error: %s\", i, err)\n\t\t}\n\n\t\tif tt.isError \u0026\u0026 err == nil {\n\t\t\tt.Errorf(\"%d. expected error, but not error\", i)\n\t\t}\n\n\t\tif b != tt.expected {\n\t\t\tt.Errorf(\"%d. expected=%t, but actual=%t\", i, tt.expected, b)\n\t\t}\n\t}\n}\n\nfunc TestParseFloatLiteral(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\texpected float64\n\t}{\n\t\t{\"123\", 123},\n\t\t{\"-123\", -123},\n\t\t{\"123.456\", 123.456},\n\t\t{\"-123.456\", -123.456},\n\t\t{\"12345678.1234567890\", 12345678.1234567890},\n\t\t{\"-12345678.09123456789\", -12345678.09123456789},\n\t\t{\"0.123\", 0.123},\n\t\t{\"-0.123\", -0.123},\n\t\t{\"\", -1},\n\t\t{\"abc\", -1},\n\t\t{\"123.45.6\", -1},\n\t\t{\"999999999999999999999\", -1},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\tgot, _ := ParseFloatLiteral([]byte(tt.input))\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"ParseFloatLiteral(%s): got %v, want %v\", tt.input, got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseFloatWithScientificNotation(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\texpected float64\n\t}{\n\t\t{\"1e6\", 1000000},\n\t\t{\"1E6\", 1000000},\n\t\t{\"1.23e10\", 1.23e10},\n\t\t{\"1.23E10\", 1.23e10},\n\t\t{\"-1.23e10\", -1.23e10},\n\t\t{\"-1.23E10\", -1.23e10},\n\t\t{\"2.45e-8\", 2.45e-8},\n\t\t{\"2.45E-8\", 2.45e-8},\n\t\t{\"-2.45e-8\", -2.45e-8},\n\t\t{\"-2.45E-8\", -2.45e-8},\n\t\t{\"5e0\", 5},\n\t\t{\"-5e0\", -5},\n\t\t{\"5E+0\", 5},\n\t\t{\"5e+1\", 50},\n\t\t{\"1e-1\", 0.1},\n\t\t{\"1E-1\", 0.1},\n\t\t{\"-1e-1\", -0.1},\n\t\t{\"-1E-1\", -0.1},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\tgot, err := ParseFloatLiteral([]byte(tt.input))\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"ParseFloatLiteral(%s): got %v, want %v\", tt.input, got, tt.expected)\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"ParseFloatLiteral(%s): got error %v\", tt.input, err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseFloat_May_Interoperability_Problem(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\tshouldErr bool\n\t}{\n\t\t{\"3.141592653589793238462643383279\", true},\n\t\t{\"1E400\", false}, // TODO: should error\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\t_, err := ParseFloatLiteral([]byte(tt.input))\n\t\t\tif tt.shouldErr \u0026\u0026 err == nil {\n\t\t\t\tt.Errorf(\"ParseFloatLiteral(%s): expected error, but not error\", tt.input)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseIntLiteral(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\texpected int64\n\t}{\n\t\t{\"0\", 0},\n\t\t{\"1\", 1},\n\t\t{\"-1\", -1},\n\t\t{\"12345\", 12345},\n\t\t{\"-12345\", -12345},\n\t\t{\"9223372036854775807\", 9223372036854775807},\n\t\t{\"-9223372036854775808\", -9223372036854775808},\n\t\t{\"-92233720368547758081\", 0},\n\t\t{\"18446744073709551616\", 0},\n\t\t{\"9223372036854775808\", 0},\n\t\t{\"-9223372036854775809\", 0},\n\t\t{\"\", 0},\n\t\t{\"abc\", 0},\n\t\t{\"12345x\", 0},\n\t\t{\"123e5\", 0},\n\t\t{\"9223372036854775807x\", 0},\n\t\t{\"27670116110564327410\", 0},\n\t\t{\"-27670116110564327410\", 0},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\tgot, _ := ParseIntLiteral([]byte(tt.input))\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"ParseIntLiteral(%s): got %v, want %v\", tt.input, got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "path.gno", + "body": "package json\n\nimport (\n\t\"errors\"\n)\n\n// ParsePath takes a JSONPath string and returns a slice of strings representing the path segments.\nfunc ParsePath(path string) ([]string, error) {\n\tbuf := newBuffer([]byte(path))\n\tresult := make([]string, 0)\n\n\tfor {\n\t\tb, err := buf.current()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\n\t\tswitch {\n\t\tcase b == dollarSign || b == atSign:\n\t\t\tresult = append(result, string(b))\n\t\t\tbuf.step()\n\n\t\tcase b == dot:\n\t\t\tbuf.step()\n\n\t\t\tif next, _ := buf.current(); next == dot {\n\t\t\t\tbuf.step()\n\t\t\t\tresult = append(result, \"..\")\n\n\t\t\t\textractNextSegment(buf, \u0026result)\n\t\t\t} else {\n\t\t\t\textractNextSegment(buf, \u0026result)\n\t\t\t}\n\n\t\tcase b == bracketOpen:\n\t\t\tstart := buf.index\n\t\t\tbuf.step()\n\n\t\t\tfor {\n\t\t\t\tif buf.index \u003e= buf.length || buf.data[buf.index] == bracketClose {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tbuf.step()\n\t\t\t}\n\n\t\t\tif buf.index \u003e= buf.length {\n\t\t\t\treturn nil, errors.New(\"unexpected end of path\")\n\t\t\t}\n\n\t\t\tsegment := string(buf.sliceFromIndices(start+1, buf.index))\n\t\t\tresult = append(result, segment)\n\n\t\t\tbuf.step()\n\n\t\tdefault:\n\t\t\tbuf.step()\n\t\t}\n\t}\n\n\treturn result, nil\n}\n\n// extractNextSegment extracts the segment from the current index\n// to the next significant character and adds it to the resulting slice.\nfunc extractNextSegment(buf *buffer, result *[]string) {\n\tstart := buf.index\n\tbuf.skipToNextSignificantToken()\n\n\tif buf.index \u003c= start {\n\t\treturn\n\t}\n\n\tsegment := string(buf.sliceFromIndices(start, buf.index))\n\tif segment != \"\" {\n\t\t*result = append(*result, segment)\n\t}\n}\n" + }, + { + "name": "path_test.gno", + "body": "package json\n\nimport \"testing\"\n\nfunc TestParseJSONPath(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tpath string\n\t\texpected []string\n\t}{\n\t\t{name: \"Empty string path\", path: \"\", expected: []string{}},\n\t\t{name: \"Root only path\", path: \"$\", expected: []string{\"$\"}},\n\t\t{name: \"Root with dot path\", path: \"$.\", expected: []string{\"$\"}},\n\t\t{name: \"All objects in path\", path: \"$..\", expected: []string{\"$\", \"..\"}},\n\t\t{name: \"Only children in path\", path: \"$.*\", expected: []string{\"$\", \"*\"}},\n\t\t{name: \"All objects' children in path\", path: \"$..*\", expected: []string{\"$\", \"..\", \"*\"}},\n\t\t{name: \"Simple dot notation path\", path: \"$.root.element\", expected: []string{\"$\", \"root\", \"element\"}},\n\t\t{name: \"Complex dot notation path with wildcard\", path: \"$.root.*.element\", expected: []string{\"$\", \"root\", \"*\", \"element\"}},\n\t\t{name: \"Path with array wildcard\", path: \"$.phoneNumbers[*].type\", expected: []string{\"$\", \"phoneNumbers\", \"*\", \"type\"}},\n\t\t{name: \"Path with filter expression\", path: \"$.store.book[?(@.price \u003c 10)].title\", expected: []string{\"$\", \"store\", \"book\", \"?(@.price \u003c 10)\", \"title\"}},\n\t\t{name: \"Path with formula\", path: \"$..phoneNumbers..('ty' + 'pe')\", expected: []string{\"$\", \"..\", \"phoneNumbers\", \"..\", \"('ty' + 'pe')\"}},\n\t\t{name: \"Simple bracket notation path\", path: \"$['root']['element']\", expected: []string{\"$\", \"'root'\", \"'element'\"}},\n\t\t{name: \"Complex bracket notation path with wildcard\", path: \"$['root'][*]['element']\", expected: []string{\"$\", \"'root'\", \"*\", \"'element'\"}},\n\t\t{name: \"Bracket notation path with integer index\", path: \"$['store']['book'][0]['title']\", expected: []string{\"$\", \"'store'\", \"'book'\", \"0\", \"'title'\"}},\n\t\t{name: \"Complex path with wildcard in bracket notation\", path: \"$['root'].*['element']\", expected: []string{\"$\", \"'root'\", \"*\", \"'element'\"}},\n\t\t{name: \"Mixed notation path with dot after bracket\", path: \"$.['root'].*.['element']\", expected: []string{\"$\", \"'root'\", \"*\", \"'element'\"}},\n\t\t{name: \"Mixed notation path with dot before bracket\", path: \"$['root'].*.['element']\", expected: []string{\"$\", \"'root'\", \"*\", \"'element'\"}},\n\t\t{name: \"Single character path with root\", path: \"$.a\", expected: []string{\"$\", \"a\"}},\n\t\t{name: \"Multiple characters path with root\", path: \"$.abc\", expected: []string{\"$\", \"abc\"}},\n\t\t{name: \"Multiple segments path with root\", path: \"$.a.b.c\", expected: []string{\"$\", \"a\", \"b\", \"c\"}},\n\t\t{name: \"Multiple segments path with wildcard and root\", path: \"$.a.*.c\", expected: []string{\"$\", \"a\", \"*\", \"c\"}},\n\t\t{name: \"Multiple segments path with filter and root\", path: \"$.a[?(@.b == 'c')].d\", expected: []string{\"$\", \"a\", \"?(@.b == 'c')\", \"d\"}},\n\t\t{name: \"Complex path with multiple filters\", path: \"$.a[?(@.b == 'c')].d[?(@.e == 'f')].g\", expected: []string{\"$\", \"a\", \"?(@.b == 'c')\", \"d\", \"?(@.e == 'f')\", \"g\"}},\n\t\t{name: \"Complex path with multiple filters and wildcards\", path: \"$.a[?(@.b == 'c')].*.d[?(@.e == 'f')].g\", expected: []string{\"$\", \"a\", \"?(@.b == 'c')\", \"*\", \"d\", \"?(@.e == 'f')\", \"g\"}},\n\t\t{name: \"Path with array index and root\", path: \"$.a[0].b\", expected: []string{\"$\", \"a\", \"0\", \"b\"}},\n\t\t{name: \"Path with multiple array indices and root\", path: \"$.a[0].b[1].c\", expected: []string{\"$\", \"a\", \"0\", \"b\", \"1\", \"c\"}},\n\t\t{name: \"Path with array index, wildcard and root\", path: \"$.a[0].*.c\", expected: []string{\"$\", \"a\", \"0\", \"*\", \"c\"}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\treult, _ := ParsePath(tt.path)\n\t\t\tif !isEqualSlice(reult, tt.expected) {\n\t\t\t\tt.Errorf(\"ParsePath(%s) expected: %v, got: %v\", tt.path, tt.expected, reult)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc isEqualSlice(a, b []string) bool {\n\tif len(a) != len(b) {\n\t\treturn false\n\t}\n\n\tfor i, v := range a {\n\t\tif v != b[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n" + }, + { + "name": "token.gno", + "body": "package json\n\nconst (\n\tbracketOpen = '['\n\tbracketClose = ']'\n\tparenOpen = '('\n\tparenClose = ')'\n\tcurlyOpen = '{'\n\tcurlyClose = '}'\n\tcomma = ','\n\tdot = '.'\n\tcolon = ':'\n\tbackTick = '`'\n\tsingleQuote = '\\''\n\tdoubleQuote = '\"'\n\temptyString = \"\"\n\twhiteSpace = ' '\n\tplus = '+'\n\tminus = '-'\n\taesterisk = '*'\n\tbang = '!'\n\tquestion = '?'\n\tnewLine = '\\n'\n\ttab = '\\t'\n\tcarriageReturn = '\\r'\n\tformFeed = '\\f'\n\tbackSpace = '\\b'\n\tslash = '/'\n\tbackSlash = '\\\\'\n\tunderScore = '_'\n\tdollarSign = '$'\n\tatSign = '@'\n\tandSign = '\u0026'\n\torSign = '|'\n)\n\nvar (\n\ttrueLiteral = []byte(\"true\")\n\tfalseLiteral = []byte(\"false\")\n\tnullLiteral = []byte(\"null\")\n)\n\ntype ValueType int\n\nconst (\n\tNotExist ValueType = iota\n\tString\n\tNumber\n\tFloat\n\tObject\n\tArray\n\tBoolean\n\tNull\n\tUnknown\n)\n\nfunc (v ValueType) String() string {\n\tswitch v {\n\tcase NotExist:\n\t\treturn \"not-exist\"\n\tcase String:\n\t\treturn \"string\"\n\tcase Number:\n\t\treturn \"number\"\n\tcase Object:\n\t\treturn \"object\"\n\tcase Array:\n\t\treturn \"array\"\n\tcase Boolean:\n\t\treturn \"boolean\"\n\tcase Null:\n\t\treturn \"null\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "int32", + "path": "gno.land/p/demo/math_eval/int32", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/math_eval/int32\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" + }, + { + "name": "int32.gno", + "body": "// eval/int32 is a evaluator for int32 expressions.\n// This code is heavily forked from https://github.com/dengsgo/math-engine\n// which is licensed under Apache 2.0:\n// https://raw.githubusercontent.com/dengsgo/math-engine/298e2b57b7e7350d0f67bd036916efd5709abe25/LICENSE\npackage int32\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nconst (\n\tIdentifier = iota\n\tNumber // numbers\n\tOperator // +, -, *, /, etc.\n\tVariable // x, y, z, etc. (one-letter only)\n)\n\ntype expression interface {\n\tString() string\n}\n\ntype expressionRaw struct {\n\texpression string\n\tType int\n\tFlag int\n\tOffset int\n}\n\ntype parser struct {\n\tInput string\n\tch byte\n\toffset int\n\terr error\n}\n\ntype expressionNumber struct {\n\tVal int\n\tStr string\n}\n\ntype expressionVariable struct {\n\tVal int\n\tStr string\n}\n\ntype expressionOperation struct {\n\tOp string\n\tLhs,\n\tRhs expression\n}\n\ntype ast struct {\n\trawexpressions []*expressionRaw\n\tsource string\n\tcurrentexpression *expressionRaw\n\tcurrentIndex int\n\tdepth int\n\terr error\n}\n\n// Parse takes an expression string, e.g. \"1+2\" and returns\n// a parsed expression. If there is an error it will return.\nfunc Parse(s string) (ar expression, err error) {\n\ttoks, err := lexer(s)\n\tif err != nil {\n\t\treturn\n\t}\n\tast, err := newAST(toks, s)\n\tif err != nil {\n\t\treturn\n\t}\n\tar, err = ast.parseExpression()\n\treturn\n}\n\n// Eval takes a parsed expression and a map of variables (or nil). The parsed\n// expression is evaluated using any variables and returns the\n// resulting int and/or error.\nfunc Eval(expr expression, variables map[string]int) (res int, err error) {\n\tif err != nil {\n\t\treturn\n\t}\n\tvar l, r int\n\tswitch expr.(type) {\n\tcase expressionVariable:\n\t\tast := expr.(expressionVariable)\n\t\tok := false\n\t\tif variables != nil {\n\t\t\tres, ok = variables[ast.Str]\n\t\t}\n\t\tif !ok {\n\t\t\terr = ufmt.Errorf(\"variable '%s' not found\", ast.Str)\n\t\t}\n\t\treturn\n\tcase expressionOperation:\n\t\tast := expr.(expressionOperation)\n\t\tl, err = Eval(ast.Lhs, variables)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tr, err = Eval(ast.Rhs, variables)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tswitch ast.Op {\n\t\tcase \"+\":\n\t\t\tres = l + r\n\t\tcase \"-\":\n\t\t\tres = l - r\n\t\tcase \"*\":\n\t\t\tres = l * r\n\t\tcase \"/\":\n\t\t\tif r == 0 {\n\t\t\t\terr = ufmt.Errorf(\"violation of arithmetic specification: a division by zero in Eval: [%d/%d]\", l, r)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tres = l / r\n\t\tcase \"%\":\n\t\t\tif r == 0 {\n\t\t\t\tres = 0\n\t\t\t} else {\n\t\t\t\tres = l % r\n\t\t\t}\n\t\tcase \"^\":\n\t\t\tres = l ^ r\n\t\tcase \"\u003e\u003e\":\n\t\t\tres = l \u003e\u003e r\n\t\tcase \"\u003c\u003c\":\n\t\t\tres = l \u003c\u003c r\n\t\tcase \"\u003e\":\n\t\t\tif l \u003e r {\n\t\t\t\tres = 1\n\t\t\t} else {\n\t\t\t\tres = 0\n\t\t\t}\n\t\tcase \"\u003c\":\n\t\t\tif l \u003c r {\n\t\t\t\tres = 1\n\t\t\t} else {\n\t\t\t\tres = 0\n\t\t\t}\n\t\tcase \"\u0026\":\n\t\t\tres = l \u0026 r\n\t\tcase \"|\":\n\t\t\tres = l | r\n\t\tdefault:\n\n\t\t}\n\tcase expressionNumber:\n\t\tres = expr.(expressionNumber).Val\n\t}\n\n\treturn\n}\n\nfunc expressionError(s string, pos int) string {\n\tr := strings.Repeat(\"-\", len(s)) + \"\\n\"\n\ts += \"\\n\"\n\tfor i := 0; i \u003c pos; i++ {\n\t\ts += \" \"\n\t}\n\ts += \"^\\n\"\n\treturn r + s + r\n}\n\nfunc (n expressionVariable) String() string {\n\treturn ufmt.Sprintf(\n\t\t\"expressionVariable: %s\",\n\t\tn.Str,\n\t)\n}\n\nfunc (n expressionNumber) String() string {\n\treturn ufmt.Sprintf(\n\t\t\"expressionNumber: %s\",\n\t\tn.Str,\n\t)\n}\n\nfunc (b expressionOperation) String() string {\n\treturn ufmt.Sprintf(\n\t\t\"expressionOperation: (%s %s %s)\",\n\t\tb.Op,\n\t\tb.Lhs.String(),\n\t\tb.Rhs.String(),\n\t)\n}\n\nfunc newAST(toks []*expressionRaw, s string) (*ast, error) {\n\ta := \u0026ast{\n\t\trawexpressions: toks,\n\t\tsource: s,\n\t}\n\tif a.rawexpressions == nil || len(a.rawexpressions) == 0 {\n\t\treturn a, errors.New(\"empty token\")\n\t} else {\n\t\ta.currentIndex = 0\n\t\ta.currentexpression = a.rawexpressions[0]\n\t}\n\treturn a, nil\n}\n\nfunc (a *ast) parseExpression() (expression, error) {\n\ta.depth++ // called depth\n\tlhs := a.parsePrimary()\n\tr := a.parseBinOpRHS(0, lhs)\n\ta.depth--\n\tif a.depth == 0 \u0026\u0026 a.currentIndex != len(a.rawexpressions) \u0026\u0026 a.err == nil {\n\t\treturn r, ufmt.Errorf(\"bad expression, reaching the end or missing the operator\\n%s\",\n\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t}\n\treturn r, nil\n}\n\nfunc (a *ast) getNextexpressionRaw() *expressionRaw {\n\ta.currentIndex++\n\tif a.currentIndex \u003c len(a.rawexpressions) {\n\t\ta.currentexpression = a.rawexpressions[a.currentIndex]\n\t\treturn a.currentexpression\n\t}\n\treturn nil\n}\n\nfunc (a *ast) getTokPrecedence() int {\n\tswitch a.currentexpression.expression {\n\tcase \"/\", \"%\", \"*\":\n\t\treturn 100\n\tcase \"\u003c\u003c\", \"\u003e\u003e\":\n\t\treturn 80\n\tcase \"+\", \"-\":\n\t\treturn 75\n\tcase \"\u003c\", \"\u003e\":\n\t\treturn 70\n\tcase \"\u0026\":\n\t\treturn 60\n\tcase \"^\":\n\t\treturn 50\n\tcase \"|\":\n\t\treturn 40\n\t}\n\treturn -1\n}\n\nfunc (a *ast) parseNumber() expressionNumber {\n\tf64, err := strconv.Atoi(a.currentexpression.expression)\n\tif err != nil {\n\t\ta.err = ufmt.Errorf(\"%v\\nwant '(' or '0-9' but get '%s'\\n%s\",\n\t\t\terr.Error(),\n\t\t\ta.currentexpression.expression,\n\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\treturn expressionNumber{}\n\t}\n\tn := expressionNumber{\n\t\tVal: f64,\n\t\tStr: a.currentexpression.expression,\n\t}\n\ta.getNextexpressionRaw()\n\treturn n\n}\n\nfunc (a *ast) parseVariable() expressionVariable {\n\tn := expressionVariable{\n\t\tVal: 0,\n\t\tStr: a.currentexpression.expression,\n\t}\n\ta.getNextexpressionRaw()\n\treturn n\n}\n\nfunc (a *ast) parsePrimary() expression {\n\tswitch a.currentexpression.Type {\n\tcase Variable:\n\t\treturn a.parseVariable()\n\tcase Number:\n\t\treturn a.parseNumber()\n\tcase Operator:\n\t\tif a.currentexpression.expression == \"(\" {\n\t\t\tt := a.getNextexpressionRaw()\n\t\t\tif t == nil {\n\t\t\t\ta.err = ufmt.Errorf(\"want '(' or '0-9' but get EOF\\n%s\",\n\t\t\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\te, _ := a.parseExpression()\n\t\t\tif e == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif a.currentexpression.expression != \")\" {\n\t\t\t\ta.err = ufmt.Errorf(\"want ')' but get %s\\n%s\",\n\t\t\t\t\ta.currentexpression.expression,\n\t\t\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\ta.getNextexpressionRaw()\n\t\t\treturn e\n\t\t} else if a.currentexpression.expression == \"-\" {\n\t\t\tif a.getNextexpressionRaw() == nil {\n\t\t\t\ta.err = ufmt.Errorf(\"want '0-9' but get '-'\\n%s\",\n\t\t\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tbin := expressionOperation{\n\t\t\t\tOp: \"-\",\n\t\t\t\tLhs: expressionNumber{},\n\t\t\t\tRhs: a.parsePrimary(),\n\t\t\t}\n\t\t\treturn bin\n\t\t} else {\n\t\t\treturn a.parseNumber()\n\t\t}\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc (a *ast) parseBinOpRHS(execPrec int, lhs expression) expression {\n\tfor {\n\t\ttokPrec := a.getTokPrecedence()\n\t\tif tokPrec \u003c execPrec {\n\t\t\treturn lhs\n\t\t}\n\t\tbinOp := a.currentexpression.expression\n\t\tif a.getNextexpressionRaw() == nil {\n\t\t\ta.err = ufmt.Errorf(\"want '(' or '0-9' but get EOF\\n%s\",\n\t\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\t\treturn nil\n\t\t}\n\t\trhs := a.parsePrimary()\n\t\tif rhs == nil {\n\t\t\treturn nil\n\t\t}\n\t\tnextPrec := a.getTokPrecedence()\n\t\tif tokPrec \u003c nextPrec {\n\t\t\trhs = a.parseBinOpRHS(tokPrec+1, rhs)\n\t\t\tif rhs == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tlhs = expressionOperation{\n\t\t\tOp: binOp,\n\t\t\tLhs: lhs,\n\t\t\tRhs: rhs,\n\t\t}\n\t}\n}\n\nfunc lexer(s string) ([]*expressionRaw, error) {\n\tp := \u0026parser{\n\t\tInput: s,\n\t\terr: nil,\n\t\tch: s[0],\n\t}\n\ttoks := p.parse()\n\tif p.err != nil {\n\t\treturn nil, p.err\n\t}\n\treturn toks, nil\n}\n\nfunc (p *parser) parse() []*expressionRaw {\n\ttoks := make([]*expressionRaw, 0)\n\tfor {\n\t\ttok := p.nextTok()\n\t\tif tok == nil {\n\t\t\tbreak\n\t\t}\n\t\ttoks = append(toks, tok)\n\t}\n\treturn toks\n}\n\nfunc (p *parser) nextTok() *expressionRaw {\n\tif p.offset \u003e= len(p.Input) || p.err != nil {\n\t\treturn nil\n\t}\n\tvar err error\n\tfor p.isWhitespace(p.ch) \u0026\u0026 err == nil {\n\t\terr = p.nextCh()\n\t}\n\tstart := p.offset\n\tvar tok *expressionRaw\n\tswitch p.ch {\n\tcase\n\t\t'(',\n\t\t')',\n\t\t'+',\n\t\t'-',\n\t\t'*',\n\t\t'/',\n\t\t'^',\n\t\t'\u0026',\n\t\t'|',\n\t\t'%':\n\t\ttok = \u0026expressionRaw{\n\t\t\texpression: string(p.ch),\n\t\t\tType: Operator,\n\t\t}\n\t\ttok.Offset = start\n\t\terr = p.nextCh()\n\tcase '\u003e', '\u003c':\n\t\ttokS := string(p.ch)\n\t\tbb, be := p.nextChPeek()\n\t\tif be == nil \u0026\u0026 string(bb) == tokS {\n\t\t\ttokS += string(p.ch)\n\t\t}\n\t\ttok = \u0026expressionRaw{\n\t\t\texpression: tokS,\n\t\t\tType: Operator,\n\t\t}\n\t\ttok.Offset = start\n\t\tif len(tokS) \u003e 1 {\n\t\t\tp.nextCh()\n\t\t}\n\t\terr = p.nextCh()\n\tcase\n\t\t'0',\n\t\t'1',\n\t\t'2',\n\t\t'3',\n\t\t'4',\n\t\t'5',\n\t\t'6',\n\t\t'7',\n\t\t'8',\n\t\t'9':\n\t\tfor p.isDigitNum(p.ch) \u0026\u0026 p.nextCh() == nil {\n\t\t\tif (p.ch == '-' || p.ch == '+') \u0026\u0026 p.Input[p.offset-1] != 'e' {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\ttok = \u0026expressionRaw{\n\t\t\texpression: strings.ReplaceAll(p.Input[start:p.offset], \"_\", \"\"),\n\t\t\tType: Number,\n\t\t}\n\t\ttok.Offset = start\n\tdefault:\n\t\tif p.isChar(p.ch) {\n\t\t\ttok = \u0026expressionRaw{\n\t\t\t\texpression: string(p.ch),\n\t\t\t\tType: Variable,\n\t\t\t}\n\t\t\ttok.Offset = start\n\t\t\terr = p.nextCh()\n\t\t} else if p.ch != ' ' {\n\t\t\tp.err = ufmt.Errorf(\"symbol error: unknown '%v', pos [%v:]\\n%s\",\n\t\t\t\tstring(p.ch),\n\t\t\t\tstart,\n\t\t\t\texpressionError(p.Input, start))\n\t\t}\n\t}\n\treturn tok\n}\n\nfunc (p *parser) nextChPeek() (byte, error) {\n\toffset := p.offset + 1\n\tif offset \u003c len(p.Input) {\n\t\treturn p.Input[offset], nil\n\t}\n\treturn byte(0), errors.New(\"no byte\")\n}\n\nfunc (p *parser) nextCh() error {\n\tp.offset++\n\tif p.offset \u003c len(p.Input) {\n\t\tp.ch = p.Input[p.offset]\n\t\treturn nil\n\t}\n\treturn errors.New(\"EOF\")\n}\n\nfunc (p *parser) isWhitespace(c byte) bool {\n\treturn c == ' ' ||\n\t\tc == '\\t' ||\n\t\tc == '\\n' ||\n\t\tc == '\\v' ||\n\t\tc == '\\f' ||\n\t\tc == '\\r'\n}\n\nfunc (p *parser) isDigitNum(c byte) bool {\n\treturn '0' \u003c= c \u0026\u0026 c \u003c= '9' || c == '.' || c == '_' || c == 'e' || c == '-' || c == '+'\n}\n\nfunc (p *parser) isChar(c byte) bool {\n\treturn 'a' \u003c= c \u0026\u0026 c \u003c= 'z' || 'A' \u003c= c \u0026\u0026 c \u003c= 'Z'\n}\n\nfunc (p *parser) isWordChar(c byte) bool {\n\treturn p.isChar(c) || '0' \u003c= c \u0026\u0026 c \u003c= '9'\n}\n" + }, + { + "name": "int32_test.gno", + "body": "package int32\n\nimport \"testing\"\n\nfunc TestOne(t *testing.T) {\n\tttt := []struct {\n\t\texp string\n\t\tres int\n\t}{\n\t\t{\"1\", 1},\n\t\t{\"--1\", 1},\n\t\t{\"1+2\", 3},\n\t\t{\"-1+2\", 1},\n\t\t{\"-(1+2)\", -3},\n\t\t{\"-(1+2)*5\", -15},\n\t\t{\"-(1+2)*5/3\", -5},\n\t\t{\"1+(-(1+2)*5/3)\", -4},\n\t\t{\"3^4\", 3 ^ 4},\n\t\t{\"8%2\", 8 % 2},\n\t\t{\"8%3\", 8 % 3},\n\t\t{\"8|3\", 8 | 3},\n\t\t{\"10%2\", 0},\n\t\t{\"(4 + 3)/2-1+11*15\", (4+3)/2 - 1 + 11*15},\n\t\t{\n\t\t\t\"(30099\u003e\u003e10^30099\u003e\u003e11)%5*((30099\u003e\u003e14\u00263^30099\u003e\u003e15\u00261)+1)*30099%99 + ((3 + (30099 \u003e\u003e 14 \u0026 3) - (30099 \u003e\u003e 16 \u0026 1)) / 3 * 30099 % 99 \u0026 64)\",\n\t\t\t(30099\u003e\u003e10^30099\u003e\u003e11)%5*((30099\u003e\u003e14\u00263^30099\u003e\u003e15\u00261)+1)*30099%99 + ((3 + (30099 \u003e\u003e 14 \u0026 3) - (30099 \u003e\u003e 16 \u0026 1)) / 3 * 30099 % 99 \u0026 64),\n\t\t},\n\t\t{\n\t\t\t\"(1023850\u003e\u003e10^1023850\u003e\u003e11)%5*((1023850\u003e\u003e14\u00263^1023850\u003e\u003e15\u00261)+1)*1023850%99 + ((3 + (1023850 \u003e\u003e 14 \u0026 3) - (1023850 \u003e\u003e 16 \u0026 1)) / 3 * 1023850 % 99 \u0026 64)\",\n\t\t\t(1023850\u003e\u003e10^1023850\u003e\u003e11)%5*((1023850\u003e\u003e14\u00263^1023850\u003e\u003e15\u00261)+1)*1023850%99 + ((3 + (1023850 \u003e\u003e 14 \u0026 3) - (1023850 \u003e\u003e 16 \u0026 1)) / 3 * 1023850 % 99 \u0026 64),\n\t\t},\n\t\t{\"((0000+1)*0000)\", 0},\n\t}\n\tfor _, tc := range ttt {\n\t\tt.Run(tc.exp, func(t *testing.T) {\n\t\t\texp, err := Parse(tc.exp)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%s:\\n%s\", tc.exp, err.Error())\n\t\t\t} else {\n\t\t\t\tres, errEval := Eval(exp, nil)\n\t\t\t\tif errEval != nil {\n\t\t\t\t\tt.Errorf(\"eval error: %s\", errEval.Error())\n\t\t\t\t} else if res != tc.res {\n\t\t\t\t\tt.Errorf(\"%s:\\nexpected %d, got %d\", tc.exp, tc.res, res)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestVariables(t *testing.T) {\n\tfn := func(x, y int) int {\n\t\treturn 1 + ((x*3+1)*(x*2))\u003e\u003ey + 1\n\t}\n\texpr := \"1 + ((x*3+1)*(x*2))\u003e\u003ey + 1\"\n\texp, err := Parse(expr)\n\tif err != nil {\n\t\tt.Errorf(\"could not parse: %s\", err.Error())\n\t}\n\tvariables := make(map[string]int)\n\tfor i := 0; i \u003c 10; i++ {\n\t\tvariables[\"x\"] = i\n\t\tvariables[\"y\"] = 2\n\t\tres, errEval := Eval(exp, variables)\n\t\tif errEval != nil {\n\t\t\tt.Errorf(\"could not evaluate: %s\", err.Error())\n\t\t}\n\t\texpected := fn(variables[\"x\"], variables[\"y\"])\n\t\tif res != expected {\n\t\t\tt.Errorf(\"expected: %d, actual: %d\", expected, res)\n\t\t}\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "ownable", + "path": "gno.land/p/demo/ownable", + "files": [ + { + "name": "errors.gno", + "body": "package ownable\n\nimport \"errors\"\n\nvar (\n\tErrUnauthorized = errors.New(\"unauthorized; caller is not owner\")\n\tErrInvalidAddress = errors.New(\"new owner address is invalid\")\n)\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/ownable\n\nrequire (\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n)\n" + }, + { + "name": "ownable.gno", + "body": "package ownable\n\nimport (\n\t\"std\"\n)\n\nconst OwnershipTransferEvent = \"OwnershipTransfer\"\n\n// Ownable is meant to be used as a top-level object to make your contract ownable OR\n// being embedded in a Gno object to manage per-object ownership.\ntype Ownable struct {\n\towner std.Address\n}\n\nfunc New() *Ownable {\n\treturn \u0026Ownable{\n\t\towner: std.PrevRealm().Addr(),\n\t}\n}\n\nfunc NewWithAddress(addr std.Address) *Ownable {\n\treturn \u0026Ownable{owner: addr}\n}\n\n// TransferOwnership transfers ownership of the Ownable struct to a new address\nfunc (o *Ownable) TransferOwnership(newOwner std.Address) error {\n\terr := o.CallerIsOwner()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !newOwner.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tprevOwner := o.owner\n\to.owner = newOwner\n\tstd.Emit(\n\t\tOwnershipTransferEvent,\n\t\t\"from\", string(prevOwner),\n\t\t\"to\", string(newOwner),\n\t)\n\treturn nil\n}\n\n// DropOwnership removes the owner, effectively disabling any owner-related actions\n// Top-level usage: disables all only-owner actions/functions,\n// Embedded usage: behaves like a burn functionality, removing the owner from the struct\nfunc (o *Ownable) DropOwnership() error {\n\terr := o.CallerIsOwner()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tprevOwner := o.owner\n\to.owner = \"\"\n\n\tstd.Emit(\n\t\tOwnershipTransferEvent,\n\t\t\"from\", string(prevOwner),\n\t\t\"to\", \"\",\n\t)\n\n\treturn nil\n}\n\nfunc (o Ownable) Owner() std.Address {\n\treturn o.owner\n}\n\n// CallerIsOwner checks if the caller of the function is the Realm's owner\nfunc (o Ownable) CallerIsOwner() error {\n\tif std.PrevRealm().Addr() == o.owner {\n\t\treturn nil\n\t}\n\treturn ErrUnauthorized\n}\n\nfunc (o Ownable) AssertCallerIsOwner() {\n\tif std.PrevRealm().Addr() != o.owner {\n\t\tpanic(ErrUnauthorized)\n\t}\n}\n" + }, + { + "name": "ownable_test.gno", + "body": "package ownable\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/uassert\"\n)\n\nvar (\n\tfirstCaller = testutils.TestAddress(\"first\")\n\tsecondCaller = testutils.TestAddress(\"second\")\n)\n\nfunc TestNew(t *testing.T) {\n\tstd.TestSetRealm(std.NewUserRealm(firstCaller))\n\tstd.TestSetOrigCaller(firstCaller) // TODO(bug): should not be needed\n\n\to := New()\n\tgot := o.Owner()\n\tuassert.Equal(t, firstCaller, got)\n}\n\nfunc TestNewWithAddress(t *testing.T) {\n\to := NewWithAddress(firstCaller)\n\n\tgot := o.Owner()\n\tuassert.Equal(t, firstCaller, got)\n}\n\nfunc TestOwner(t *testing.T) {\n\tstd.TestSetRealm(std.NewUserRealm(firstCaller))\n\n\to := New()\n\texpected := firstCaller\n\tgot := o.Owner()\n\tuassert.Equal(t, expected, got)\n}\n\nfunc TestTransferOwnership(t *testing.T) {\n\tstd.TestSetRealm(std.NewUserRealm(firstCaller))\n\n\to := New()\n\n\terr := o.TransferOwnership(secondCaller)\n\tuassert.NoError(t, err, \"TransferOwnership failed\")\n\n\tgot := o.Owner()\n\tuassert.Equal(t, secondCaller, got)\n}\n\nfunc TestCallerIsOwner(t *testing.T) {\n\tstd.TestSetRealm(std.NewUserRealm(firstCaller))\n\n\to := New()\n\tunauthorizedCaller := secondCaller\n\n\tstd.TestSetRealm(std.NewUserRealm(unauthorizedCaller))\n\tstd.TestSetOrigCaller(unauthorizedCaller) // TODO(bug): should not be needed\n\n\terr := o.CallerIsOwner()\n\tuassert.Error(t, err) // XXX: IsError(..., unauthorizedCaller)\n}\n\nfunc TestDropOwnership(t *testing.T) {\n\tstd.TestSetRealm(std.NewUserRealm(firstCaller))\n\n\to := New()\n\n\terr := o.DropOwnership()\n\tuassert.NoError(t, err, \"DropOwnership failed\")\n\n\towner := o.Owner()\n\tuassert.Empty(t, owner, \"owner should be empty\")\n}\n\n// Errors\n\nfunc TestErrUnauthorized(t *testing.T) {\n\tstd.TestSetRealm(std.NewUserRealm(firstCaller))\n\tstd.TestSetOrigCaller(firstCaller) // TODO(bug): should not be needed\n\n\to := New()\n\n\tstd.TestSetRealm(std.NewUserRealm(secondCaller))\n\tstd.TestSetOrigCaller(secondCaller) // TODO(bug): should not be needed\n\n\terr := o.TransferOwnership(firstCaller)\n\tuassert.ErrorContains(t, err, ErrUnauthorized.Error())\n\n\terr = o.DropOwnership()\n\tuassert.ErrorContains(t, err, ErrUnauthorized.Error())\n}\n\nfunc TestErrInvalidAddress(t *testing.T) {\n\tstd.TestSetRealm(std.NewUserRealm(firstCaller))\n\n\to := New()\n\n\terr := o.TransferOwnership(\"\")\n\tuassert.ErrorContains(t, err, ErrInvalidAddress.Error())\n\n\terr = o.TransferOwnership(\"10000000001000000000100000000010000000001000000000\")\n\tuassert.ErrorContains(t, err, ErrInvalidAddress.Error())\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "seqid", + "path": "gno.land/p/demo/seqid", + "files": [ + { + "name": "README.md", + "body": "# seqid\n\n```\npackage seqid // import \"gno.land/p/demo/seqid\"\n\nPackage seqid provides a simple way to have sequential IDs which will be ordered\ncorrectly when inserted in an AVL tree.\n\nSample usage:\n\n var id seqid.ID\n var users avl.Tree\n\n func NewUser() {\n \tusers.Set(id.Next().Binary(), \u0026User{ ... })\n }\n\nTYPES\n\ntype ID uint64\n An ID is a simple sequential ID generator.\n\nfunc FromBinary(b string) (ID, bool)\n FromBinary creates a new ID from the given string.\n\nfunc (i ID) Binary() string\n Binary returns a big-endian binary representation of the ID, suitable to be\n used as an AVL key.\n\nfunc (i *ID) Next() ID\n Next advances the ID i. It will panic if increasing ID would overflow.\n\nfunc (i *ID) TryNext() (ID, bool)\n TryNext increases i by 1 and returns its value. It returns true if\n successful, or false if the increment would result in an overflow.\n```\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/seqid\n\nrequire gno.land/p/demo/cford32 v0.0.0-latest\n" + }, + { + "name": "seqid.gno", + "body": "// Package seqid provides a simple way to have sequential IDs which will be\n// ordered correctly when inserted in an AVL tree.\n//\n// Sample usage:\n//\n//\tvar id seqid.ID\n//\tvar users avl.Tree\n//\n//\tfunc NewUser() {\n//\t\tusers.Set(id.Next().String(), \u0026User{ ... })\n//\t}\npackage seqid\n\nimport (\n\t\"encoding/binary\"\n\n\t\"gno.land/p/demo/cford32\"\n)\n\n// An ID is a simple sequential ID generator.\ntype ID uint64\n\n// Next advances the ID i.\n// It will panic if increasing ID would overflow.\nfunc (i *ID) Next() ID {\n\tnext, ok := i.TryNext()\n\tif !ok {\n\t\tpanic(\"seqid: next ID overflows uint64\")\n\t}\n\treturn next\n}\n\nconst maxID ID = 1\u003c\u003c64 - 1\n\n// TryNext increases i by 1 and returns its value.\n// It returns true if successful, or false if the increment would result in\n// an overflow.\nfunc (i *ID) TryNext() (ID, bool) {\n\tif *i == maxID {\n\t\t// Addition will overflow.\n\t\treturn 0, false\n\t}\n\t*i++\n\treturn *i, true\n}\n\n// Binary returns a big-endian binary representation of the ID,\n// suitable to be used as an AVL key.\nfunc (i ID) Binary() string {\n\tbuf := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(buf, uint64(i))\n\treturn string(buf)\n}\n\n// String encodes i using cford32's compact encoding. For more information,\n// see the documentation for package [gno.land/p/demo/cford32].\n//\n// The result of String will be a 7-byte string for IDs [0,2^34), and a\n// 13-byte string for all values following that. All generated string IDs\n// follow the same lexicographic order as their number values; that is, for any\n// two IDs (x, y) such that x \u003c y, x.String() \u003c y.String().\n// As such, this string representation is suitable to be used as an AVL key.\nfunc (i ID) String() string {\n\treturn string(cford32.PutCompact(uint64(i)))\n}\n\n// FromBinary creates a new ID from the given string, expected to be a binary\n// big-endian encoding of an ID (such as that of [ID.Binary]).\n// The second return value is true if the conversion was successful.\nfunc FromBinary(b string) (ID, bool) {\n\tif len(b) != 8 {\n\t\treturn 0, false\n\t}\n\treturn ID(binary.BigEndian.Uint64([]byte(b))), true\n}\n\n// FromString creates a new ID from the given string, expected to be a string\n// representation using cford32, such as that returned by [ID.String].\n//\n// The encoding scheme used by cford32 allows the same ID to have many\n// different representations (though the one returned by [ID.String] is only\n// one, deterministic and safe to be used in AVL). The encoding scheme is\n// \"human-centric\" and is thus case insensitive, and maps some ambiguous\n// characters to be the same, ie. L = I = 1, O = 0. For this reason, when\n// parsing user input to retrieve a key (encoded as a string), always sanitize\n// it first using FromString, then run String(), instead of using the user's\n// input directly.\nfunc FromString(b string) (ID, error) {\n\tn, err := cford32.Uint64([]byte(b))\n\treturn ID(n), err\n}\n" + }, + { + "name": "seqid_test.gno", + "body": "package seqid\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestID(t *testing.T) {\n\tvar i ID\n\n\tfor j := 0; j \u003c 100; j++ {\n\t\ti.Next()\n\t}\n\tif i != 100 {\n\t\tt.Fatalf(\"invalid: wanted %d got %d\", 100, i)\n\t}\n}\n\nfunc TestID_Overflow(t *testing.T) {\n\ti := ID(maxID)\n\n\tdefer func() {\n\t\terr := recover()\n\t\tif !strings.Contains(fmt.Sprint(err), \"next ID overflows\") {\n\t\t\tt.Errorf(\"did not overflow\")\n\t\t}\n\t}()\n\n\ti.Next()\n}\n\nfunc TestID_Binary(t *testing.T) {\n\tvar i ID\n\tprev := i.Binary()\n\n\tfor j := 0; j \u003c 1000; j++ {\n\t\tcur := i.Next().Binary()\n\t\tif cur \u003c= prev {\n\t\t\tt.Fatalf(\"cur %x \u003e prev %x\", cur, prev)\n\t\t}\n\t\tprev = cur\n\t}\n}\n\nfunc TestID_String(t *testing.T) {\n\tvar i ID\n\tprev := i.String()\n\n\tfor j := 0; j \u003c 1000; j++ {\n\t\tcur := i.Next().String()\n\t\tif cur \u003c= prev {\n\t\t\tt.Fatalf(\"cur %s \u003e prev %s\", cur, prev)\n\t\t}\n\t\tprev = cur\n\t}\n\n\t// Test for when cford32 switches over to the long encoding.\n\ti = 1\u003c\u003c34 - 512\n\tfor j := 0; j \u003c 1024; j++ {\n\t\tcur := i.Next().String()\n\t\t// println(cur)\n\t\tif cur \u003c= prev {\n\t\t\tt.Fatalf(\"cur %s \u003e prev %s\", cur, prev)\n\t\t}\n\t\tprev = cur\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "memeland", + "path": "gno.land/p/demo/memeland", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/memeland\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/p/demo/seqid v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "memeland.gno", + "body": "package memeland\n\nimport (\n\t\"sort\"\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/p/demo/seqid\"\n)\n\nconst (\n\tDATE_CREATED = \"DATE_CREATED\"\n\tUPVOTES = \"UPVOTES\"\n)\n\ntype Post struct {\n\tID string\n\tData string\n\tAuthor std.Address\n\tTimestamp time.Time\n\tUpvoteTracker *avl.Tree // address \u003e struct{}{}\n}\n\ntype Memeland struct {\n\t*ownable.Ownable\n\tPosts []*Post\n\tMemeCounter seqid.ID\n}\n\nfunc NewMemeland() *Memeland {\n\treturn \u0026Memeland{\n\t\tOwnable: ownable.New(),\n\t\tPosts: make([]*Post, 0),\n\t}\n}\n\n// PostMeme - Adds a new post\nfunc (m *Memeland) PostMeme(data string, timestamp int64) string {\n\tif data == \"\" || timestamp \u003c= 0 {\n\t\tpanic(\"timestamp or data cannot be empty\")\n\t}\n\n\t// Generate ID\n\tid := m.MemeCounter.Next().String()\n\n\tnewPost := \u0026Post{\n\t\tID: id,\n\t\tData: data,\n\t\tAuthor: std.PrevRealm().Addr(),\n\t\tTimestamp: time.Unix(timestamp, 0),\n\t\tUpvoteTracker: avl.NewTree(),\n\t}\n\n\tm.Posts = append(m.Posts, newPost)\n\treturn id\n}\n\nfunc (m *Memeland) Upvote(id string) string {\n\tpost := m.getPost(id)\n\tif post == nil {\n\t\tpanic(\"post with specified ID does not exist\")\n\t}\n\n\tcaller := std.PrevRealm().Addr().String()\n\n\tif _, exists := post.UpvoteTracker.Get(caller); exists {\n\t\tpanic(\"user has already upvoted this post\")\n\t}\n\n\tpost.UpvoteTracker.Set(caller, struct{}{})\n\n\treturn \"upvote successful\"\n}\n\n// GetPostsInRange returns a JSON string of posts within the given timestamp range, supporting pagination\nfunc (m *Memeland) GetPostsInRange(startTimestamp, endTimestamp int64, page, pageSize int, sortBy string) string {\n\tif len(m.Posts) == 0 {\n\t\treturn \"[]\"\n\t}\n\n\tif page \u003c 1 {\n\t\tpanic(\"page number cannot be less than 1\")\n\t}\n\n\t// No empty pages\n\tif pageSize \u003c 1 {\n\t\tpanic(\"page size cannot be less than 1\")\n\t}\n\n\t// No pages larger than 10\n\tif pageSize \u003e 10 {\n\t\tpanic(\"page size cannot be larger than 10\")\n\t}\n\n\t// Need to pass in a sort parameter\n\tif sortBy == \"\" {\n\t\tpanic(\"sort order cannot be empty\")\n\t}\n\n\tvar filteredPosts []*Post\n\n\tstart := time.Unix(startTimestamp, 0)\n\tend := time.Unix(endTimestamp, 0)\n\n\t// Filtering posts\n\tfor _, p := range m.Posts {\n\t\tif !p.Timestamp.Before(start) \u0026\u0026 !p.Timestamp.After(end) {\n\t\t\tfilteredPosts = append(filteredPosts, p)\n\t\t}\n\t}\n\n\tswitch sortBy {\n\t// Sort by upvote descending\n\tcase UPVOTES:\n\t\tdateSorter := PostSorter{\n\t\t\tPosts: filteredPosts,\n\t\t\tLessF: func(i, j int) bool {\n\t\t\t\treturn filteredPosts[i].UpvoteTracker.Size() \u003e filteredPosts[j].UpvoteTracker.Size()\n\t\t\t},\n\t\t}\n\t\tsort.Sort(dateSorter)\n\tcase DATE_CREATED:\n\t\t// Sort by timestamp, beginning with newest\n\t\tdateSorter := PostSorter{\n\t\t\tPosts: filteredPosts,\n\t\t\tLessF: func(i, j int) bool {\n\t\t\t\treturn filteredPosts[i].Timestamp.After(filteredPosts[j].Timestamp)\n\t\t\t},\n\t\t}\n\t\tsort.Sort(dateSorter)\n\tdefault:\n\t\tpanic(\"sort order can only be \\\"UPVOTES\\\" or \\\"DATE_CREATED\\\"\")\n\t}\n\n\t// Pagination\n\tstartIndex := (page - 1) * pageSize\n\tendIndex := startIndex + pageSize\n\n\t// If page does not contain any posts\n\tif startIndex \u003e= len(filteredPosts) {\n\t\treturn \"[]\"\n\t}\n\n\t// If page contains fewer posts than the page size\n\tif endIndex \u003e len(filteredPosts) {\n\t\tendIndex = len(filteredPosts)\n\t}\n\n\t// Return JSON representation of paginated and sorted posts\n\treturn PostsToJSONString(filteredPosts[startIndex:endIndex])\n}\n\n// RemovePost allows the owner to remove a post with a specific ID\nfunc (m *Memeland) RemovePost(id string) string {\n\tif id == \"\" {\n\t\tpanic(\"id cannot be empty\")\n\t}\n\n\tif err := m.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor i, post := range m.Posts {\n\t\tif post.ID == id {\n\t\t\tm.Posts = append(m.Posts[:i], m.Posts[i+1:]...)\n\t\t\treturn id\n\t\t}\n\t}\n\n\tpanic(\"post with specified id does not exist\")\n}\n\n// PostsToJSONString converts a slice of Post structs into a JSON string\nfunc PostsToJSONString(posts []*Post) string {\n\tvar sb strings.Builder\n\tsb.WriteString(\"[\")\n\n\tfor i, post := range posts {\n\t\tif i \u003e 0 {\n\t\t\tsb.WriteString(\",\")\n\t\t}\n\n\t\tsb.WriteString(PostToJSONString(post))\n\t}\n\tsb.WriteString(\"]\")\n\n\treturn sb.String()\n}\n\n// PostToJSONString returns a Post formatted as a JSON string\nfunc PostToJSONString(post *Post) string {\n\tvar sb strings.Builder\n\n\tsb.WriteString(\"{\")\n\tsb.WriteString(`\"id\":\"` + post.ID + `\",`)\n\tsb.WriteString(`\"data\":\"` + escapeString(post.Data) + `\",`)\n\tsb.WriteString(`\"author\":\"` + escapeString(post.Author.String()) + `\",`)\n\tsb.WriteString(`\"timestamp\":\"` + strconv.Itoa(int(post.Timestamp.Unix())) + `\",`)\n\tsb.WriteString(`\"upvotes\":` + strconv.Itoa(post.UpvoteTracker.Size()))\n\tsb.WriteString(\"}\")\n\n\treturn sb.String()\n}\n\n// escapeString escapes quotes in a string for JSON compatibility.\nfunc escapeString(s string) string {\n\treturn strings.ReplaceAll(s, `\"`, `\\\"`)\n}\n\nfunc (m *Memeland) getPost(id string) *Post {\n\tfor _, p := range m.Posts {\n\t\tif p.ID == id {\n\t\t\treturn p\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// PostSorter is a flexible sorter for the *Post slice\ntype PostSorter struct {\n\tPosts []*Post\n\tLessF func(i, j int) bool\n}\n\nfunc (p PostSorter) Len() int {\n\treturn len(p.Posts)\n}\n\nfunc (p PostSorter) Swap(i, j int) {\n\tp.Posts[i], p.Posts[j] = p.Posts[j], p.Posts[i]\n}\n\nfunc (p PostSorter) Less(i, j int) bool {\n\treturn p.LessF(i, j)\n}\n" + }, + { + "name": "memeland_test.gno", + "body": "package memeland\n\nimport (\n\t\"std\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gno.land/p/demo/seqid\"\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nfunc TestPostMeme(t *testing.T) {\n\tm := NewMemeland()\n\tid := m.PostMeme(\"Test meme data\", time.Now().Unix())\n\tif id == \"\" {\n\t\tt.Error(\"Expected valid ID, got empty string\")\n\t}\n}\n\nfunc TestGetPostsInRangePagination(t *testing.T) {\n\tm := NewMemeland()\n\tnow := time.Now()\n\n\tnumOfPosts := 5\n\tvar memeData []string\n\tfor i := 1; i \u003c= numOfPosts; i++ {\n\t\t// Prepare meme data\n\t\tnextTime := now.Add(time.Duration(i) * time.Minute)\n\t\tdata := ufmt.Sprintf(\"Meme #%d\", i)\n\t\tmemeData = append(memeData, data)\n\n\t\tm.PostMeme(data, nextTime.Unix())\n\t}\n\n\t// Get timestamps\n\tbeforeEarliest := now.Add(-1 * time.Minute)\n\tafterLatest := now.Add(time.Duration(numOfPosts)*time.Minute + time.Minute)\n\n\ttestCases := []struct {\n\t\tpage int\n\t\tpageSize int\n\t\texpectedNumOfPosts int\n\t}{\n\t\t{page: 1, pageSize: 1, expectedNumOfPosts: 1}, // one per page\n\t\t{page: 2, pageSize: 1, expectedNumOfPosts: 1}, // one on second page\n\t\t{page: 1, pageSize: numOfPosts, expectedNumOfPosts: numOfPosts}, // all posts on single page\n\t\t{page: 12, pageSize: 1, expectedNumOfPosts: 0}, // empty page\n\t\t{page: 1, pageSize: numOfPosts + 1, expectedNumOfPosts: numOfPosts}, // page with fewer posts than its size\n\t\t{page: 5, pageSize: numOfPosts / 5, expectedNumOfPosts: 1}, // evenly distribute posts per page\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(ufmt.Sprintf(\"Page%d_Size%d\", tc.page, tc.pageSize), func(t *testing.T) {\n\t\t\tresult := m.GetPostsInRange(beforeEarliest.Unix(), afterLatest.Unix(), tc.page, tc.pageSize, \"DATE_CREATED\")\n\n\t\t\t// Count posts by how many times id: shows up in JSON string\n\t\t\tpostCount := strings.Count(result, `\"id\":\"`)\n\t\t\tif postCount != tc.expectedNumOfPosts {\n\t\t\t\tt.Errorf(\"Expected %d posts in the JSON string, but found %d\", tc.expectedNumOfPosts, postCount)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetPostsInRangeByTimestamp(t *testing.T) {\n\tm := NewMemeland()\n\tnow := time.Now()\n\n\tnumOfPosts := 5\n\tvar memeData []string\n\tfor i := 1; i \u003c= numOfPosts; i++ {\n\t\t// Prepare meme data\n\t\tnextTime := now.Add(time.Duration(i) * time.Minute)\n\t\tdata := ufmt.Sprintf(\"Meme #%d\", i)\n\t\tmemeData = append(memeData, data)\n\n\t\tm.PostMeme(data, nextTime.Unix())\n\t}\n\n\t// Get timestamps\n\tbeforeEarliest := now.Add(-1 * time.Minute)\n\tafterLatest := now.Add(time.Duration(numOfPosts)*time.Minute + time.Minute)\n\n\t// Default sort is by addition order/timestamp\n\tjsonStr := m.GetPostsInRange(\n\t\tbeforeEarliest.Unix(), // start at earliest post\n\t\tafterLatest.Unix(), // end at latest post\n\t\t1, // first page\n\t\tnumOfPosts, // all memes on the page\n\t\t\"DATE_CREATED\", // sort by newest first\n\t)\n\n\tif jsonStr == \"\" {\n\t\tt.Error(\"Expected non-empty JSON string, got empty string\")\n\t}\n\n\t// Count the number of posts returned in the JSON string as a rudimentary check for correct pagination/filtering\n\tpostCount := strings.Count(jsonStr, `\"id\":\"`)\n\tif seqid.ID(postCount) != m.MemeCounter {\n\t\tt.Errorf(\"Expected %d posts in the JSON string, but found %d\", m.MemeCounter, postCount)\n\t}\n\n\t// Check if data is there\n\tfor _, expData := range memeData {\n\t\tif !strings.Contains(jsonStr, expData) {\n\t\t\tt.Errorf(\"Expected %s in the JSON string, but counld't find it\", expData)\n\t\t}\n\t}\n\n\t// Check if ordering is correct, sort by created date\n\tfor i := 0; i \u003c len(memeData)-2; i++ {\n\t\tif strings.Index(jsonStr, memeData[i]) \u003c strings.Index(jsonStr, memeData[i+1]) {\n\t\t\tt.Errorf(\"Expected %s to be before %s, but was at %d, and %d\", memeData[i], memeData[i+1], i, i+1)\n\t\t}\n\t}\n}\n\nfunc TestGetPostsInRangeByUpvote(t *testing.T) {\n\tm := NewMemeland()\n\tnow := time.Now()\n\n\tmemeData1 := \"Meme #1\"\n\tmemeData2 := \"Meme #2\"\n\n\t// Create posts at specific times for testing\n\tid1 := m.PostMeme(memeData1, now.Unix())\n\tid2 := m.PostMeme(memeData2, now.Add(time.Minute).Unix())\n\n\tm.Upvote(id1)\n\tm.Upvote(id2)\n\n\t// Change caller so avoid double upvote panic\n\tstd.TestSetOrigCaller(testutils.TestAddress(\"alice\"))\n\tm.Upvote(id1)\n\n\t// Final upvote count:\n\t// Meme #1 - 2 upvote\n\t// Meme #2 - 1 upvotes\n\n\t// Get timestamps\n\tbeforeEarliest := now.Add(-time.Minute)\n\tafterLatest := now.Add(time.Hour)\n\n\t// Default sort is by addition order/timestamp\n\tjsonStr := m.GetPostsInRange(\n\t\tbeforeEarliest.Unix(), // start at earliest post\n\t\tafterLatest.Unix(), // end at latest post\n\t\t1, // first page\n\t\t2, // all memes on the page\n\t\t\"UPVOTES\", // sort by upvote\n\t)\n\n\tif jsonStr == \"\" {\n\t\tt.Error(\"Expected non-empty JSON string, got empty string\")\n\t}\n\n\t// Count the number of posts returned in the JSON string as a rudimentary check for correct pagination/filtering\n\tpostCount := strings.Count(jsonStr, `\"id\":\"`)\n\tif seqid.ID(postCount) != m.MemeCounter {\n\t\tt.Errorf(\"Expected %d posts in the JSON string, but found %d\", m.MemeCounter, postCount)\n\t}\n\n\t// Check if ordering is correct\n\tif strings.Index(jsonStr, \"Meme #1\") \u003e strings.Index(jsonStr, \"Meme #2\") {\n\t\tt.Errorf(\"Expected %s to be before %s\", memeData1, memeData2)\n\t}\n}\n\nfunc TestBadSortBy(t *testing.T) {\n\tm := NewMemeland()\n\tnow := time.Now()\n\n\tnumOfPosts := 5\n\tvar memeData []string\n\tfor i := 1; i \u003c= numOfPosts; i++ {\n\t\t// Prepare meme data\n\t\tnextTime := now.Add(time.Duration(i) * time.Minute)\n\t\tdata := ufmt.Sprintf(\"Meme #%d\", i)\n\t\tmemeData = append(memeData, data)\n\n\t\tm.PostMeme(data, nextTime.Unix())\n\t}\n\n\t// Get timestamps\n\tbeforeEarliest := now.Add(-1 * time.Minute)\n\tafterLatest := now.Add(time.Duration(numOfPosts)*time.Minute + time.Minute)\n\n\ttests := []struct {\n\t\tname string\n\t\tsortBy string\n\t\twantPanic string\n\t}{\n\t\t{\n\t\t\tname: \"Empty sortBy\",\n\t\t\tsortBy: \"\",\n\t\t\twantPanic: \"runtime error: index out of range\",\n\t\t},\n\t\t{\n\t\t\tname: \"Wrong sortBy\",\n\t\t\tsortBy: \"random string\",\n\t\t\twantPanic: \"\",\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r == nil {\n\t\t\t\t\tt.Errorf(\"code did not panic when it should have\")\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\t// Panics should be caught\n\t\t\t_ = m.GetPostsInRange(beforeEarliest.Unix(), afterLatest.Unix(), 1, 1, tc.sortBy)\n\t\t})\n\t}\n}\n\nfunc TestNoPosts(t *testing.T) {\n\tm := NewMemeland()\n\n\t// Add a post to Memeland\n\tnow := time.Now().Unix()\n\n\tjsonStr := m.GetPostsInRange(0, now, 1, 1, \"DATE_CREATED\")\n\n\tif jsonStr != \"[]\" {\n\t\tt.Errorf(\"Expected 0 posts to return [], got %s\", jsonStr)\n\t}\n}\n\nfunc TestUpvote(t *testing.T) {\n\tm := NewMemeland()\n\n\t// Add a post to Memeland\n\tnow := time.Now().Unix()\n\tpostID := m.PostMeme(\"Test meme data\", now)\n\n\t// Initial upvote count should be 0\n\tpost := m.getPost(postID)\n\n\tif post.UpvoteTracker.Size() != 0 {\n\t\tt.Errorf(\"Expected initial upvotes to be 0, got %d\", post.UpvoteTracker.Size())\n\t}\n\n\t// Upvote the post\n\tupvoteResult := m.Upvote(postID)\n\tif upvoteResult != \"upvote successful\" {\n\t\tt.Errorf(\"Expected upvote to be successful, got: %s\", upvoteResult)\n\t}\n\n\t// Retrieve the post again and check the upvote count\n\tpost = m.getPost(postID)\n\n\tif post.UpvoteTracker.Size() != 1 {\n\t\tt.Errorf(\"Expected upvotes to be 1 after upvoting, got %d\", post.UpvoteTracker.Size())\n\t}\n}\n\nfunc TestDelete(t *testing.T) {\n\talice := testutils.TestAddress(\"alice\")\n\tstd.TestSetOrigCaller(alice)\n\n\t// Alice is admin\n\tm := NewMemeland()\n\n\t// Set caller to Bob\n\tbob := testutils.TestAddress(\"bob\")\n\tstd.TestSetOrigCaller(bob)\n\n\t// Bob adds post to Memeland\n\tnow := time.Now()\n\tpostID := m.PostMeme(\"Meme #1\", now.Unix())\n\n\t// Alice removes Bob's post\n\tstd.TestSetOrigCaller(alice)\n\n\tid := m.RemovePost(postID)\n\tif id != postID {\n\t\tt.Errorf(\"post IDs not matching\")\n\t}\n\n\tif len(m.Posts) != 0 {\n\t\tt.Errorf(\"there should be 0 posts after removing\")\n\t}\n}\n\nfunc TestDeleteByNonAdmin(t *testing.T) {\n\talice := testutils.TestAddress(\"alice\")\n\tstd.TestSetOrigCaller(alice)\n\n\tm := NewMemeland()\n\n\t// Add a post to Memeland\n\tnow := time.Now()\n\tpostID := m.PostMeme(\"Meme #1\", now.Unix())\n\n\t// Bob will try to delete meme posted by Alice, which should fail\n\tbob := testutils.TestAddress(\"bob\")\n\tstd.TestSetOrigCaller(bob)\n\n\tdefer func() {\n\t\tif r := recover(); r == nil {\n\t\t\tt.Errorf(\"code did not panic when it should have\")\n\t\t}\n\t}()\n\n\t// Should panic - caught by defer\n\tm.RemovePost(postID)\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "merkle", + "path": "gno.land/p/demo/merkle", + "files": [ + { + "name": "README.md", + "body": "# p/demo/merkle\n\nThis package implement a merkle tree that is complient with [merkletreejs](https://github.com/merkletreejs/merkletreejs)\n\n## [merkletreejs](https://github.com/merkletreejs/merkletreejs)\n\n```javascript\nconst { MerkleTree } = require(\"merkletreejs\");\nconst SHA256 = require(\"crypto-js/sha256\");\n\nlet leaves = [];\nfor (let i = 0; i \u003c 10; i++) {\n leaves.push(SHA256(`node_${i}`));\n}\n\nconst tree = new MerkleTree(leaves, SHA256);\nconst root = tree.getRoot().toString(\"hex\");\n\nconsole.log(root); // cd8a40502b0b92bf58e7432a5abb2d8b60121cf2b7966d6ebaf103f907a1bc21\n```\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/merkle\n" + }, + { + "name": "merkle.gno", + "body": "package merkle\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"errors\"\n)\n\ntype Hashable interface {\n\tBytes() []byte\n}\n\ntype nodes []Node\n\ntype Node struct {\n\thash []byte\n\n\tposition uint8\n}\n\nfunc (n Node) Hash() string {\n\treturn hex.EncodeToString(n.hash[:])\n}\n\ntype Tree struct {\n\tlayers []nodes\n}\n\n// Root return the merkle root of the tree\nfunc (t *Tree) Root() string {\n\tfor _, l := range t.layers {\n\t\tif len(l) == 1 {\n\t\t\treturn l[0].Hash()\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// NewTree create a new Merkle Tree\nfunc NewTree(data []Hashable) *Tree {\n\ttree := \u0026Tree{}\n\n\tleaves := make([]Node, len(data))\n\n\tfor i, d := range data {\n\t\thash := sha256.Sum256(d.Bytes())\n\t\tleaves[i] = Node{hash: hash[:]}\n\t}\n\n\ttree.layers = []nodes{nodes(leaves)}\n\n\tvar buff bytes.Buffer\n\tfor len(leaves) \u003e 1 {\n\t\tlevel := make([]Node, 0, len(leaves)/2+1)\n\t\tfor i := 0; i \u003c len(leaves); i += 2 {\n\t\t\tbuff.Reset()\n\n\t\t\tif i \u003c len(leaves)-1 {\n\t\t\t\tbuff.Write(leaves[i].hash)\n\t\t\t\tbuff.Write(leaves[i+1].hash)\n\t\t\t\thash := sha256.Sum256(buff.Bytes())\n\t\t\t\tlevel = append(level, Node{\n\t\t\t\t\thash: hash[:],\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlevel = append(level, leaves[i])\n\t\t\t}\n\t\t}\n\t\tleaves = level\n\t\ttree.layers = append(tree.layers, level)\n\t}\n\treturn tree\n}\n\n// Proof return a MerkleProof\nfunc (t *Tree) Proof(data Hashable) ([]Node, error) {\n\ttargetHash := sha256.Sum256(data.Bytes())\n\ttargetIndex := -1\n\n\tfor i, layer := range t.layers[0] {\n\t\tif bytes.Equal(targetHash[:], layer.hash) {\n\t\t\ttargetIndex = i\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif targetIndex == -1 {\n\t\treturn nil, errors.New(\"target not found\")\n\t}\n\n\tproofs := make([]Node, 0, len(t.layers))\n\n\tfor _, layer := range t.layers {\n\t\tvar pairIndex int\n\n\t\tif targetIndex%2 == 0 {\n\t\t\tpairIndex = targetIndex + 1\n\t\t} else {\n\t\t\tpairIndex = targetIndex - 1\n\t\t}\n\t\tif pairIndex \u003c len(layer) {\n\t\t\tproofs = append(proofs, Node{\n\t\t\t\thash: layer[pairIndex].hash,\n\t\t\t\tposition: uint8(targetIndex) % 2,\n\t\t\t})\n\t\t}\n\t\ttargetIndex /= 2\n\t}\n\treturn proofs, nil\n}\n\n// Verify if a merkle proof is valid\nfunc (t *Tree) Verify(leaf Hashable, proofs []Node) bool {\n\treturn Verify(t.Root(), leaf, proofs)\n}\n\n// Verify if a merkle proof is valid\nfunc Verify(root string, leaf Hashable, proofs []Node) bool {\n\thash := sha256.Sum256(leaf.Bytes())\n\n\tfor i := 0; i \u003c len(proofs); i += 1 {\n\t\tvar h []byte\n\t\tif proofs[i].position == 0 {\n\t\t\th = append(hash[:], proofs[i].hash...)\n\t\t} else {\n\t\t\th = append(proofs[i].hash, hash[:]...)\n\t\t}\n\t\thash = sha256.Sum256(h)\n\t}\n\treturn hex.EncodeToString(hash[:]) == root\n}\n" + }, + { + "name": "merkle_test.gno", + "body": "package merkle\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype testData struct {\n\tcontent string\n}\n\nfunc (d testData) Bytes() []byte {\n\treturn []byte(d.content)\n}\n\nfunc TestMerkleTree(t *testing.T) {\n\ttests := []struct {\n\t\tsize int\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tsize: 1,\n\t\t\texpected: \"cf9f824bce7f5bc63d557b23591f58577f53fe29f974a615bdddbd0140f912f4\",\n\t\t},\n\t\t{\n\t\t\tsize: 3,\n\t\t\texpected: \"1a4a5f0fa267244bf9f74a63fdf2a87eed5e97e4bd104a9e94728c8fb5442177\",\n\t\t},\n\t\t{\n\t\t\tsize: 10,\n\t\t\texpected: \"cd8a40502b0b92bf58e7432a5abb2d8b60121cf2b7966d6ebaf103f907a1bc21\",\n\t\t},\n\t\t{\n\t\t\tsize: 1000,\n\t\t\texpected: \"fa533d2efdf12be26bc410dfa42936ac63361324e35e9b1ff54d422a1dd2388b\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tvar leaves []Hashable\n\t\tfor i := 0; i \u003c test.size; i++ {\n\t\t\tleaves = append(leaves, testData{fmt.Sprintf(\"node_%d\", i)})\n\t\t}\n\n\t\ttree := NewTree(leaves)\n\n\t\tif tree == nil {\n\t\t\tt.Error(\"Merkle tree creation failed\")\n\t\t}\n\n\t\troot := tree.Root()\n\n\t\tif root != test.expected {\n\t\t\tt.Fatalf(\"merkle.Tree.Root(), expected: %s; got: %s\", test.expected, root)\n\t\t}\n\n\t\tfor _, leaf := range leaves {\n\t\t\tproofs, err := tree.Proof(leaf)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(\"failed to proof leaf: %v, on tree: %v\", leaf, test)\n\t\t\t}\n\n\t\t\tok := Verify(root, leaf, proofs)\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"failed to verify leaf: %v, on tree: %v\", leaf, tree)\n\t\t\t}\n\t\t}\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "microblog", + "path": "gno.land/p/demo/microblog", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/microblog\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "microblog.gno", + "body": "package microblog\n\nimport (\n\t\"errors\"\n\t\"sort\"\n\t\"std\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar (\n\tErrNotFound = errors.New(\"not found\")\n\tStatusNotFound = \"404\"\n)\n\ntype Microblog struct {\n\tTitle string\n\tPrefix string // i.e. r/gnoland/blog:\n\tPages avl.Tree // author (string) -\u003e Page\n}\n\nfunc NewMicroblog(title string, prefix string) (m *Microblog) {\n\treturn \u0026Microblog{\n\t\tTitle: title,\n\t\tPrefix: prefix,\n\t\tPages: avl.Tree{},\n\t}\n}\n\nfunc (m *Microblog) GetPages() []*Page {\n\tvar (\n\t\tpages = make([]*Page, m.Pages.Size())\n\t\tindex = 0\n\t)\n\n\tm.Pages.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tpages[index] = value.(*Page)\n\t\tindex++\n\t\treturn false\n\t})\n\n\tsort.Sort(byLastPosted(pages))\n\n\treturn pages\n}\n\nfunc (m *Microblog) NewPost(text string) error {\n\tauthor := std.GetOrigCaller()\n\t_, found := m.Pages.Get(author.String())\n\tif !found {\n\t\t// make a new page for the new author\n\t\tm.Pages.Set(author.String(), \u0026Page{\n\t\t\tAuthor: author,\n\t\t\tCreatedAt: time.Now(),\n\t\t})\n\t}\n\n\tpage, err := m.GetPage(author.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn page.NewPost(text)\n}\n\nfunc (m *Microblog) GetPage(author string) (*Page, error) {\n\tsilo, found := m.Pages.Get(author)\n\tif !found {\n\t\treturn nil, ErrNotFound\n\t}\n\treturn silo.(*Page), nil\n}\n\ntype Page struct {\n\tID int\n\tAuthor std.Address\n\tCreatedAt time.Time\n\tLastPosted time.Time\n\tPosts avl.Tree // time -\u003e Post\n}\n\n// byLastPosted implements sort.Interface for []Page based on\n// the LastPosted field.\ntype byLastPosted []*Page\n\nfunc (a byLastPosted) Len() int { return len(a) }\nfunc (a byLastPosted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }\nfunc (a byLastPosted) Less(i, j int) bool { return a[i].LastPosted.After(a[j].LastPosted) }\n\nfunc (p *Page) NewPost(text string) error {\n\tnow := time.Now()\n\tp.LastPosted = now\n\tp.Posts.Set(ufmt.Sprintf(\"%s%d\", now.Format(time.RFC3339), p.Posts.Size()), \u0026Post{\n\t\tID: p.Posts.Size(),\n\t\tText: text,\n\t\tCreatedAt: now,\n\t})\n\treturn nil\n}\n\nfunc (p *Page) GetPosts() []*Post {\n\tposts := make([]*Post, p.Posts.Size())\n\ti := 0\n\tp.Posts.ReverseIterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tpostParsed := value.(*Post)\n\t\tposts[i] = postParsed\n\t\ti++\n\t\treturn false\n\t})\n\treturn posts\n}\n\n// Post lists the specific update\ntype Post struct {\n\tID int\n\tCreatedAt time.Time\n\tText string\n}\n\nfunc (p *Post) String() string {\n\treturn \"\u003e \" + strings.ReplaceAll(p.Text, \"\\n\", \"\\n\u003e\\n\u003e\") + \"\\n\u003e\\n\u003e *\" + p.CreatedAt.Format(time.RFC1123) + \"*\"\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "pausable", + "path": "gno.land/p/demo/pausable", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/pausable\n\nrequire gno.land/p/demo/ownable v0.0.0-latest\n" + }, + { + "name": "pausable.gno", + "body": "package pausable\n\nimport \"gno.land/p/demo/ownable\"\n\ntype Pausable struct {\n\t*ownable.Ownable\n\tpaused bool\n}\n\n// New returns a new Pausable struct with non-paused state as default\nfunc New() *Pausable {\n\treturn \u0026Pausable{\n\t\tOwnable: ownable.New(),\n\t\tpaused: false,\n\t}\n}\n\n// NewFromOwnable is the same as New, but with a pre-existing top-level ownable\nfunc NewFromOwnable(ownable *ownable.Ownable) *Pausable {\n\treturn \u0026Pausable{\n\t\tOwnable: ownable,\n\t\tpaused: false,\n\t}\n}\n\n// IsPaused checks if Pausable is paused\nfunc (p Pausable) IsPaused() bool {\n\treturn p.paused\n}\n\n// Pause sets the state of Pausable to true, meaning all pausable functions are paused\nfunc (p *Pausable) Pause() error {\n\tif err := p.CallerIsOwner(); err != nil {\n\t\treturn err\n\t}\n\n\tp.paused = true\n\treturn nil\n}\n\n// Unpause sets the state of Pausable to false, meaning all pausable functions are resumed\nfunc (p *Pausable) Unpause() error {\n\tif err := p.CallerIsOwner(); err != nil {\n\t\treturn err\n\t}\n\n\tp.paused = false\n\treturn nil\n}\n" + }, + { + "name": "pausable_test.gno", + "body": "package pausable\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/ownable\"\n)\n\nvar (\n\tfirstCaller = std.Address(\"g1l9aypkr8xfvs82zeux486ddzec88ty69lue9de\")\n\tsecondCaller = std.Address(\"g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa\")\n)\n\nfunc TestNew(t *testing.T) {\n\tstd.TestSetOrigCaller(firstCaller)\n\n\tresult := New()\n\n\tif result.paused != false {\n\t\tt.Fatalf(\"Expected result to be unpaused, got %t\\n\", result.paused)\n\t}\n\n\tif result.Owner() != firstCaller {\n\t\tt.Fatalf(\"Expected %s, got %s\\n\", firstCaller, result.Owner())\n\t}\n}\n\nfunc TestNewFromOwnable(t *testing.T) {\n\tstd.TestSetOrigCaller(firstCaller)\n\to := ownable.New()\n\n\tstd.TestSetOrigCaller(secondCaller)\n\tresult := NewFromOwnable(o)\n\n\tif result.Owner() != firstCaller {\n\t\tt.Fatalf(\"Expected %s, got %s\\n\", firstCaller, result.Owner())\n\t}\n}\n\nfunc TestSetUnpaused(t *testing.T) {\n\tstd.TestSetOrigCaller(firstCaller)\n\n\tresult := New()\n\tresult.Unpause()\n\n\tif result.IsPaused() {\n\t\tt.Fatalf(\"Expected result to be unpaused, got %t\\n\", result.IsPaused())\n\t}\n}\n\nfunc TestSetPaused(t *testing.T) {\n\tstd.TestSetOrigCaller(firstCaller)\n\n\tresult := New()\n\tresult.Pause()\n\n\tif !result.IsPaused() {\n\t\tt.Fatalf(\"Expected result to be paused, got %t\\n\", result.IsPaused())\n\t}\n}\n\nfunc TestIsPaused(t *testing.T) {\n\tstd.TestSetOrigCaller(firstCaller)\n\n\tresult := New()\n\n\tif result.IsPaused() {\n\t\tt.Fatalf(\"Expected result to be unpaused, got %t\\n\", result.IsPaused())\n\t}\n\n\tresult.Pause()\n\n\tif !result.IsPaused() {\n\t\tt.Fatalf(\"Expected result to be paused, got %t\\n\", result.IsPaused())\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "releases", + "path": "gno.land/p/demo/releases", + "files": [ + { + "name": "changelog.gno", + "body": "package releases\n\ntype changelog struct {\n\tname string\n\treleases []release\n}\n\nfunc NewChangelog(name string) *changelog {\n\treturn \u0026changelog{\n\t\tname: name,\n\t\treleases: make([]release, 0),\n\t}\n}\n\nfunc (c *changelog) NewRelease(version, url, notes string) {\n\tif latest := c.Latest(); latest != nil {\n\t\tlatest.isLatest = false\n\t}\n\n\trelease := release{\n\t\t// manual\n\t\tversion: version,\n\t\turl: url,\n\t\tnotes: notes,\n\n\t\t// internal\n\t\tchangelog: c,\n\t\tisLatest: true,\n\t}\n\n\tc.releases = append(c.releases, release)\n}\n\nfunc (c *changelog) Render(path string) string {\n\tif path == \"\" {\n\t\toutput := \"# \" + c.name + \"\\n\\n\"\n\t\tmax := len(c.releases) - 1\n\t\tmin := 0\n\t\tif max-min \u003e 10 {\n\t\t\tmin = max - 10\n\t\t}\n\t\tfor i := max; i \u003e= min; i-- {\n\t\t\trelease := c.releases[i]\n\t\t\toutput += release.Render()\n\t\t}\n\t\treturn output\n\t}\n\n\trelease := c.ByVersion(path)\n\tif release != nil {\n\t\treturn release.Render()\n\t}\n\n\treturn \"no such release\"\n}\n\nfunc (c *changelog) Latest() *release {\n\tif len(c.releases) \u003e 0 {\n\t\tpos := len(c.releases) - 1\n\t\treturn \u0026c.releases[pos]\n\t}\n\treturn nil\n}\n\nfunc (c *changelog) ByVersion(version string) *release {\n\tfor _, release := range c.releases {\n\t\tif release.version == version {\n\t\t\treturn \u0026release\n\t\t}\n\t}\n\treturn nil\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/releases\n" + }, + { + "name": "release.gno", + "body": "package releases\n\ntype release struct {\n\t// manual\n\tversion string\n\turl string\n\tnotes string\n\n\t// internal\n\tisLatest bool\n\tchangelog *changelog\n}\n\nfunc (r *release) URL() string { return r.url }\nfunc (r *release) Version() string { return r.version }\nfunc (r *release) Notes() string { return r.notes }\nfunc (r *release) IsLatest() bool { return r.isLatest }\n\nfunc (r *release) Title() string {\n\toutput := r.changelog.name + \" \" + r.version\n\tif r.isLatest {\n\t\toutput += \" (latest)\"\n\t}\n\treturn output\n}\n\nfunc (r *release) Link() string {\n\treturn \"[\" + r.Title() + \"](\" + r.url + \")\"\n}\n\nfunc (r *release) Render() string {\n\toutput := \"\"\n\toutput += \"## \" + r.Link() + \"\\n\\n\"\n\tif r.notes != \"\" {\n\t\toutput += r.notes + \"\\n\\n\"\n\t}\n\treturn output\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "stack", + "path": "gno.land/p/demo/stack", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/stack\n" + }, + { + "name": "stack.gno", + "body": "package stack\n\ntype Stack struct {\n\ttop *node\n\tlength int\n}\n\ntype node struct {\n\tvalue interface{}\n\tprev *node\n}\n\nfunc New() *Stack {\n\treturn \u0026Stack{nil, 0}\n}\n\nfunc (s *Stack) Len() int {\n\treturn s.length\n}\n\nfunc (s *Stack) Top() interface{} {\n\tif s.length == 0 {\n\t\treturn nil\n\t}\n\treturn s.top.value\n}\n\nfunc (s *Stack) Pop() interface{} {\n\tif s.length == 0 {\n\t\treturn nil\n\t}\n\n\tnode := s.top\n\ts.top = node.prev\n\ts.length -= 1\n\treturn node.value\n}\n\nfunc (s *Stack) Push(value interface{}) {\n\tnode := \u0026node{value, s.top}\n\ts.top = node\n\ts.length += 1\n}\n" + }, + { + "name": "stack_test.gno", + "body": "package stack\n\nimport \"testing\"\n\nfunc TestStack(t *testing.T) {\n\ts := New() // Empty stack\n\n\tif s.Len() != 0 {\n\t\tt.Errorf(\"s.Len(): expected 0; got %d\", s.Len())\n\t}\n\n\ts.Push(1)\n\n\tif s.Len() != 1 {\n\t\tt.Errorf(\"s.Len(): expected 1; got %d\", s.Len())\n\t}\n\n\tif top := s.Top(); top.(int) != 1 {\n\t\tt.Errorf(\"s.Top(): expected 1; got %v\", top.(int))\n\t}\n\n\tif elem := s.Pop(); elem.(int) != 1 {\n\t\tt.Errorf(\"s.Pop(): expected 1; got %v\", elem.(int))\n\t}\n\tif s.Len() != 0 {\n\t\tt.Errorf(\"s.Len(): expected 0; got %d\", s.Len())\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "svg", + "path": "gno.land/p/demo/svg", + "files": [ + { + "name": "doc.gno", + "body": "/*\nPackage svg is a minimalist SVG generation library for Gno.\n\nThe svg package provides a simple and lightweight solution for programmatically generating SVG (Scalable Vector Graphics) markup in Gno. It allows you to create basic shapes like rectangles and circles, and output the generated SVG to a\n\nExample:\n\n\timport \"gno.land/p/demo/svg\"\"\n\n\tfunc Foo() string {\n\t canvas := svg.Canvas{Width: 200, Height: 200}\n\t canvas.DrawRectangle(50, 50, 100, 100, \"red\")\n\t canvas.DrawCircle(100, 100, 50, \"blue\")\n\t return canvas.String()\n\t}\n*/\npackage svg // import \"gno.land/p/demo/svg\"\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/svg\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" + }, + { + "name": "svg.gno", + "body": "package svg\n\nimport \"gno.land/p/demo/ufmt\"\n\ntype Canvas struct {\n\tWidth int\n\tHeight int\n\tElems []Elem\n}\n\ntype Elem interface{ String() string }\n\nfunc (c Canvas) String() string {\n\toutput := \"\"\n\toutput += ufmt.Sprintf(`\u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"%d\" height=\"%d\"\u003e`, c.Width, c.Height)\n\tfor _, elem := range c.Elems {\n\t\toutput += elem.String()\n\t}\n\toutput += \"\u003c/svg\u003e\"\n\treturn output\n}\n\nfunc (c *Canvas) Append(elem Elem) {\n\tc.Elems = append(c.Elems, elem)\n}\n\ntype Circle struct {\n\tCX int // center X\n\tCY int // center Y\n\tR int // radius\n\tFill string\n}\n\nfunc (c Circle) String() string {\n\treturn ufmt.Sprintf(`\u003ccircle cx=\"%d\" cy=\"%d\" r=\"%d\" fill=\"%s\" /\u003e`, c.CX, c.CY, c.R, c.Fill)\n}\n\nfunc (c *Canvas) DrawCircle(cx, cy, r int, fill string) {\n\tc.Append(Circle{\n\t\tCX: cx,\n\t\tCY: cy,\n\t\tR: r,\n\t\tFill: fill,\n\t})\n}\n\ntype Rectangle struct {\n\tX, Y, Width, Height int\n\tFill string\n}\n\nfunc (c Rectangle) String() string {\n\treturn ufmt.Sprintf(`\u003crect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"%s\" /\u003e`, c.X, c.Y, c.Width, c.Height, c.Fill)\n}\n\nfunc (c *Canvas) DrawRectangle(x, y, width, height int, fill string) {\n\tc.Append(Rectangle{\n\t\tX: x,\n\t\tY: y,\n\t\tWidth: width,\n\t\tHeight: height,\n\t\tFill: fill,\n\t})\n}\n\ntype Text struct {\n\tX, Y int\n\tText, Fill string\n}\n\nfunc (c Text) String() string {\n\treturn ufmt.Sprintf(`\u003ctext x=\"%d\" y=\"%d\" fill=\"%s\"\u003e%s\u003c/text\u003e`, c.X, c.Y, c.Fill, c.Text)\n}\n\nfunc (c *Canvas) DrawText(x, y int, text, fill string) {\n\tc.Append(Text{\n\t\tX: x,\n\t\tY: y,\n\t\tText: text,\n\t\tFill: fill,\n\t})\n}\n" + }, + { + "name": "z0_filetest.gno", + "body": "// PKGPATH: gno.land/p/demo/svg_test\npackage svg_test\n\nimport \"gno.land/p/demo/svg\"\n\nfunc main() {\n\tcanvas := svg.Canvas{Width: 500, Height: 500}\n\tcanvas.DrawRectangle(50, 50, 100, 100, \"red\")\n\tcanvas.DrawCircle(100, 100, 50, \"blue\")\n\tcanvas.DrawText(100, 100, \"hello world!\", \"magenta\")\n\tprintln(canvas)\n}\n\n// Output:\n// \u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"500\" height=\"500\"\u003e\u003crect x=\"50\" y=\"50\" width=\"100\" height=\"100\" fill=\"red\" /\u003e\u003ccircle cx=\"100\" cy=\"100\" r=\"50\" fill=\"blue\" /\u003e\u003ctext x=\"100\" y=\"100\" fill=\"magenta\"\u003ehello world!\u003c/text\u003e\u003c/svg\u003e\n" + }, + { + "name": "z1_filetest.gno", + "body": "// PKGPATH: gno.land/p/demo/svg_test\npackage svg_test\n\nimport \"gno.land/p/demo/svg\"\n\nfunc main() {\n\tcanvas := svg.Canvas{\n\t\tWidth: 500, Height: 500,\n\t\tElems: []svg.Elem{\n\t\t\tsvg.Rectangle{50, 50, 100, 100, \"red\"},\n\t\t\tsvg.Circle{50, 50, 100, \"red\"},\n\t\t\tsvg.Text{100, 100, \"hello world!\", \"magenta\"},\n\t\t},\n\t}\n\tprintln(canvas)\n}\n\n// Output:\n// \u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"500\" height=\"500\"\u003e\u003crect x=\"50\" y=\"50\" width=\"100\" height=\"100\" fill=\"red\" /\u003e\u003ccircle cx=\"50\" cy=\"50\" r=\"100\" fill=\"red\" /\u003e\u003ctext x=\"100\" y=\"100\" fill=\"magenta\"\u003ehello world!\u003c/text\u003e\u003c/svg\u003e\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "tamagotchi", + "path": "gno.land/p/demo/tamagotchi", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/tamagotchi\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" + }, + { + "name": "tamagotchi.gno", + "body": "package tamagotchi\n\nimport (\n\t\"time\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// Tamagotchi structure\ntype Tamagotchi struct {\n\tname string\n\thunger int\n\thappiness int\n\thealth int\n\tage int\n\tmaxAge int\n\tsleepy int\n\tcreated time.Time\n\tlastUpdated time.Time\n}\n\nfunc New(name string) *Tamagotchi {\n\tnow := time.Now()\n\treturn \u0026Tamagotchi{\n\t\tname: name,\n\t\thunger: 50,\n\t\thappiness: 50,\n\t\thealth: 50,\n\t\tmaxAge: 100,\n\t\tlastUpdated: now,\n\t\tcreated: now,\n\t}\n}\n\nfunc (t *Tamagotchi) Name() string {\n\tt.update()\n\treturn t.name\n}\n\nfunc (t *Tamagotchi) Hunger() int {\n\tt.update()\n\treturn t.hunger\n}\n\nfunc (t *Tamagotchi) Happiness() int {\n\tt.update()\n\treturn t.happiness\n}\n\nfunc (t *Tamagotchi) Health() int {\n\tt.update()\n\treturn t.health\n}\n\nfunc (t *Tamagotchi) Age() int {\n\tt.update()\n\treturn t.age\n}\n\nfunc (t *Tamagotchi) Sleepy() int {\n\tt.update()\n\treturn t.sleepy\n}\n\n// Feed method for Tamagotchi\nfunc (t *Tamagotchi) Feed() {\n\tt.update()\n\tif t.dead() {\n\t\treturn\n\t}\n\tt.hunger = bound(t.hunger-10, 0, 100)\n}\n\n// Play method for Tamagotchi\nfunc (t *Tamagotchi) Play() {\n\tt.update()\n\tif t.dead() {\n\t\treturn\n\t}\n\tt.happiness = bound(t.happiness+10, 0, 100)\n}\n\n// Heal method for Tamagotchi\nfunc (t *Tamagotchi) Heal() {\n\tt.update()\n\n\tif t.dead() {\n\t\treturn\n\t}\n\tt.health = bound(t.health+10, 0, 100)\n}\n\nfunc (t Tamagotchi) dead() bool { return t.health == 0 }\n\n// Update applies changes based on the duration since the last update\nfunc (t *Tamagotchi) update() {\n\tif t.dead() {\n\t\treturn\n\t}\n\n\tnow := time.Now()\n\tif t.lastUpdated == now {\n\t\treturn\n\t}\n\n\tduration := now.Sub(t.lastUpdated)\n\telapsedMins := int(duration.Minutes())\n\n\tt.hunger = bound(t.hunger+elapsedMins, 0, 100)\n\tt.happiness = bound(t.happiness-elapsedMins, 0, 100)\n\tt.health = bound(t.health-elapsedMins, 0, 100)\n\tt.sleepy = bound(t.sleepy+elapsedMins, 0, 100)\n\n\t// age is hours since created\n\tt.age = int(now.Sub(t.created).Hours())\n\tif t.age \u003e t.maxAge {\n\t\tt.age = t.maxAge\n\t\tt.health = 0\n\t}\n\tif t.health == 0 {\n\t\tt.sleepy = 0\n\t\tt.happiness = 0\n\t\tt.hunger = 0\n\t}\n\n\tt.lastUpdated = now\n}\n\n// Face returns an ASCII art representation of the Tamagotchi's current state\nfunc (t *Tamagotchi) Face() string {\n\tt.update()\n\treturn t.face()\n}\n\nfunc (t *Tamagotchi) face() string {\n\tswitch {\n\tcase t.health == 0:\n\t\treturn \"😵\" // dead face\n\tcase t.health \u003c 30:\n\t\treturn \"😷\" // sick face\n\tcase t.happiness \u003c 30:\n\t\treturn \"😢\" // sad face\n\tcase t.hunger \u003e 70:\n\t\treturn \"😫\" // hungry face\n\tcase t.sleepy \u003e 70:\n\t\treturn \"😴\" // sleepy face\n\tdefault:\n\t\treturn \"😃\" // happy face\n\t}\n}\n\n// Markdown method for Tamagotchi\nfunc (t *Tamagotchi) Markdown() string {\n\tt.update()\n\treturn ufmt.Sprintf(`# %s %s\n\n* age: %d\n* hunger: %d\n* happiness: %d\n* health: %d\n* sleepy: %d`,\n\t\tt.name, t.Face(),\n\t\tt.age, t.hunger, t.happiness, t.health, t.sleepy,\n\t)\n}\n\nfunc bound(n, min, max int) int {\n\tif n \u003c min {\n\t\treturn min\n\t}\n\tif n \u003e max {\n\t\treturn max\n\t}\n\treturn n\n}\n" + }, + { + "name": "z0_filetest.gno", + "body": "package main\n\nimport (\n\t\"time\"\n\n\t\"internal/os_test\"\n\n\t\"gno.land/p/demo/tamagotchi\"\n)\n\nfunc main() {\n\tt := tamagotchi.New(\"Gnome\")\n\n\tprintln(\"\\n-- INITIAL\\n\")\n\tprintln(t.Markdown())\n\n\tprintln(\"\\n-- WAIT 20 minutes\\n\")\n\tos_test.Sleep(20 * time.Minute)\n\tprintln(t.Markdown())\n\n\tprintln(\"\\n-- FEEDx3, PLAYx2, HEALx4\\n\")\n\tt.Feed()\n\tt.Feed()\n\tt.Feed()\n\tt.Play()\n\tt.Play()\n\tt.Heal()\n\tt.Heal()\n\tt.Heal()\n\tt.Heal()\n\tprintln(t.Markdown())\n\n\tprintln(\"\\n-- WAIT 20 minutes\\n\")\n\tos_test.Sleep(20 * time.Minute)\n\tprintln(t.Markdown())\n\n\tprintln(\"\\n-- WAIT 20 hours\\n\")\n\tos_test.Sleep(20 * time.Hour)\n\tprintln(t.Markdown())\n\n\tprintln(\"\\n-- WAIT 20 hours\\n\")\n\tos_test.Sleep(20 * time.Hour)\n\tprintln(t.Markdown())\n}\n\n// Output:\n// -- INITIAL\n//\n// # Gnome 😃\n//\n// * age: 0\n// * hunger: 50\n// * happiness: 50\n// * health: 50\n// * sleepy: 0\n//\n// -- WAIT 20 minutes\n//\n// # Gnome 😃\n//\n// * age: 0\n// * hunger: 70\n// * happiness: 30\n// * health: 30\n// * sleepy: 20\n//\n// -- FEEDx3, PLAYx2, HEALx4\n//\n// # Gnome 😃\n//\n// * age: 0\n// * hunger: 40\n// * happiness: 50\n// * health: 70\n// * sleepy: 20\n//\n// -- WAIT 20 minutes\n//\n// # Gnome 😃\n//\n// * age: 0\n// * hunger: 60\n// * happiness: 30\n// * health: 50\n// * sleepy: 40\n//\n// -- WAIT 20 hours\n//\n// # Gnome 😵\n//\n// * age: 20\n// * hunger: 0\n// * happiness: 0\n// * health: 0\n// * sleepy: 0\n//\n// -- WAIT 20 hours\n//\n// # Gnome 😵\n//\n// * age: 20\n// * hunger: 0\n// * happiness: 0\n// * health: 0\n// * sleepy: 0\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "subtests", + "path": "gno.land/p/demo/tests/subtests", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/tests/subtests\n\n// TODO: this file should not exist.\n// This is a temporary workaround. Until https://github.com/gnolang/gno/issues/852\n" + }, + { + "name": "subtests.gno", + "body": "package subtests\n\nimport (\n\t\"std\"\n)\n\nfunc GetCurrentRealm() std.Realm {\n\treturn std.CurrentRealm()\n}\n\nfunc GetPrevRealm() std.Realm {\n\treturn std.PrevRealm()\n}\n\nfunc Exec(fn func()) {\n\tfn()\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "subtests", + "path": "gno.land/r/demo/tests/subtests", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/tests/subtests\n\n// TODO: this file should not exist.\n// This is a temporary workaround. Until https://github.com/gnolang/gno/issues/852\n" + }, + { + "name": "subtests.gno", + "body": "package subtests\n\nimport (\n\t\"std\"\n)\n\nfunc GetCurrentRealm() std.Realm {\n\treturn std.CurrentRealm()\n}\n\nfunc GetPrevRealm() std.Realm {\n\treturn std.PrevRealm()\n}\n\nfunc Exec(fn func()) {\n\tfn()\n}\n\nfunc CallAssertOriginCall() {\n\tstd.AssertOriginCall()\n}\n\nfunc CallIsOriginCall() bool {\n\treturn std.IsOriginCall()\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "tests", + "path": "gno.land/r/demo/tests", + "files": [ + { + "name": "README.md", + "body": "Modules here are only useful for file realm tests.\nThey can be safely ignored for other purposes.\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/tests\n\nrequire gno.land/r/demo/tests/subtests v0.0.0-latest\n" + }, + { + "name": "interfaces.gno", + "body": "package tests\n\nimport (\n\t\"strconv\"\n)\n\ntype Stringer interface {\n\tString() string\n}\n\nvar stringers []Stringer\n\nfunc AddStringer(str Stringer) {\n\t// NOTE: this is ridiculous, a slice that will become too long\n\t// eventually. Don't do this in production programs; use\n\t// gno.land/p/demo/avl or similar structures.\n\tstringers = append(stringers, str)\n}\n\nfunc Render(path string) string {\n\tres := \"\"\n\t// NOTE: like the function above, this function too will eventually\n\t// become too expensive to call.\n\tfor i, stringer := range stringers {\n\t\tres += strconv.Itoa(i) + \": \" + stringer.String() + \"\\n\"\n\t}\n\treturn res\n}\n" + }, + { + "name": "realm_compositelit.gno", + "body": "package tests\n\ntype (\n\tWord uint\n\tnat []Word\n)\n\nvar zero = \u0026Int{\n\tneg: true,\n\tabs: []Word{0},\n}\n\n// structLit\ntype Int struct {\n\tneg bool\n\tabs nat\n}\n\nfunc GetZeroType() nat {\n\ta := zero.abs\n\treturn a\n}\n" + }, + { + "name": "realm_method38d.gno", + "body": "package tests\n\nvar abs nat\n\nfunc (n nat) Add() nat {\n\treturn []Word{0}\n}\n\nfunc GetAbs() nat {\n\tabs = []Word{0}\n\n\treturn abs\n}\n\nfunc AbsAdd() nat {\n\trt := GetAbs().Add()\n\n\treturn rt\n}\n" + }, + { + "name": "tests.gno", + "body": "package tests\n\nimport (\n\t\"std\"\n\n\trsubtests \"gno.land/r/demo/tests/subtests\"\n)\n\nvar counter int\n\nfunc IncCounter() {\n\tcounter++\n}\n\nfunc Counter() int {\n\treturn counter\n}\n\nfunc CurrentRealmPath() string {\n\treturn std.CurrentRealm().PkgPath()\n}\n\nvar initOrigCaller = std.GetOrigCaller()\n\nfunc InitOrigCaller() std.Address {\n\treturn initOrigCaller\n}\n\nfunc CallAssertOriginCall() {\n\tstd.AssertOriginCall()\n}\n\nfunc CallIsOriginCall() bool {\n\treturn std.IsOriginCall()\n}\n\nfunc CallSubtestsAssertOriginCall() {\n\trsubtests.CallAssertOriginCall()\n}\n\nfunc CallSubtestsIsOriginCall() bool {\n\treturn rsubtests.CallIsOriginCall()\n}\n\n//----------------------------------------\n// Test structure to ensure cross-realm modification is prevented.\n\ntype TestRealmObject struct {\n\tField string\n}\n\nfunc ModifyTestRealmObject(t *TestRealmObject) {\n\tt.Field += \"_modified\"\n}\n\nfunc (t *TestRealmObject) Modify() {\n\tt.Field += \"_modified\"\n}\n\n//----------------------------------------\n// Test helpers to test a particular realm bug.\n\ntype TestNode struct {\n\tName string\n\tChild *TestNode\n}\n\nvar (\n\tgTestNode1 *TestNode\n\tgTestNode2 *TestNode\n\tgTestNode3 *TestNode\n)\n\nfunc InitTestNodes() {\n\tgTestNode1 = \u0026TestNode{Name: \"first\"}\n\tgTestNode2 = \u0026TestNode{Name: \"second\", Child: \u0026TestNode{Name: \"second's child\"}}\n}\n\nfunc ModTestNodes() {\n\ttmp := \u0026TestNode{}\n\ttmp.Child = gTestNode2.Child\n\tgTestNode3 = tmp // set to new-real\n\t// gTestNode1 = tmp.Child // set back to original is-real\n\tgTestNode3 = nil // delete.\n}\n\nfunc PrintTestNodes() {\n\tprintln(gTestNode2.Child.Name)\n}\n\nfunc GetPrevRealm() std.Realm {\n\treturn std.PrevRealm()\n}\n\nfunc GetRSubtestsPrevRealm() std.Realm {\n\treturn rsubtests.GetPrevRealm()\n}\n\nfunc Exec(fn func()) {\n\tfn()\n}\n" + }, + { + "name": "tests_test.gno", + "body": "package tests\n\nimport (\n\t\"std\"\n\t\"testing\"\n)\n\nfunc TestAssertOriginCall(t *testing.T) {\n\t// CallAssertOriginCall(): no panic\n\tCallAssertOriginCall()\n\tif !CallIsOriginCall() {\n\t\tt.Errorf(\"expected IsOriginCall=true but got false\")\n\t}\n\n\t// CallAssertOriginCall() from a block: panic\n\texpectedReason := \"invalid non-origin call\"\n\tfunc() {\n\t\tdefer func() {\n\t\t\tr := recover()\n\t\t\tif r == nil || r.(string) != expectedReason {\n\t\t\t\tt.Errorf(\"expected panic with '%v', got '%v'\", expectedReason, r)\n\t\t\t}\n\t\t}()\n\t\t// if called inside a function literal, this is no longer an origin call\n\t\t// because there's one additional frame (the function literal block).\n\t\tif CallIsOriginCall() {\n\t\t\tt.Errorf(\"expected IsOriginCall=false but got true\")\n\t\t}\n\t\tCallAssertOriginCall()\n\t}()\n\n\t// CallSubtestsAssertOriginCall(): panic\n\tdefer func() {\n\t\tr := recover()\n\t\tif r == nil || r.(string) != expectedReason {\n\t\t\tt.Errorf(\"expected panic with '%v', got '%v'\", expectedReason, r)\n\t\t}\n\t}()\n\tif CallSubtestsIsOriginCall() {\n\t\tt.Errorf(\"expected IsOriginCall=false but got true\")\n\t}\n\tCallSubtestsAssertOriginCall()\n}\n\nfunc TestPrevRealm(t *testing.T) {\n\tvar (\n\t\tuser1Addr = std.DerivePkgAddr(\"user1.gno\")\n\t\trTestsAddr = std.DerivePkgAddr(\"gno.land/r/demo/tests\")\n\t)\n\t// When a single realm in the frames, PrevRealm returns the user\n\tif addr := GetPrevRealm().Addr(); addr != user1Addr {\n\t\tt.Errorf(\"want GetPrevRealm().Addr==%s, got %s\", user1Addr, addr)\n\t}\n\t// When 2 or more realms in the frames, PrevRealm returns the second to last\n\tif addr := GetRSubtestsPrevRealm().Addr(); addr != rTestsAddr {\n\t\tt.Errorf(\"want GetRSubtestsPrevRealm().Addr==%s, got %s\", rTestsAddr, addr)\n\t}\n}\n" + }, + { + "name": "z0_filetest.gno", + "body": "package main\n\nimport (\n\t\"gno.land/r/demo/tests\"\n)\n\nfunc main() {\n\tprintln(\"tests.CallIsOriginCall:\", tests.CallIsOriginCall())\n\ttests.CallAssertOriginCall()\n\tprintln(\"tests.CallAssertOriginCall doesn't panic when called directly\")\n\n\t{\n\t\t// if called inside a block, this is no longer an origin call because\n\t\t// there's one additional frame (the block).\n\t\tprintln(\"tests.CallIsOriginCall:\", tests.CallIsOriginCall())\n\t\tdefer func() {\n\t\t\tr := recover()\n\t\t\tprintln(\"tests.AssertOriginCall panics if when called inside a function literal:\", r)\n\t\t}()\n\t\ttests.CallAssertOriginCall()\n\t}\n}\n\n// Output:\n// tests.CallIsOriginCall: true\n// tests.CallAssertOriginCall doesn't panic when called directly\n// tests.CallIsOriginCall: true\n// tests.AssertOriginCall panics if when called inside a function literal: undefined\n" + }, + { + "name": "z1_filetest.gno", + "body": "package main\n\nimport (\n\t\"gno.land/r/demo/tests\"\n)\n\nfunc main() {\n\tprintln(tests.Counter())\n\ttests.IncCounter()\n\tprintln(tests.Counter())\n}\n\n// Output:\n// 0\n// 1\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "tests", + "path": "gno.land/p/demo/tests", + "files": [ + { + "name": "README.md", + "body": "Modules here are only useful for file realm tests.\nThey can be safely ignored for other purposes.\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/p/demo/tests\n\nrequire (\n\tgno.land/p/demo/tests/subtests v0.0.0-latest\n\tgno.land/r/demo/tests v0.0.0-latest\n)\n" + }, + { + "name": "tests.gno", + "body": "package tests\n\nimport (\n\t\"std\"\n\n\tpsubtests \"gno.land/p/demo/tests/subtests\"\n\t\"gno.land/r/demo/tests\"\n\trtests \"gno.land/r/demo/tests\"\n)\n\nconst World = \"world\"\n\n// IncCounter demonstrates that it's possible to call a realm function from\n// a package. So a package can potentially write into the store, by calling\n// an other realm.\nfunc IncCounter() {\n\ttests.IncCounter()\n}\n\nfunc CurrentRealmPath() string {\n\treturn std.CurrentRealm().PkgPath()\n}\n\n//----------------------------------------\n// cross realm test vars\n\ntype TestRealmObject2 struct {\n\tField string\n}\n\nfunc (o2 *TestRealmObject2) Modify() {\n\to2.Field = \"modified\"\n}\n\nvar (\n\tsomevalue1 TestRealmObject2\n\tSomeValue2 TestRealmObject2\n\tSomeValue3 *TestRealmObject2\n)\n\nfunc init() {\n\tsomevalue1 = TestRealmObject2{Field: \"init\"}\n\tSomeValue2 = TestRealmObject2{Field: \"init\"}\n\tSomeValue3 = \u0026TestRealmObject2{Field: \"init\"}\n}\n\nfunc ModifyTestRealmObject2a() {\n\tsomevalue1.Field = \"modified\"\n}\n\nfunc ModifyTestRealmObject2b() {\n\tSomeValue2.Field = \"modified\"\n}\n\nfunc ModifyTestRealmObject2c() {\n\tSomeValue3.Field = \"modified\"\n}\n\nfunc GetPrevRealm() std.Realm {\n\treturn std.PrevRealm()\n}\n\nfunc GetPSubtestsPrevRealm() std.Realm {\n\treturn psubtests.GetPrevRealm()\n}\n\nfunc GetRTestsGetPrevRealm() std.Realm {\n\treturn rtests.GetPrevRealm()\n}\n\n// Warning: unsafe pattern.\nfunc Exec(fn func()) {\n\tfn()\n}\n" + }, + { + "name": "tests_test.gno", + "body": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/tests\"\n)\n\nvar World = \"WORLD\"\n\nfunc TestGetHelloWorld(t *testing.T) {\n\t// tests.World is 'world'\n\ts := \"hello \" + tests.World + World\n\tconst want = \"hello worldWORLD\"\n\tif s != want {\n\t\tt.Errorf(\"got %q want %q\", s, want)\n\t}\n}\n" + }, + { + "name": "z0_filetest.gno", + "body": "package main\n\nimport (\n\tptests \"gno.land/p/demo/tests\"\n\trtests \"gno.land/r/demo/tests\"\n)\n\nfunc main() {\n\tprintln(rtests.Counter())\n\tptests.IncCounter()\n\tprintln(rtests.Counter())\n}\n\n// Output:\n// 0\n// 1\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "p_crossrealm", + "path": "gno.land/p/demo/tests/p_crossrealm", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/tests/p_crossrealm\n" + }, + { + "name": "p_crossrealm.gno", + "body": "package p_crossrealm\n\ntype Stringer interface {\n\tString() string\n}\n\ntype Container struct {\n\tA int\n\tB Stringer\n}\n\nfunc (c *Container) Touch() *Container {\n\tc.A += 1\n\treturn c\n}\n\nfunc (c *Container) Print() {\n\tprintln(\"A:\", c.A)\n\tif c.B == nil {\n\t\tprintln(\"B: undefined\")\n\t} else {\n\t\tprintln(\"B:\", c.B.String())\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "todolist", + "path": "gno.land/p/demo/todolist", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/todolist\n\nrequire gno.land/p/demo/avl v0.0.0-latest\n" + }, + { + "name": "todolist.gno", + "body": "package todolist\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\ntype TodoList struct {\n\tTitle string\n\tTasks *avl.Tree\n\tOwner std.Address\n}\n\ntype Task struct {\n\tTitle string\n\tDone bool\n}\n\nfunc NewTodoList(title string) *TodoList {\n\treturn \u0026TodoList{\n\t\tTitle: title,\n\t\tTasks: avl.NewTree(),\n\t\tOwner: std.GetOrigCaller(),\n\t}\n}\n\nfunc NewTask(title string) *Task {\n\treturn \u0026Task{\n\t\tTitle: title,\n\t\tDone: false,\n\t}\n}\n\nfunc (tl *TodoList) AddTask(id int, task *Task) {\n\ttl.Tasks.Set(strconv.Itoa(id), task)\n}\n\nfunc ToggleTaskStatus(task *Task) {\n\ttask.Done = !task.Done\n}\n\nfunc (tl *TodoList) RemoveTask(taskId string) {\n\ttl.Tasks.Remove(taskId)\n}\n\nfunc (tl *TodoList) GetTasks() []*Task {\n\ttasks := make([]*Task, 0, tl.Tasks.Size())\n\ttl.Tasks.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\ttasks = append(tasks, value.(*Task))\n\t\treturn false\n\t})\n\treturn tasks\n}\n\nfunc (tl *TodoList) GetTodolistOwner() std.Address {\n\treturn tl.Owner\n}\n\nfunc (tl *TodoList) GetTodolistTitle() string {\n\treturn tl.Title\n}\n" + }, + { + "name": "todolist_test.gno", + "body": "package todolist\n\nimport (\n\t\"std\"\n\t\"testing\"\n)\n\nfunc TestNewTodoList(t *testing.T) {\n\ttitle := \"My Todo List\"\n\ttodoList := NewTodoList(title)\n\n\tif todoList.GetTodolistTitle() != title {\n\t\tt.Errorf(\"Expected title %q, got %q\", title, todoList.GetTodolistTitle())\n\t}\n\n\tif len(todoList.GetTasks()) != 0 {\n\t\tt.Errorf(\"Expected 0 tasks, got %d\", len(todoList.GetTasks()))\n\t}\n\n\tif todoList.GetTodolistOwner() != std.GetOrigCaller() {\n\t\tt.Errorf(\"Expected owner %v, got %v\", std.GetOrigCaller(), todoList.GetTodolistOwner())\n\t}\n}\n\nfunc TestNewTask(t *testing.T) {\n\ttitle := \"My Task\"\n\ttask := NewTask(title)\n\n\tif task.Title != title {\n\t\tt.Errorf(\"Expected title %q, got %q\", title, task.Title)\n\t}\n\n\tif task.Done {\n\t\tt.Errorf(\"Expected task to be not done, but it is done\")\n\t}\n}\n\nfunc TestAddTask(t *testing.T) {\n\ttodoList := NewTodoList(\"My Todo List\")\n\ttask := NewTask(\"My Task\")\n\n\ttodoList.AddTask(1, task)\n\n\ttasks := todoList.GetTasks()\n\tif len(tasks) != 1 {\n\t\tt.Errorf(\"Expected 1 task, got %d\", len(tasks))\n\t}\n\n\tif tasks[0] != task {\n\t\tt.Errorf(\"Expected task %v, got %v\", task, tasks[0])\n\t}\n}\n\nfunc TestToggleTaskStatus(t *testing.T) {\n\ttask := NewTask(\"My Task\")\n\n\tToggleTaskStatus(task)\n\n\tif !task.Done {\n\t\tt.Errorf(\"Expected task to be done, but it is not done\")\n\t}\n\n\tToggleTaskStatus(task)\n\n\tif task.Done {\n\t\tt.Errorf(\"Expected task to be not done, but it is done\")\n\t}\n}\n\nfunc TestRemoveTask(t *testing.T) {\n\ttodoList := NewTodoList(\"My Todo List\")\n\ttask := NewTask(\"My Task\")\n\ttodoList.AddTask(1, task)\n\n\ttodoList.RemoveTask(\"1\")\n\n\ttasks := todoList.GetTasks()\n\tif len(tasks) != 0 {\n\t\tt.Errorf(\"Expected 0 tasks, got %d\", len(tasks))\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "ui", + "path": "gno.land/p/demo/ui", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/ui\n" + }, + { + "name": "ui.gno", + "body": "package ui\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype DOM struct {\n\t// metadata\n\tPrefix string\n\tTitle string\n\tWithComments bool\n\tClasses []string\n\n\t// elements\n\tHeader Element\n\tBody Element\n\tFooter Element\n}\n\nfunc (dom DOM) String() string {\n\tclasses := strings.Join(dom.Classes, \" \")\n\n\toutput := \"\"\n\n\tif classes != \"\" {\n\t\toutput += \"\u003cmain class='\" + classes + \"'\u003e\" + \"\\n\\n\"\n\t}\n\n\tif dom.Title != \"\" {\n\t\toutput += H1(dom.Title).String(dom) + \"\\n\"\n\t}\n\n\tif header := dom.Header.String(dom); header != \"\" {\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- header --\u003e\"\n\t\t}\n\t\toutput += header + \"\\n\"\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- /header --\u003e\"\n\t\t}\n\t}\n\n\tif body := dom.Body.String(dom); body != \"\" {\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- body --\u003e\"\n\t\t}\n\t\toutput += body + \"\\n\"\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- /body --\u003e\"\n\t\t}\n\t}\n\n\tif footer := dom.Footer.String(dom); footer != \"\" {\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- footer --\u003e\"\n\t\t}\n\t\toutput += footer + \"\\n\"\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- /footer --\u003e\"\n\t\t}\n\t}\n\n\tif classes != \"\" {\n\t\toutput += \"\u003c/main\u003e\"\n\t}\n\n\t// TODO: cleanup double new-lines.\n\n\treturn output\n}\n\ntype Jumbotron []DomStringer\n\nfunc (j Jumbotron) String(dom DOM) string {\n\toutput := `\u003cdiv class=\"jumbotron\"\u003e` + \"\\n\\n\"\n\tfor _, elem := range j {\n\t\toutput += elem.String(dom) + \"\\n\"\n\t}\n\toutput += `\u003c/div\u003e\u003c!-- /jumbotron --\u003e` + \"\\n\"\n\treturn output\n}\n\n// XXX: rename Element to Div?\ntype Element []DomStringer\n\nfunc (e *Element) Append(elems ...DomStringer) {\n\t*e = append(*e, elems...)\n}\n\nfunc (e *Element) String(dom DOM) string {\n\toutput := \"\"\n\tfor _, elem := range *e {\n\t\toutput += elem.String(dom) + \"\\n\"\n\t}\n\treturn output\n}\n\ntype Breadcrumb []DomStringer\n\nfunc (b *Breadcrumb) Append(elems ...DomStringer) {\n\t*b = append(*b, elems...)\n}\n\nfunc (b Breadcrumb) String(dom DOM) string {\n\toutput := \"\"\n\tfor idx, entry := range b {\n\t\tif idx \u003e 0 {\n\t\t\toutput += \" / \"\n\t\t}\n\t\toutput += entry.String(dom)\n\t}\n\treturn output\n}\n\ntype Columns struct {\n\tMaxWidth int\n\tColumns []Element\n}\n\nfunc (c *Columns) Append(elems ...Element) {\n\tc.Columns = append(c.Columns, elems...)\n}\n\nfunc (c Columns) String(dom DOM) string {\n\toutput := `\u003cdiv class=\"columns-` + strconv.Itoa(c.MaxWidth) + `\"\u003e` + \"\\n\"\n\tfor _, entry := range c.Columns {\n\t\toutput += `\u003cdiv class=\"column\"\u003e` + \"\\n\\n\"\n\t\toutput += entry.String(dom)\n\t\toutput += \"\u003c/div\u003e\u003c!-- /column--\u003e\\n\"\n\t}\n\toutput += \"\u003c/div\u003e\u003c!-- /columns-\" + strconv.Itoa(c.MaxWidth) + \" --\u003e\\n\"\n\treturn output\n}\n\ntype Link struct {\n\tText string\n\tPath string\n\tURL string\n}\n\n// TODO: image\n\n// TODO: pager\n\nfunc (l Link) String(dom DOM) string {\n\turl := \"\"\n\tswitch {\n\tcase l.Path != \"\" \u0026\u0026 l.URL != \"\":\n\t\tpanic(\"a link should have a path or a URL, not both.\")\n\tcase l.Path != \"\":\n\t\tif l.Text == \"\" {\n\t\t\tl.Text = l.Path\n\t\t}\n\t\turl = dom.Prefix + l.Path\n\tcase l.URL != \"\":\n\t\tif l.Text == \"\" {\n\t\t\tl.Text = l.URL\n\t\t}\n\t\turl = l.URL\n\t}\n\n\treturn \"[\" + l.Text + \"](\" + url + \")\"\n}\n\ntype BulletList []DomStringer\n\nfunc (bl BulletList) String(dom DOM) string {\n\toutput := \"\"\n\n\tfor _, entry := range bl {\n\t\toutput += \"- \" + entry.String(dom) + \"\\n\"\n\t}\n\n\treturn output\n}\n\nfunc Text(s string) DomStringer {\n\treturn Raw{Content: s}\n}\n\ntype DomStringer interface {\n\tString(dom DOM) string\n}\n\ntype Raw struct {\n\tContent string\n}\n\nfunc (r Raw) String(_ DOM) string {\n\treturn r.Content\n}\n\ntype (\n\tH1 string\n\tH2 string\n\tH3 string\n\tH4 string\n\tH5 string\n\tH6 string\n\tBold string\n\tItalic string\n\tCode string\n\tParagraph string\n\tQuote string\n\tHR struct{}\n)\n\nfunc (text H1) String(_ DOM) string { return \"# \" + string(text) + \"\\n\" }\nfunc (text H2) String(_ DOM) string { return \"## \" + string(text) + \"\\n\" }\nfunc (text H3) String(_ DOM) string { return \"### \" + string(text) + \"\\n\" }\nfunc (text H4) String(_ DOM) string { return \"#### \" + string(text) + \"\\n\" }\nfunc (text H5) String(_ DOM) string { return \"##### \" + string(text) + \"\\n\" }\nfunc (text H6) String(_ DOM) string { return \"###### \" + string(text) + \"\\n\" }\nfunc (text Quote) String(_ DOM) string { return \"\u003e \" + string(text) + \"\\n\" }\nfunc (text Bold) String(_ DOM) string { return \"**\" + string(text) + \"**\" }\nfunc (text Italic) String(_ DOM) string { return \"_\" + string(text) + \"_\" }\nfunc (text Paragraph) String(_ DOM) string { return \"\\n\" + string(text) + \"\\n\" }\nfunc (_ HR) String(_ DOM) string { return \"\\n---\\n\" }\n\nfunc (text Code) String(_ DOM) string {\n\t// multiline\n\tif strings.Contains(string(text), \"\\n\") {\n\t\treturn \"\\n```\\n\" + string(text) + \"\\n```\\n\"\n\t}\n\n\t// single line\n\treturn \"`\" + string(text) + \"`\"\n}\n" + }, + { + "name": "ui_test.gno", + "body": "package ui\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "watchdog", + "path": "gno.land/p/demo/watchdog", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/demo/watchdog\n\nrequire gno.land/p/demo/uassert v0.0.0-latest\n" + }, + { + "name": "watchdog.gno", + "body": "package watchdog\n\nimport \"time\"\n\ntype Watchdog struct {\n\tDuration time.Duration\n\tlastUpdate time.Time\n\tlastDown time.Time\n}\n\nfunc (w *Watchdog) Alive() {\n\tnow := time.Now()\n\tif !w.IsAlive() {\n\t\tw.lastDown = now\n\t}\n\tw.lastUpdate = now\n}\n\nfunc (w Watchdog) Status() string {\n\tif w.IsAlive() {\n\t\treturn \"OK\"\n\t}\n\treturn \"KO\"\n}\n\nfunc (w Watchdog) IsAlive() bool {\n\treturn time.Since(w.lastUpdate) \u003c w.Duration\n}\n\nfunc (w Watchdog) UpSince() time.Time {\n\treturn w.lastDown\n}\n\nfunc (w Watchdog) DownSince() time.Time {\n\tif !w.IsAlive() {\n\t\treturn w.lastUpdate\n\t}\n\treturn time.Time{}\n}\n" + }, + { + "name": "watchdog_test.gno", + "body": "package watchdog\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"gno.land/p/demo/uassert\"\n)\n\nfunc TestPackage(t *testing.T) {\n\tw := Watchdog{Duration: 5 * time.Minute}\n\tuassert.False(t, w.IsAlive())\n\tw.Alive()\n\tuassert.True(t, w.IsAlive())\n\t// XXX: add more tests when we'll be able to \"skip time\".\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "proposal", + "path": "gno.land/p/gov/proposal", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/gov/proposal\n\nrequire (\n\tgno.land/p/demo/context v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n)\n" + }, + { + "name": "proposal.gno", + "body": "// Package proposal provides a structure for executing proposals.\npackage proposal\n\nimport (\n\t\"errors\"\n\t\"std\"\n\n\t\"gno.land/p/demo/context\"\n)\n\nvar errNotGovDAO = errors.New(\"only r/gov/dao can be the caller\")\n\n// NewExecutor creates a new executor with the provided callback function.\nfunc NewExecutor(callback func() error) Executor {\n\treturn \u0026executorImpl{\n\t\tcallback: callback,\n\t\tdone: false,\n\t}\n}\n\n// NewCtxExecutor creates a new executor with the provided callback function.\nfunc NewCtxExecutor(callback func(ctx context.Context) error) Executor {\n\treturn \u0026executorImpl{\n\t\tcallbackCtx: callback,\n\t\tdone: false,\n\t}\n}\n\n// executorImpl is an implementation of the Executor interface.\ntype executorImpl struct {\n\tcallback func() error\n\tcallbackCtx func(ctx context.Context) error\n\tdone bool\n\tsuccess bool\n}\n\n// Execute runs the executor's callback function.\nfunc (exec *executorImpl) Execute() error {\n\tif exec.done {\n\t\treturn ErrAlreadyDone\n\t}\n\n\t// Verify the executor is r/gov/dao\n\tassertCalledByGovdao()\n\n\tvar err error\n\tif exec.callback != nil {\n\t\terr = exec.callback()\n\t} else if exec.callbackCtx != nil {\n\t\tctx := context.WithValue(context.Empty(), statusContextKey, approvedStatus)\n\t\terr = exec.callbackCtx(ctx)\n\t}\n\texec.done = true\n\texec.success = err == nil\n\n\treturn err\n}\n\n// IsDone returns whether the executor has been executed.\nfunc (exec *executorImpl) IsDone() bool {\n\treturn exec.done\n}\n\n// IsSuccessful returns whether the execution was successful.\nfunc (exec *executorImpl) IsSuccessful() bool {\n\treturn exec.success\n}\n\n// IsExpired returns whether the execution had expired or not.\n// This implementation never expires.\nfunc (exec *executorImpl) IsExpired() bool {\n\treturn false\n}\n\nfunc IsApprovedByGovdaoContext(ctx context.Context) bool {\n\tv := ctx.Value(statusContextKey)\n\tif v == nil {\n\t\treturn false\n\t}\n\tvs, ok := v.(string)\n\treturn ok \u0026\u0026 vs == approvedStatus\n}\n\nfunc AssertContextApprovedByGovDAO(ctx context.Context) {\n\tif !IsApprovedByGovdaoContext(ctx) {\n\t\tpanic(\"not approved by govdao\")\n\t}\n}\n\n// assertCalledByGovdao asserts that the calling Realm is /r/gov/dao\nfunc assertCalledByGovdao() {\n\tcaller := std.CurrentRealm().PkgPath()\n\n\tif caller != daoPkgPath {\n\t\tpanic(errNotGovDAO)\n\t}\n}\n\ntype propContextKey string\n\nfunc (k propContextKey) String() string { return string(k) }\n\nconst (\n\tstatusContextKey = propContextKey(\"govdao-prop-status\")\n\tapprovedStatus = \"approved\"\n)\n" + }, + { + "name": "proposal_test.gno", + "body": "package proposal\n\nimport (\n\t\"errors\"\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/uassert\"\n\t\"gno.land/p/demo/urequire\"\n)\n\nfunc TestExecutor(t *testing.T) {\n\tt.Parallel()\n\n\tverifyProposalFailed := func(e Executor) {\n\t\tuassert.True(t, e.IsDone(), \"expected proposal to be done\")\n\t\tuassert.False(t, e.IsSuccessful(), \"expected proposal to fail\")\n\t}\n\n\tverifyProposalSucceeded := func(e Executor) {\n\t\tuassert.True(t, e.IsDone(), \"expected proposal to be done\")\n\t\tuassert.True(t, e.IsSuccessful(), \"expected proposal to be successful\")\n\t}\n\n\tt.Run(\"govdao not caller\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tcalled = false\n\n\t\t\tcb = func() error {\n\t\t\t\tcalled = true\n\n\t\t\t\treturn nil\n\t\t\t}\n\t\t)\n\n\t\t// Create the executor\n\t\te := NewExecutor(cb)\n\n\t\turequire.False(t, e.IsDone(), \"expected status to be NotExecuted\")\n\n\t\t// Execute as not the /r/gov/dao caller\n\t\tuassert.PanicsWithMessage(t, errNotGovDAO.Error(), func() {\n\t\t\t_ = e.Execute()\n\t\t})\n\n\t\tuassert.False(t, called, \"expected proposal to not execute\")\n\t})\n\n\tt.Run(\"execution successful\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tcalled = false\n\n\t\t\tcb = func() error {\n\t\t\t\tcalled = true\n\n\t\t\t\treturn nil\n\t\t\t}\n\t\t)\n\n\t\t// Create the executor\n\t\te := NewExecutor(cb)\n\n\t\turequire.False(t, e.IsDone(), \"expected status to be NotExecuted\")\n\n\t\t// Execute as the /r/gov/dao caller\n\t\tr := std.NewCodeRealm(daoPkgPath)\n\t\tstd.TestSetRealm(r)\n\n\t\tuassert.NotPanics(t, func() {\n\t\t\terr := e.Execute()\n\n\t\t\tuassert.NoError(t, err)\n\t\t})\n\n\t\tuassert.True(t, called, \"expected proposal to execute\")\n\n\t\t// Make sure the execution params are correct\n\t\tverifyProposalSucceeded(e)\n\t})\n\n\tt.Run(\"execution unsuccessful\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tcalled = false\n\t\t\texpectedErr = errors.New(\"unexpected\")\n\n\t\t\tcb = func() error {\n\t\t\t\tcalled = true\n\n\t\t\t\treturn expectedErr\n\t\t\t}\n\t\t)\n\n\t\t// Create the executor\n\t\te := NewExecutor(cb)\n\n\t\t// Execute as the /r/gov/dao caller\n\t\tr := std.NewCodeRealm(daoPkgPath)\n\t\tstd.TestSetRealm(r)\n\n\t\tuassert.NotPanics(t, func() {\n\t\t\terr := e.Execute()\n\n\t\t\tuassert.ErrorIs(t, err, expectedErr)\n\t\t})\n\n\t\tuassert.True(t, called, \"expected proposal to execute\")\n\n\t\t// Make sure the execution params are correct\n\t\tverifyProposalFailed(e)\n\t})\n\n\tt.Run(\"proposal already executed\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tcalled = false\n\n\t\t\tcb = func() error {\n\t\t\t\tcalled = true\n\n\t\t\t\treturn nil\n\t\t\t}\n\t\t)\n\n\t\t// Create the executor\n\t\te := NewExecutor(cb)\n\n\t\turequire.False(t, e.IsDone(), \"expected status to be NotExecuted\")\n\n\t\t// Execute as the /r/gov/dao caller\n\t\tr := std.NewCodeRealm(daoPkgPath)\n\t\tstd.TestSetRealm(r)\n\n\t\tuassert.NotPanics(t, func() {\n\t\t\tuassert.NoError(t, e.Execute())\n\t\t})\n\n\t\tuassert.True(t, called, \"expected proposal to execute\")\n\n\t\t// Make sure the execution params are correct\n\t\tverifyProposalSucceeded(e)\n\n\t\t// Attempt to execute the proposal again\n\t\tuassert.NotPanics(t, func() {\n\t\t\terr := e.Execute()\n\n\t\t\tuassert.ErrorIs(t, err, ErrAlreadyDone)\n\t\t})\n\t})\n}\n" + }, + { + "name": "types.gno", + "body": "// Package proposal defines types for proposal execution.\npackage proposal\n\nimport \"errors\"\n\n// Executor represents a minimal closure-oriented proposal design.\n// It is intended to be used by a govdao governance proposal (v1, v2, etc).\ntype Executor interface {\n\t// Execute executes the given proposal, and returns any error encountered\n\t// during the execution\n\tExecute() error\n\n\t// IsDone returns a flag indicating if the proposal was executed\n\tIsDone() bool\n\n\t// IsSuccessful returns a flag indicating if the proposal was executed\n\t// and is successful\n\tIsSuccessful() bool // IsDone() \u0026\u0026 !err\n\n\t// IsExpired returns whether the execution had expired or not.\n\tIsExpired() bool\n}\n\n// ErrAlreadyDone is the error returned when trying to execute an already\n// executed proposal.\nvar ErrAlreadyDone = errors.New(\"already executed\")\n\n// Status enum.\ntype Status string\n\nconst (\n\tNotExecuted Status = \"not_executed\"\n\tSucceeded Status = \"succeeded\"\n\tFailed Status = \"failed\"\n)\n\nconst daoPkgPath = \"gno.land/r/gov/dao\" // TODO: make sure this is configurable through r/sys/vars\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "validators", + "path": "gno.land/p/sys/validators", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/sys/validators\n" + }, + { + "name": "types.gno", + "body": "package validators\n\nimport (\n\t\"errors\"\n\t\"std\"\n)\n\n// ValsetProtocol defines the validator set protocol (PoA / PoS / PoC / ?)\ntype ValsetProtocol interface {\n\t// AddValidator adds a new validator to the validator set.\n\t// If the validator is already present, the method should error out\n\t//\n\t// TODO: This API is not ideal -- the address should be derived from\n\t// the public key, and not be passed in as such, but currently Gno\n\t// does not support crypto address derivation\n\tAddValidator(address std.Address, pubKey string, power uint64) (Validator, error)\n\n\t// RemoveValidator removes the given validator from the set.\n\t// If the validator is not present in the set, the method should error out\n\tRemoveValidator(address std.Address) (Validator, error)\n\n\t// IsValidator returns a flag indicating if the given\n\t// bech32 address is part of the validator set\n\tIsValidator(address std.Address) bool\n\n\t// GetValidator returns the validator using the given address\n\tGetValidator(address std.Address) (Validator, error)\n\n\t// GetValidators returns the currently active validator set\n\tGetValidators() []Validator\n}\n\n// Validator represents a single chain validator\ntype Validator struct {\n\tAddress std.Address // bech32 address\n\tPubKey string // bech32 representation of the public key\n\tVotingPower uint64\n}\n\nconst (\n\tValidatorAddedEvent = \"ValidatorAdded\" // emitted when a validator was added to the set\n\tValidatorRemovedEvent = \"ValidatorRemoved\" // emitted when a validator was removed from the set\n)\n\nvar (\n\t// ErrValidatorExists is returned when the validator is already in the set\n\tErrValidatorExists = errors.New(\"validator already exists\")\n\n\t// ErrValidatorMissing is returned when the validator is not in the set\n\tErrValidatorMissing = errors.New(\"validator doesn't exist\")\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "poa", + "path": "gno.land/p/nt/poa", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/p/nt/poa\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n\tgno.land/p/sys/validators v0.0.0-latest\n)\n" + }, + { + "name": "option.gno", + "body": "package poa\n\nimport \"gno.land/p/sys/validators\"\n\ntype Option func(*PoA)\n\n// WithInitialSet sets the initial PoA validator set\nfunc WithInitialSet(validators []validators.Validator) Option {\n\treturn func(p *PoA) {\n\t\tfor _, validator := range validators {\n\t\t\tp.validators.Set(validator.Address.String(), validator)\n\t\t}\n\t}\n}\n" + }, + { + "name": "poa.gno", + "body": "package poa\n\nimport (\n\t\"errors\"\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/sys/validators\"\n)\n\nvar ErrInvalidVotingPower = errors.New(\"invalid voting power\")\n\n// PoA specifies the Proof of Authority validator set, with simple add / remove constraints.\n//\n// To add:\n// - proposed validator must not be part of the set already\n// - proposed validator voting power must be \u003e 0\n//\n// To remove:\n// - proposed validator must be part of the set already\ntype PoA struct {\n\tvalidators *avl.Tree // std.Address -\u003e validators.Validator\n}\n\n// NewPoA creates a new empty Proof of Authority validator set\nfunc NewPoA(opts ...Option) *PoA {\n\t// Create the empty set\n\tp := \u0026PoA{\n\t\tvalidators: avl.NewTree(),\n\t}\n\n\t// Apply the options\n\tfor _, opt := range opts {\n\t\topt(p)\n\t}\n\n\treturn p\n}\n\nfunc (p *PoA) AddValidator(address std.Address, pubKey string, power uint64) (validators.Validator, error) {\n\t// Validate that the operation is a valid call.\n\t// Check if the validator is already in the set\n\tif p.IsValidator(address) {\n\t\treturn validators.Validator{}, validators.ErrValidatorExists\n\t}\n\n\t// Make sure the voting power \u003e 0\n\tif power == 0 {\n\t\treturn validators.Validator{}, ErrInvalidVotingPower\n\t}\n\n\tv := validators.Validator{\n\t\tAddress: address,\n\t\tPubKey: pubKey, // TODO: in the future, verify the public key\n\t\tVotingPower: power,\n\t}\n\n\t// Add the validator to the set\n\tp.validators.Set(address.String(), v)\n\n\treturn v, nil\n}\n\nfunc (p *PoA) RemoveValidator(address std.Address) (validators.Validator, error) {\n\t// Validate that the operation is a valid call\n\t// Fetch the validator\n\tvalidator, err := p.GetValidator(address)\n\tif err != nil {\n\t\treturn validators.Validator{}, err\n\t}\n\n\t// Remove the validator from the set\n\tp.validators.Remove(address.String())\n\n\treturn validator, nil\n}\n\nfunc (p *PoA) IsValidator(address std.Address) bool {\n\t_, exists := p.validators.Get(address.String())\n\n\treturn exists\n}\n\nfunc (p *PoA) GetValidator(address std.Address) (validators.Validator, error) {\n\tvalidatorRaw, exists := p.validators.Get(address.String())\n\tif !exists {\n\t\treturn validators.Validator{}, validators.ErrValidatorMissing\n\t}\n\n\tvalidator := validatorRaw.(validators.Validator)\n\n\treturn validator, nil\n}\n\nfunc (p *PoA) GetValidators() []validators.Validator {\n\tvals := make([]validators.Validator, 0, p.validators.Size())\n\n\tp.validators.Iterate(\"\", \"\", func(_ string, value interface{}) bool {\n\t\tvalidator := value.(validators.Validator)\n\t\tvals = append(vals, validator)\n\n\t\treturn false\n\t})\n\n\treturn vals\n}\n" + }, + { + "name": "poa_test.gno", + "body": "package poa\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/uassert\"\n\t\"gno.land/p/demo/urequire\"\n\t\"gno.land/p/sys/validators\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// generateTestValidators generates a dummy validator set\nfunc generateTestValidators(count int) []validators.Validator {\n\tvals := make([]validators.Validator, 0, count)\n\n\tfor i := 0; i \u003c count; i++ {\n\t\tval := validators.Validator{\n\t\t\tAddress: testutils.TestAddress(ufmt.Sprintf(\"%d\", i)),\n\t\t\tPubKey: \"public-key\",\n\t\t\tVotingPower: 1,\n\t\t}\n\n\t\tvals = append(vals, val)\n\t}\n\n\treturn vals\n}\n\nfunc TestPoA_AddValidator_Invalid(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"validator already in set\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tproposalAddress = testutils.TestAddress(\"caller\")\n\t\t\tproposalKey = \"public-key\"\n\n\t\t\tinitialSet = generateTestValidators(1)\n\t\t)\n\n\t\tinitialSet[0].Address = proposalAddress\n\t\tinitialSet[0].PubKey = proposalKey\n\n\t\t// Create the protocol with an initial set\n\t\tp := NewPoA(WithInitialSet(initialSet))\n\n\t\t// Attempt to add the validator\n\t\t_, err := p.AddValidator(proposalAddress, proposalKey, 1)\n\t\tuassert.ErrorIs(t, err, validators.ErrValidatorExists)\n\t})\n\n\tt.Run(\"invalid voting power\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tproposalAddress = testutils.TestAddress(\"caller\")\n\t\t\tproposalKey = \"public-key\"\n\t\t)\n\n\t\t// Create the protocol with no initial set\n\t\tp := NewPoA()\n\n\t\t// Attempt to add the validator\n\t\t_, err := p.AddValidator(proposalAddress, proposalKey, 0)\n\t\tuassert.ErrorIs(t, err, ErrInvalidVotingPower)\n\t})\n}\n\nfunc TestPoA_AddValidator(t *testing.T) {\n\tt.Parallel()\n\n\tvar (\n\t\tproposalAddress = testutils.TestAddress(\"caller\")\n\t\tproposalKey = \"public-key\"\n\t)\n\n\t// Create the protocol with no initial set\n\tp := NewPoA()\n\n\t// Attempt to add the validator\n\t_, err := p.AddValidator(proposalAddress, proposalKey, 1)\n\tuassert.NoError(t, err)\n\n\t// Make sure the validator is added\n\tif !p.IsValidator(proposalAddress) || p.validators.Size() != 1 {\n\t\tt.Fatal(\"address is not validator\")\n\t}\n}\n\nfunc TestPoA_RemoveValidator_Invalid(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"proposed removal not in set\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\tproposalAddress = testutils.TestAddress(\"caller\")\n\t\t\tinitialSet = generateTestValidators(1)\n\t\t)\n\n\t\tinitialSet[0].Address = proposalAddress\n\n\t\t// Create the protocol with an initial set\n\t\tp := NewPoA(WithInitialSet(initialSet))\n\n\t\t// Attempt to remove the validator\n\t\t_, err := p.RemoveValidator(testutils.TestAddress(\"totally random\"))\n\t\tuassert.ErrorIs(t, err, validators.ErrValidatorMissing)\n\t})\n}\n\nfunc TestPoA_RemoveValidator(t *testing.T) {\n\tt.Parallel()\n\n\tvar (\n\t\tproposalAddress = testutils.TestAddress(\"caller\")\n\t\tinitialSet = generateTestValidators(1)\n\t)\n\n\tinitialSet[0].Address = proposalAddress\n\n\t// Create the protocol with an initial set\n\tp := NewPoA(WithInitialSet(initialSet))\n\n\t// Attempt to remove the validator\n\t_, err := p.RemoveValidator(proposalAddress)\n\turequire.NoError(t, err)\n\n\t// Make sure the validator is removed\n\tif p.IsValidator(proposalAddress) || p.validators.Size() != 0 {\n\t\tt.Fatal(\"address is validator\")\n\t}\n}\n\nfunc TestPoA_GetValidator(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"validator not in set\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Create the protocol with no initial set\n\t\tp := NewPoA()\n\n\t\t// Attempt to get the voting power\n\t\t_, err := p.GetValidator(testutils.TestAddress(\"caller\"))\n\t\tuassert.ErrorIs(t, err, validators.ErrValidatorMissing)\n\t})\n\n\tt.Run(\"validator fetched\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar (\n\t\t\taddress = testutils.TestAddress(\"caller\")\n\t\t\tpubKey = \"public-key\"\n\t\t\tvotingPower = uint64(10)\n\n\t\t\tinitialSet = generateTestValidators(1)\n\t\t)\n\n\t\tinitialSet[0].Address = address\n\t\tinitialSet[0].PubKey = pubKey\n\t\tinitialSet[0].VotingPower = votingPower\n\n\t\t// Create the protocol with an initial set\n\t\tp := NewPoA(WithInitialSet(initialSet))\n\n\t\t// Get the validator\n\t\tval, err := p.GetValidator(address)\n\t\turequire.NoError(t, err)\n\n\t\t// Validate the address\n\t\tif val.Address != address {\n\t\t\tt.Fatal(\"invalid address\")\n\t\t}\n\n\t\t// Validate the voting power\n\t\tif val.VotingPower != votingPower {\n\t\t\tt.Fatal(\"invalid voting power\")\n\t\t}\n\n\t\t// Validate the public key\n\t\tif val.PubKey != pubKey {\n\t\t\tt.Fatal(\"invalid public key\")\n\t\t}\n\t})\n}\n\nfunc TestPoA_GetValidators(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"empty set\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Create the protocol with no initial set\n\t\tp := NewPoA()\n\n\t\t// Attempt to get the voting power\n\t\tvals := p.GetValidators()\n\n\t\tif len(vals) != 0 {\n\t\t\tt.Fatal(\"validator set is not empty\")\n\t\t}\n\t})\n\n\tt.Run(\"validator set fetched\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tinitialSet := generateTestValidators(10)\n\n\t\t// Create the protocol with an initial set\n\t\tp := NewPoA(WithInitialSet(initialSet))\n\n\t\t// Get the validator set\n\t\tvals := p.GetValidators()\n\n\t\tif len(vals) != len(initialSet) {\n\t\t\tt.Fatal(\"returned validator set mismatch\")\n\t\t}\n\n\t\tfor _, val := range vals {\n\t\t\tfor _, initialVal := range initialSet {\n\t\t\t\tif val.Address != initialVal.Address {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Validate the voting power\n\t\t\t\tuassert.Equal(t, val.VotingPower, initialVal.VotingPower)\n\n\t\t\t\t// Validate the public key\n\t\t\t\tuassert.Equal(t, val.PubKey, initialVal.PubKey)\n\t\t\t}\n\t\t}\n\t})\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "gnoface", + "path": "gno.land/r/demo/art/gnoface", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/art/gnoface\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" + }, + { + "name": "gnoface.gno", + "body": "package gnoface\n\nimport (\n\t\"math/rand\"\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nfunc Render(path string) string {\n\tseed := uint64(std.GetHeight())\n\n\tpath = strings.TrimSpace(path)\n\tif path != \"\" {\n\t\ts, err := strconv.Atoi(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tseed = uint64(s)\n\t}\n\n\toutput := ufmt.Sprintf(\"Gnoface #%d\\n\", seed)\n\toutput += \"```\\n\" + Draw(seed) + \"```\\n\"\n\treturn output\n}\n\nfunc Draw(seed uint64) string {\n\tvar (\n\t\thairs = []string{\n\t\t\t\" s\",\n\t\t\t\" .......\",\n\t\t\t\" s s s\",\n\t\t\t\" /\\\\ /\\\\\",\n\t\t\t\" |||||||\",\n\t\t}\n\t\theadtop = []string{\n\t\t\t\" /-------\\\\\",\n\t\t\t\" /~~~~~~~\\\\\",\n\t\t\t\" /|||||||\\\\\",\n\t\t\t\" ////////\\\\\",\n\t\t\t\" |||||||||\",\n\t\t\t\" /\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\",\n\t\t}\n\t\theadspace = []string{\n\t\t\t\" | |\",\n\t\t}\n\t\teyebrow = []string{\n\t\t\t\"~\",\n\t\t\t\"*\",\n\t\t\t\"_\",\n\t\t\t\".\",\n\t\t}\n\t\tear = []string{\n\t\t\t\"o\",\n\t\t\t\" \",\n\t\t\t\"D\",\n\t\t\t\"O\",\n\t\t\t\"\u003c\",\n\t\t\t\"\u003e\",\n\t\t\t\".\",\n\t\t\t\"|\",\n\t\t\t\")\",\n\t\t\t\"(\",\n\t\t}\n\t\teyesmiddle = []string{\n\t\t\t\"| o o |\",\n\t\t\t\"| o _ |\",\n\t\t\t\"| _ o |\",\n\t\t\t\"| . . |\",\n\t\t\t\"| O O |\",\n\t\t\t\"| v v |\",\n\t\t\t\"| X X |\",\n\t\t\t\"| x X |\",\n\t\t\t\"| X D |\",\n\t\t\t\"| ~ ~ |\",\n\t\t}\n\t\tnose = []string{\n\t\t\t\" | o |\",\n\t\t\t\" | O |\",\n\t\t\t\" | V |\",\n\t\t\t\" | L |\",\n\t\t\t\" | C |\",\n\t\t\t\" | ~ |\",\n\t\t\t\" | . . |\",\n\t\t\t\" | . |\",\n\t\t}\n\t\tmouth = []string{\n\t\t\t\" | __/ |\",\n\t\t\t\" | \\\\_/ |\",\n\t\t\t\" | . |\",\n\t\t\t\" | ___ |\",\n\t\t\t\" | ~~~ |\",\n\t\t\t\" | === |\",\n\t\t\t\" | \u003c=\u003e |\",\n\t\t}\n\t\theadbottom = []string{\n\t\t\t\" \\\\-------/\",\n\t\t\t\" \\\\~~~~~~~/\",\n\t\t\t\" \\\\_______/\",\n\t\t}\n\t)\n\n\tr := rand.New(rand.NewPCG(seed, 0xdeadbeef))\n\n\treturn pick(r, hairs) + \"\\n\" +\n\t\tpick(r, headtop) + \"\\n\" +\n\t\tpick(r, headspace) + \"\\n\" +\n\t\t\" | \" + pick(r, eyebrow) + \" \" + pick(r, eyebrow) + \" |\\n\" +\n\t\tpick(r, ear) + pick(r, eyesmiddle) + pick(r, ear) + \"\\n\" +\n\t\tpick(r, headspace) + \"\\n\" +\n\t\tpick(r, nose) + \"\\n\" +\n\t\tpick(r, headspace) + \"\\n\" +\n\t\tpick(r, mouth) + \"\\n\" +\n\t\tpick(r, headspace) + \"\\n\" +\n\t\tpick(r, headbottom) + \"\\n\"\n}\n\nfunc pick(r *rand.Rand, slice []string) string {\n\treturn slice[r.IntN(len(slice))]\n}\n\n// based on https://github.com/moul/pipotron/blob/master/dict/ascii-face.yml\n" + }, + { + "name": "gnoface_test.gno", + "body": "package gnoface\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nfunc TestDraw(t *testing.T) {\n\tcases := []struct {\n\t\tseed uint64\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tseed: 42,\n\t\t\texpected: `\n |||||||\n |||||||||\n | |\n | . ~ |\n)| v v |O\n | |\n | L |\n | |\n | ___ |\n | |\n \\~~~~~~~/\n`[1:],\n\t\t},\n\t\t{\n\t\t\tseed: 1337,\n\t\t\texpected: `\n .......\n |||||||||\n | |\n | . _ |\nD| x X |O\n | |\n | ~ |\n | |\n | ~~~ |\n | |\n \\~~~~~~~/\n`[1:],\n\t\t},\n\t\t{\n\t\t\tseed: 123456789,\n\t\t\texpected: `\n .......\n ////////\\\n | |\n | ~ * |\n|| x X |o\n | |\n | V |\n | |\n | . |\n | |\n \\-------/\n`[1:],\n\t\t},\n\t}\n\tfor _, tc := range cases {\n\t\tname := ufmt.Sprintf(\"%d\", tc.seed)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tgot := Draw(tc.seed)\n\t\t\tif got != tc.expected {\n\t\t\t\tt.Errorf(\"got %s, expected %s\", got, tc.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRender(t *testing.T) {\n\tcases := []struct {\n\t\tpath string\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tpath: \"42\",\n\t\t\texpected: \"Gnoface #42\\n```\" + `\n |||||||\n |||||||||\n | |\n | . ~ |\n)| v v |O\n | |\n | L |\n | |\n | ___ |\n | |\n \\~~~~~~~/\n` + \"```\\n\",\n\t\t},\n\t\t{\n\t\t\tpath: \"1337\",\n\t\t\texpected: \"Gnoface #1337\\n```\" + `\n .......\n |||||||||\n | |\n | . _ |\nD| x X |O\n | |\n | ~ |\n | |\n | ~~~ |\n | |\n \\~~~~~~~/\n` + \"```\\n\",\n\t\t},\n\t\t{\n\t\t\tpath: \"123456789\",\n\t\t\texpected: \"Gnoface #123456789\\n```\" + `\n .......\n ////////\\\n | |\n | ~ * |\n|| x X |o\n | |\n | V |\n | |\n | . |\n | |\n \\-------/\n` + \"```\\n\",\n\t\t},\n\t}\n\tfor _, tc := range cases {\n\t\tt.Run(tc.path, func(t *testing.T) {\n\t\t\tgot := Render(tc.path)\n\t\t\tif got != tc.expected {\n\t\t\t\tt.Errorf(\"got %s, expected %s\", got, tc.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "millipede", + "path": "gno.land/r/demo/art/millipede", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/art/millipede\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" + }, + { + "name": "millipede.gno", + "body": "package millipede\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nconst (\n\tminSize = 1\n\tdefaultSize = 20\n\tmaxSize = 100\n)\n\nfunc Draw(size int) string {\n\tif size \u003c minSize || size \u003e maxSize {\n\t\tpanic(\"invalid millipede size\")\n\t}\n\tpaddings := []string{\" \", \" \", \"\", \" \", \" \", \" \", \" \", \" \", \" \"}\n\tvar b strings.Builder\n\tb.WriteString(\" ╚⊙ ⊙╝\\n\")\n\tfor i := 0; i \u003c size; i++ {\n\t\tb.WriteString(paddings[i%9] + \"╚═(███)═╝\\n\")\n\t}\n\treturn b.String()\n}\n\nfunc Render(path string) string {\n\tsize := defaultSize\n\n\tpath = strings.TrimSpace(path)\n\tif path != \"\" {\n\t\tvar err error\n\t\tsize, err = strconv.Atoi(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\toutput := \"```\\n\" + Draw(size) + \"```\\n\"\n\tif size \u003e minSize {\n\t\toutput += ufmt.Sprintf(\"[%d](/r/demo/art/millpede:%d)\u003c \", size-1, size-1)\n\t}\n\tif size \u003c maxSize {\n\t\toutput += ufmt.Sprintf(\" \u003e[%d](/r/demo/art/millipede:%d)\", size+1, size+1)\n\t}\n\treturn output\n}\n\n// based on https://github.com/getmillipede/millipede-go/blob/977f046c39c35a650eac0fd30245e96b22c7803c/main.go\n" + }, + { + "name": "millipede_test.gno", + "body": "package millipede\n\nimport \"testing\"\n\nfunc TestRender(t *testing.T) {\n\tcases := []struct {\n\t\tpath string\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tpath: \"\",\n\t\t\texpected: \"```\" + `\n ╚⊙ ⊙╝\n ╚═(███)═╝\n ╚═(███)═╝\n╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n ╚═(███)═╝\n` + \"```\\n[19](/r/demo/art/millpede:19)\u003c \u003e[21](/r/demo/art/millipede:21)\",\n\t\t},\n\t\t{\n\t\t\tpath: \"4\",\n\t\t\texpected: \"```\" + `\n ╚⊙ ⊙╝\n ╚═(███)═╝\n ╚═(███)═╝\n╚═(███)═╝\n ╚═(███)═╝\n` + \"```\\n[3](/r/demo/art/millpede:3)\u003c \u003e[5](/r/demo/art/millipede:5)\",\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(tc.path, func(t *testing.T) {\n\t\t\tgot := Render(tc.path)\n\t\t\tif got != tc.expected {\n\t\t\t\tt.Errorf(\"expected %s, got %s.\", tc.expected, got)\n\t\t\t}\n\t\t})\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "banktest", + "path": "gno.land/r/demo/banktest", + "files": [ + { + "name": "README.md", + "body": "This is a simple test realm contract that demonstrates how to use the banker.\n\nSee [gno.land/r/demo/banktest/banktest.go](/r/demo/banktest/banktest.go) to see the original contract code.\n\nThis article will go through each line to explain how it works.\n\n```go\npackage banktest\n```\n\nThis package is locally named \"banktest\" (could be anything).\n\n```go\nimport (\n \"std\"\n)\n```\n\nThe \"std\" package is defined by the gno code in stdlibs/std/. \u003c/br\u003e Self explanatory; and you'll see more usage from std later.\n\n```go\ntype activity struct {\n caller std.Address\n sent std.Coins\n returned std.Coins\n time time.Time\n}\n\nfunc (act *activity) String() string {\n return act.caller.String() + \" \" +\n act.sent.String() + \" sent, \" +\n act.returned.String() + \" returned, at \" +\n act.time.Format(\"2006-01-02 3:04pm MST\")\n}\n\nvar latest [10]*activity\n```\n\nThis is just maintaining a list of recent activity to this contract. Notice that the \"latest\" variable is defined \"globally\" within the context of the realm with path \"gno.land/r/demo/banktest\".\n\nThis means that calls to functions defined within this package are encapsulated within this \"data realm\", where the data is mutated based on transactions that can potentially cross many realm and non-realm package boundaries (in the call stack).\n\n```go\n// Deposit will take the coins (to the realm's pkgaddr) or return them to user.\nfunc Deposit(returnDenom string, returnAmount int64) string {\n std.AssertOriginCall()\n caller := std.GetOrigCaller()\n send := std.Coins{{returnDenom, returnAmount}}\n```\n\nThis is the beginning of the definition of the contract function named \"Deposit\". `std.AssertOriginCall() asserts that this function was called by a gno transactional Message. The caller is the user who signed off on this transactional message. Send is the amount of deposit sent along with this message.\n\n```go\n // record activity\n act := \u0026activity{\n caller: caller,\n sent: std.GetOrigSend(),\n returned: send,\n time: time.Now(),\n }\n for i := len(latest) - 2; i \u003e= 0; i-- {\n latest[i+1] = latest[i] // shift by +1.\n }\n latest[0] = act\n```\n\nUpdating the \"latest\" array for viewing at gno.land/r/demo/banktest: (w/ trailing colon).\n\n```go\n // return if any.\n if returnAmount \u003e 0 {\n```\n\nIf the user requested the return of coins...\n\n```go\n banker := std.GetBanker(std.BankerTypeOrigSend)\n```\n\nuse a std.Banker instance to return any deposited coins to the original sender.\n\n```go\n pkgaddr := std.GetOrigPkgAddr()\n // TODO: use std.Coins constructors, this isn't generally safe.\n banker.SendCoins(pkgaddr, caller, send)\n return \"returned!\"\n```\n\nNotice that each realm package has an associated Cosmos address.\n\nFinally, the results are rendered via an ABCI query call when you visit [/r/demo/banktest:](/r/demo/banktest:).\n\n```go\nfunc Render(path string) string {\n // get realm coins.\n banker := std.GetBanker(std.BankerTypeReadonly)\n coins := banker.GetCoins(std.GetOrigPkgAddr())\n\n // render\n res := \"\"\n res += \"## recent activity\\n\"\n res += \"\\n\"\n for _, act := range latest {\n if act == nil {\n break\n }\n res += \" * \" + act.String() + \"\\n\"\n }\n res += \"\\n\"\n res += \"## total deposits\\n\"\n res += coins.String()\n return res\n}\n```\n\nYou can call this contract yourself, by vistiing [/r/demo/banktest](/r/demo/banktest) and the [quickstart guide](/r/demo/boards:gnolang/4).\n" + }, + { + "name": "banktest.gno", + "body": "package banktest\n\nimport (\n\t\"std\"\n\t\"time\"\n)\n\ntype activity struct {\n\tcaller std.Address\n\tsent std.Coins\n\treturned std.Coins\n\ttime time.Time\n}\n\nfunc (act *activity) String() string {\n\treturn act.caller.String() + \" \" +\n\t\tact.sent.String() + \" sent, \" +\n\t\tact.returned.String() + \" returned, at \" +\n\t\tact.time.Format(\"2006-01-02 3:04pm MST\")\n}\n\nvar latest [10]*activity\n\n// Deposit will take the coins (to the realm's pkgaddr) or return them to user.\nfunc Deposit(returnDenom string, returnAmount int64) string {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tsend := std.Coins{{returnDenom, returnAmount}}\n\t// record activity\n\tact := \u0026activity{\n\t\tcaller: caller,\n\t\tsent: std.GetOrigSend(),\n\t\treturned: send,\n\t\ttime: time.Now(),\n\t}\n\tfor i := len(latest) - 2; i \u003e= 0; i-- {\n\t\tlatest[i+1] = latest[i] // shift by +1.\n\t}\n\tlatest[0] = act\n\t// return if any.\n\tif returnAmount \u003e 0 {\n\t\tbanker := std.GetBanker(std.BankerTypeOrigSend)\n\t\tpkgaddr := std.GetOrigPkgAddr()\n\t\t// TODO: use std.Coins constructors, this isn't generally safe.\n\t\tbanker.SendCoins(pkgaddr, caller, send)\n\t\treturn \"returned!\"\n\t} else {\n\t\treturn \"thank you!\"\n\t}\n}\n\nfunc Render(path string) string {\n\t// get realm coins.\n\tbanker := std.GetBanker(std.BankerTypeReadonly)\n\tcoins := banker.GetCoins(std.GetOrigPkgAddr())\n\n\t// render\n\tres := \"\"\n\tres += \"## recent activity\\n\"\n\tres += \"\\n\"\n\tfor _, act := range latest {\n\t\tif act == nil {\n\t\t\tbreak\n\t\t}\n\t\tres += \" * \" + act.String() + \"\\n\"\n\t}\n\tres += \"\\n\"\n\tres += \"## total deposits\\n\"\n\tres += coins.String()\n\treturn res\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/banktest\n" + }, + { + "name": "z_0_filetest.gno", + "body": "// SEND: 100000000ugnot\n\npackage main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/banktest\"\n)\n\nfunc main() {\n\t// set up main address and banktest addr.\n\tbanktestAddr := std.DerivePkgAddr(\"gno.land/r/demo/banktest\")\n\tmainaddr := std.DerivePkgAddr(\"main\")\n\tstd.TestSetOrigCaller(mainaddr)\n\tstd.TestSetOrigPkgAddr(banktestAddr)\n\n\t// get and print balance of mainaddr.\n\t// with the SEND, + 200 gnot given by the TestContext, main should have 300gnot.\n\tbanker := std.GetBanker(std.BankerTypeRealmSend)\n\tmainbal := banker.GetCoins(mainaddr)\n\tprintln(\"main before:\", mainbal)\n\n\t// simulate a Deposit call. use Send + OrigSend to simulate -send.\n\tbanker.SendCoins(mainaddr, banktestAddr, std.Coins{{\"ugnot\", 100_000_000}})\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 100_000_000}}, nil)\n\tres := banktest.Deposit(\"ugnot\", 50_000_000)\n\tprintln(\"Deposit():\", res)\n\n\t// print main balance after.\n\tmainbal = banker.GetCoins(mainaddr)\n\tprintln(\"main after:\", mainbal)\n\n\t// simulate a Render(). banker should have given back all coins.\n\tres = banktest.Render(\"\")\n\tprintln(res)\n}\n\n// Output:\n// main before: 300000000ugnot\n// Deposit(): returned!\n// main after: 250000000ugnot\n// ## recent activity\n//\n// * g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4 100000000ugnot sent, 50000000ugnot returned, at 2009-02-13 11:31pm UTC\n//\n// ## total deposits\n// 50000000ugnot\n" + }, + { + "name": "z_1_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/banktest\"\n)\n\nfunc main() {\n\tbanktestAddr := std.DerivePkgAddr(\"gno.land/r/demo/banktest\")\n\n\t// simulate a Deposit call.\n\tstd.TestSetOrigPkgAddr(banktestAddr)\n\tstd.TestIssueCoins(banktestAddr, std.Coins{{\"ugnot\", 100000000}})\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 100000000}}, nil)\n\tres := banktest.Deposit(\"ugnot\", 101000000)\n\tprintln(res)\n}\n\n// Error:\n// cannot send \"101000000ugnot\", limit \"100000000ugnot\" exceeded with \"\" already spent\n" + }, + { + "name": "z_2_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/banktest\"\n)\n\nfunc main() {\n\tbanktestAddr := std.DerivePkgAddr(\"gno.land/r/demo/banktest\")\n\n\t// print main balance before.\n\tmainaddr := std.DerivePkgAddr(\"main\")\n\tstd.TestSetOrigCaller(mainaddr)\n\n\tbanker := std.GetBanker(std.BankerTypeReadonly)\n\tmainbal := banker.GetCoins(mainaddr)\n\tprintln(\"main before:\", mainbal) // plus OrigSend equals 300.\n\n\t// simulate a Deposit call.\n\tstd.TestSetOrigPkgAddr(banktestAddr)\n\tstd.TestIssueCoins(banktestAddr, std.Coins{{\"ugnot\", 100000000}})\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 100000000}}, nil)\n\tres := banktest.Deposit(\"ugnot\", 55000000)\n\tprintln(\"Deposit():\", res)\n\n\t// print main balance after.\n\tmainbal = banker.GetCoins(mainaddr)\n\tprintln(\"main after:\", mainbal) // now 255.\n\n\t// simulate a Render().\n\tres = banktest.Render(\"\")\n\tprintln(res)\n}\n\n// Output:\n// main before: 200000000ugnot\n// Deposit(): returned!\n// main after: 255000000ugnot\n// ## recent activity\n//\n// * g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4 100000000ugnot sent, 55000000ugnot returned, at 2009-02-13 11:31pm UTC\n//\n// ## total deposits\n// 45000000ugnot\n" + }, + { + "name": "z_3_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n)\n\nfunc main() {\n\tbanktestAddr := std.DerivePkgAddr(\"gno.land/r/demo/banktest\")\n\n\tmainaddr := std.DerivePkgAddr(\"main\")\n\tstd.TestSetOrigCaller(mainaddr)\n\n\tbanker := std.GetBanker(std.BankerTypeRealmSend)\n\tsend := std.Coins{{\"ugnot\", 123}}\n\tbanker.SendCoins(banktestAddr, mainaddr, send)\n\n}\n\n// Error:\n// can only send coins from realm that created banker \"g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4\", not \"g1dv3435088tlrgggf745kaud0ptrkc9v42k8llz\"\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "bar20", + "path": "gno.land/r/demo/bar20", + "files": [ + { + "name": "bar20.gno", + "body": "// Package bar20 is similar to gno.land/r/demo/foo20 but exposes a safe-object\n// that can be used by `maketx run`, another contract importing foo20, and in\n// the future when we'll support `maketx call Token.XXX`.\npackage bar20\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/grc/grc20\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar (\n\tbanker *grc20.Banker // private banker.\n\tToken grc20.Token // public safe-object.\n)\n\nfunc init() {\n\tbanker = grc20.NewBanker(\"Bar\", \"BAR\", 4)\n\tToken = banker.Token()\n}\n\nfunc Faucet() string {\n\tcaller := std.PrevRealm().Addr()\n\tif err := banker.Mint(caller, 1_000_000); err != nil {\n\t\treturn \"error: \" + err.Error()\n\t}\n\treturn \"OK\"\n}\n\nfunc Render(path string) string {\n\tparts := strings.Split(path, \"/\")\n\tc := len(parts)\n\n\tswitch {\n\tcase path == \"\":\n\t\treturn banker.RenderHome() // XXX: should be Token.RenderHome()\n\tcase c == 2 \u0026\u0026 parts[0] == \"balance\":\n\t\towner := std.Address(parts[1])\n\t\tbalance := Token.BalanceOf(owner)\n\t\treturn ufmt.Sprintf(\"%d\\n\", balance)\n\tdefault:\n\t\treturn \"404\\n\"\n\t}\n}\n" + }, + { + "name": "bar20_test.gno", + "body": "package bar20\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/urequire\"\n)\n\nfunc TestPackage(t *testing.T) {\n\talice := testutils.TestAddress(\"alice\")\n\tstd.TestSetRealm(std.NewUserRealm(alice))\n\tstd.TestSetOrigCaller(alice) // XXX: should not need this\n\n\turequire.Equal(t, Token.BalanceOf(alice), uint64(0))\n\turequire.Equal(t, Faucet(), \"OK\")\n\turequire.Equal(t, Token.BalanceOf(alice), uint64(1_000_000))\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/bar20\n\nrequire (\n\tgno.land/p/demo/grc/grc20 v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "deep", + "path": "gno.land/r/demo/deep/very/deep", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/deep/very/deep\n" + }, + { + "name": "render.gno", + "body": "package deep\n\nfunc Render(path string) string {\n\tif path == \"\" {\n\t\treturn \"it works!\"\n\t} else {\n\t\treturn \"hi \" + path\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "echo", + "path": "gno.land/r/demo/echo", + "files": [ + { + "name": "echo.gno", + "body": "package echo\n\n/*\n * This realm echoes the `path` argument it received.\n * Can be used by developers as a simple endpoint to test\n * forbidden characters, for pentesting or simply to\n * test it works.\n *\n * See also r/demo/print (to print various thing like user address)\n */\nfunc Render(path string) string {\n\treturn path\n}\n" + }, + { + "name": "echo_test.gno", + "body": "package echo\n\nimport \"testing\"\n\nfunc Test(t *testing.T) {\n\tif Render(\"aa\") != \"aa\" {\n\t\tt.Fail()\n\t}\n\tif Render(\"\") != \"\" {\n\t\tt.Fail()\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/echo\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "event", + "path": "gno.land/r/demo/event", + "files": [ + { + "name": "event.gno", + "body": "package event\n\nimport (\n\t\"std\"\n)\n\nfunc Emit(value string) {\n\tstd.Emit(\"TAG\", \"key\", value)\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/event\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "foo1155", + "path": "gno.land/r/demo/foo1155", + "files": [ + { + "name": "foo1155.gno", + "body": "package foo1155\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/grc/grc1155\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/r/demo/users\"\n\n\tpusers \"gno.land/p/demo/users\"\n)\n\nvar (\n\tdummyURI = \"ipfs://xyz\"\n\tadmin std.Address = \"g10x5phu0k6p64cwrhfpsc8tk43st9kug6wft530\"\n\tfoo = grc1155.NewBasicGRC1155Token(dummyURI)\n)\n\nfunc init() {\n\tmintGRC1155Token(admin) // @administrator (10)\n}\n\nfunc mintGRC1155Token(owner std.Address) {\n\tfor i := 1; i \u003c= 10; i++ {\n\t\ttid := grc1155.TokenID(ufmt.Sprintf(\"%d\", i))\n\t\tfoo.SafeMint(owner, tid, 100)\n\t}\n}\n\n// Getters\n\nfunc BalanceOf(user pusers.AddressOrName, tid grc1155.TokenID) uint64 {\n\tbalance, err := foo.BalanceOf(users.Resolve(user), tid)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn balance\n}\n\nfunc BalanceOfBatch(ul []pusers.AddressOrName, batch []grc1155.TokenID) []uint64 {\n\tvar usersResolved []std.Address\n\n\tfor i := 0; i \u003c len(ul); i++ {\n\t\tusersResolved[i] = users.Resolve(ul[i])\n\t}\n\tbalanceBatch, err := foo.BalanceOfBatch(usersResolved, batch)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn balanceBatch\n}\n\nfunc IsApprovedForAll(owner, user pusers.AddressOrName) bool {\n\treturn foo.IsApprovedForAll(users.Resolve(owner), users.Resolve(user))\n}\n\n// Setters\n\nfunc SetApprovalForAll(user pusers.AddressOrName, approved bool) {\n\terr := foo.SetApprovalForAll(users.Resolve(user), approved)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc TransferFrom(from, to pusers.AddressOrName, tid grc1155.TokenID, amount uint64) {\n\terr := foo.SafeTransferFrom(users.Resolve(from), users.Resolve(to), tid, amount)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc BatchTransferFrom(from, to pusers.AddressOrName, batch []grc1155.TokenID, amounts []uint64) {\n\terr := foo.SafeBatchTransferFrom(users.Resolve(from), users.Resolve(to), batch, amounts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// Admin\n\nfunc Mint(to pusers.AddressOrName, tid grc1155.TokenID, amount uint64) {\n\tcaller := std.GetOrigCaller()\n\tassertIsAdmin(caller)\n\terr := foo.SafeMint(users.Resolve(to), tid, amount)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc MintBatch(to pusers.AddressOrName, batch []grc1155.TokenID, amounts []uint64) {\n\tcaller := std.GetOrigCaller()\n\tassertIsAdmin(caller)\n\terr := foo.SafeBatchMint(users.Resolve(to), batch, amounts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc Burn(from pusers.AddressOrName, tid grc1155.TokenID, amount uint64) {\n\tcaller := std.GetOrigCaller()\n\tassertIsAdmin(caller)\n\terr := foo.Burn(users.Resolve(from), tid, amount)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc BurnBatch(from pusers.AddressOrName, batch []grc1155.TokenID, amounts []uint64) {\n\tcaller := std.GetOrigCaller()\n\tassertIsAdmin(caller)\n\terr := foo.BatchBurn(users.Resolve(from), batch, amounts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// Render\n\nfunc Render(path string) string {\n\tswitch {\n\tcase path == \"\":\n\t\treturn foo.RenderHome()\n\tdefault:\n\t\treturn \"404\\n\"\n\t}\n}\n\n// Util\n\nfunc assertIsAdmin(address std.Address) {\n\tif address != admin {\n\t\tpanic(\"restricted access\")\n\t}\n}\n" + }, + { + "name": "foo1155_test.gno", + "body": "package foo1155\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/grc/grc1155\"\n\t\"gno.land/p/demo/users\"\n)\n\nfunc TestFoo721(t *testing.T) {\n\tadmin := users.AddressOrName(\"g10x5phu0k6p64cwrhfpsc8tk43st9kug6wft530\")\n\tbob := users.AddressOrName(\"g1ze6et22ces5atv79y4xh38s4kuraey4y2fr6tw\")\n\ttid1 := grc1155.TokenID(\"1\")\n\ttid2 := grc1155.TokenID(\"2\")\n\n\tfor i, tc := range []struct {\n\t\tname string\n\t\texpected interface{}\n\t\tfn func() interface{}\n\t}{\n\t\t{\"BalanceOf(admin, tid1)\", uint64(100), func() interface{} { return BalanceOf(admin, tid1) }},\n\t\t{\"BalanceOf(bob, tid1)\", uint64(0), func() interface{} { return BalanceOf(bob, tid1) }},\n\t\t{\"IsApprovedForAll(admin, bob)\", false, func() interface{} { return IsApprovedForAll(admin, bob) }},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := tc.fn()\n\t\t\tif tc.expected != got {\n\t\t\t\tt.Errorf(\"expected: %v got: %v\", tc.expected, got)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/foo1155\n\nrequire (\n\tgno.land/p/demo/grc/grc1155 v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "foo20", + "path": "gno.land/r/demo/foo20", + "files": [ + { + "name": "foo20.gno", + "body": "// foo20 is a GRC20 token contract where all the GRC20 methods are proxified\n// with top-level functions. see also gno.land/r/demo/bar20.\npackage foo20\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/grc/grc20\"\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/p/demo/ufmt\"\n\tpusers \"gno.land/p/demo/users\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbanker *grc20.Banker\n\tadmin *ownable.Ownable\n\ttoken grc20.Token\n)\n\nfunc init() {\n\tadmin = ownable.NewWithAddress(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\") // @manfred\n\tbanker = grc20.NewBanker(\"Foo\", \"FOO\", 4)\n\tbanker.Mint(admin.Owner(), 1000000*10000) // @administrator (1M)\n\ttoken = banker.Token()\n}\n\nfunc TotalSupply() uint64 { return token.TotalSupply() }\n\nfunc BalanceOf(owner pusers.AddressOrName) uint64 {\n\townerAddr := users.Resolve(owner)\n\treturn token.BalanceOf(ownerAddr)\n}\n\nfunc Allowance(owner, spender pusers.AddressOrName) uint64 {\n\townerAddr := users.Resolve(owner)\n\tspenderAddr := users.Resolve(spender)\n\treturn token.Allowance(ownerAddr, spenderAddr)\n}\n\nfunc Transfer(to pusers.AddressOrName, amount uint64) {\n\ttoAddr := users.Resolve(to)\n\tcheckErr(token.Transfer(toAddr, amount))\n}\n\nfunc Approve(spender pusers.AddressOrName, amount uint64) {\n\tspenderAddr := users.Resolve(spender)\n\tcheckErr(token.Approve(spenderAddr, amount))\n}\n\nfunc TransferFrom(from, to pusers.AddressOrName, amount uint64) {\n\tfromAddr := users.Resolve(from)\n\ttoAddr := users.Resolve(to)\n\tcheckErr(token.TransferFrom(fromAddr, toAddr, amount))\n}\n\n// Faucet is distributing foo20 tokens without restriction (unsafe).\n// For a real token faucet, you should take care of setting limits are asking payment.\nfunc Faucet() {\n\tcaller := std.PrevRealm().Addr()\n\tamount := uint64(1_000 * 10_000) // 1k\n\tcheckErr(banker.Mint(caller, amount))\n}\n\nfunc Mint(to pusers.AddressOrName, amount uint64) {\n\tadmin.AssertCallerIsOwner()\n\ttoAddr := users.Resolve(to)\n\tcheckErr(banker.Mint(toAddr, amount))\n}\n\nfunc Burn(from pusers.AddressOrName, amount uint64) {\n\tadmin.AssertCallerIsOwner()\n\tfromAddr := users.Resolve(from)\n\tcheckErr(banker.Burn(fromAddr, amount))\n}\n\nfunc Render(path string) string {\n\tparts := strings.Split(path, \"/\")\n\tc := len(parts)\n\n\tswitch {\n\tcase path == \"\":\n\t\treturn banker.RenderHome()\n\tcase c == 2 \u0026\u0026 parts[0] == \"balance\":\n\t\towner := pusers.AddressOrName(parts[1])\n\t\townerAddr := users.Resolve(owner)\n\t\tbalance := banker.BalanceOf(ownerAddr)\n\t\treturn ufmt.Sprintf(\"%d\\n\", balance)\n\tdefault:\n\t\treturn \"404\\n\"\n\t}\n}\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" + }, + { + "name": "foo20_test.gno", + "body": "package foo20\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/uassert\"\n\tpusers \"gno.land/p/demo/users\"\n\t\"gno.land/r/demo/users\"\n)\n\nfunc TestReadOnlyPublicMethods(t *testing.T) {\n\tvar (\n\t\tadmin = pusers.AddressOrName(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\t\talice = pusers.AddressOrName(testutils.TestAddress(\"alice\"))\n\t\tbob = pusers.AddressOrName(testutils.TestAddress(\"bob\"))\n\t)\n\n\ttype test struct {\n\t\tname string\n\t\tbalance uint64\n\t\tfn func() uint64\n\t}\n\n\t// check balances #1.\n\t{\n\t\ttests := []test{\n\t\t\t{\"TotalSupply\", 10_000_000_000, func() uint64 { return TotalSupply() }},\n\t\t\t{\"BalanceOf(admin)\", 10_000_000_000, func() uint64 { return BalanceOf(admin) }},\n\t\t\t{\"BalanceOf(alice)\", 0, func() uint64 { return BalanceOf(alice) }},\n\t\t\t{\"Allowance(admin, alice)\", 0, func() uint64 { return Allowance(admin, alice) }},\n\t\t\t{\"BalanceOf(bob)\", 0, func() uint64 { return BalanceOf(bob) }},\n\t\t}\n\t\tfor _, tc := range tests {\n\t\t\tgot := tc.fn()\n\t\t\tuassert.Equal(t, got, tc.balance)\n\t\t}\n\t}\n\n\t// bob uses the faucet.\n\tstd.TestSetOrigCaller(users.Resolve(bob))\n\tFaucet()\n\n\t// check balances #2.\n\t{\n\t\ttests := []test{\n\t\t\t{\"TotalSupply\", 10_010_000_000, func() uint64 { return TotalSupply() }},\n\t\t\t{\"BalanceOf(admin)\", 10_000_000_000, func() uint64 { return BalanceOf(admin) }},\n\t\t\t{\"BalanceOf(alice)\", 0, func() uint64 { return BalanceOf(alice) }},\n\t\t\t{\"Allowance(admin, alice)\", 0, func() uint64 { return Allowance(admin, alice) }},\n\t\t\t{\"BalanceOf(bob)\", 10_000_000, func() uint64 { return BalanceOf(bob) }},\n\t\t}\n\t\tfor _, tc := range tests {\n\t\t\tgot := tc.fn()\n\t\t\tuassert.Equal(t, got, tc.balance)\n\t\t}\n\t}\n}\n\nfunc TestErrConditions(t *testing.T) {\n\tvar (\n\t\tadmin = pusers.AddressOrName(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\t\talice = pusers.AddressOrName(testutils.TestAddress(\"alice\"))\n\t\tempty = pusers.AddressOrName(\"\")\n\t)\n\n\ttype test struct {\n\t\tname string\n\t\tmsg string\n\t\tfn func()\n\t}\n\n\tstd.TestSetOrigCaller(users.Resolve(admin))\n\t{\n\t\ttests := []test{\n\t\t\t{\"Transfer(admin, 1)\", \"cannot send transfer to self\", func() { Transfer(admin, 1) }},\n\t\t\t{\"Approve(empty, 1))\", \"invalid address\", func() { Approve(empty, 1) }},\n\t\t}\n\t\tfor _, tc := range tests {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tuassert.PanicsWithMessage(t, tc.msg, tc.fn)\n\t\t\t})\n\t\t}\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/foo20\n\nrequire (\n\tgno.land/p/demo/grc/grc20 v0.0.0-latest\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "foo721", + "path": "gno.land/r/demo/foo721", + "files": [ + { + "name": "foo721.gno", + "body": "package foo721\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/grc/grc721\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/r/demo/users\"\n\n\tpusers \"gno.land/p/demo/users\"\n)\n\nvar (\n\tadmin std.Address = \"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\"\n\tfoo = grc721.NewBasicNFT(\"FooNFT\", \"FNFT\")\n)\n\nfunc init() {\n\tmintNNFT(admin, 10) // @administrator (10)\n\tmintNNFT(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\", 5) // @hariom (5)\n}\n\nfunc mintNNFT(owner std.Address, n uint64) {\n\tcount := foo.TokenCount()\n\tfor i := count; i \u003c count+n; i++ {\n\t\ttid := grc721.TokenID(ufmt.Sprintf(\"%d\", i))\n\t\tfoo.Mint(owner, tid)\n\t}\n}\n\n// Getters\n\nfunc BalanceOf(user pusers.AddressOrName) uint64 {\n\tbalance, err := foo.BalanceOf(users.Resolve(user))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn balance\n}\n\nfunc OwnerOf(tid grc721.TokenID) std.Address {\n\towner, err := foo.OwnerOf(tid)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn owner\n}\n\nfunc IsApprovedForAll(owner, user pusers.AddressOrName) bool {\n\treturn foo.IsApprovedForAll(users.Resolve(owner), users.Resolve(user))\n}\n\nfunc GetApproved(tid grc721.TokenID) std.Address {\n\taddr, err := foo.GetApproved(tid)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn addr\n}\n\n// Setters\n\nfunc Approve(user pusers.AddressOrName, tid grc721.TokenID) {\n\terr := foo.Approve(users.Resolve(user), tid)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc SetApprovalForAll(user pusers.AddressOrName, approved bool) {\n\terr := foo.SetApprovalForAll(users.Resolve(user), approved)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc TransferFrom(from, to pusers.AddressOrName, tid grc721.TokenID) {\n\terr := foo.TransferFrom(users.Resolve(from), users.Resolve(to), tid)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// Admin\n\nfunc Mint(to pusers.AddressOrName, tid grc721.TokenID) {\n\tcaller := std.PrevRealm().Addr()\n\tassertIsAdmin(caller)\n\terr := foo.Mint(users.Resolve(to), tid)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc Burn(tid grc721.TokenID) {\n\tcaller := std.PrevRealm().Addr()\n\tassertIsAdmin(caller)\n\terr := foo.Burn(tid)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// Render\n\nfunc Render(path string) string {\n\tswitch {\n\tcase path == \"\":\n\t\treturn foo.RenderHome()\n\tdefault:\n\t\treturn \"404\\n\"\n\t}\n}\n\n// Util\n\nfunc assertIsAdmin(address std.Address) {\n\tif address != admin {\n\t\tpanic(\"restricted access\")\n\t}\n}\n" + }, + { + "name": "foo721_test.gno", + "body": "package foo721\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/grc/grc721\"\n\t\"gno.land/r/demo/users\"\n\n\tpusers \"gno.land/p/demo/users\"\n)\n\nfunc TestFoo721(t *testing.T) {\n\tadmin := pusers.AddressOrName(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\thariom := pusers.AddressOrName(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\tfor i, tc := range []struct {\n\t\tname string\n\t\texpected interface{}\n\t\tfn func() interface{}\n\t}{\n\t\t{\"BalanceOf(admin)\", uint64(10), func() interface{} { return BalanceOf(admin) }},\n\t\t{\"BalanceOf(hariom)\", uint64(5), func() interface{} { return BalanceOf(hariom) }},\n\t\t{\"OwnerOf(0)\", users.Resolve(admin), func() interface{} { return OwnerOf(grc721.TokenID(\"0\")) }},\n\t\t{\"IsApprovedForAll(admin, hariom)\", false, func() interface{} { return IsApprovedForAll(admin, hariom) }},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := tc.fn()\n\t\t\tif tc.expected != got {\n\t\t\t\tt.Errorf(\"expected: %v got: %v\", tc.expected, got)\n\t\t\t}\n\t\t})\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/foo721\n\nrequire (\n\tgno.land/p/demo/grc/grc721 v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "foo20", + "path": "gno.land/r/demo/grc20factory", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/grc20factory\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/grc/grc20 v0.0.0-latest\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "grc20factory.gno", + "body": "package foo20\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/grc/grc20\"\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar instances avl.Tree // symbol -\u003e instance\n\nfunc New(name, symbol string, decimals uint, initialMint, faucet uint64) {\n\tcaller := std.PrevRealm().Addr()\n\tNewWithAdmin(name, symbol, decimals, initialMint, faucet, caller)\n}\n\nfunc NewWithAdmin(name, symbol string, decimals uint, initialMint, faucet uint64, admin std.Address) {\n\texists := instances.Has(symbol)\n\tif exists {\n\t\tpanic(\"token already exists\")\n\t}\n\n\tbanker := grc20.NewBanker(name, symbol, decimals)\n\tif initialMint \u003e 0 {\n\t\tbanker.Mint(admin, initialMint)\n\t}\n\n\tinst := instance{\n\t\tbanker: banker,\n\t\tadmin: ownable.NewWithAddress(admin),\n\t\tfaucet: faucet,\n\t}\n\n\tinstances.Set(symbol, \u0026inst)\n}\n\ntype instance struct {\n\tbanker *grc20.Banker\n\tadmin *ownable.Ownable\n\tfaucet uint64 // per-request amount. disabled if 0.\n}\n\nfunc (inst instance) Token() grc20.Token { return inst.banker.Token() }\n\nfunc TotalSupply(symbol string) uint64 {\n\tinst := mustGetInstance(symbol)\n\treturn inst.Token().TotalSupply()\n}\n\nfunc BalanceOf(symbol string, owner std.Address) uint64 {\n\tinst := mustGetInstance(symbol)\n\treturn inst.Token().BalanceOf(owner)\n}\n\nfunc Allowance(symbol string, owner, spender std.Address) uint64 {\n\tinst := mustGetInstance(symbol)\n\treturn inst.Token().Allowance(owner, spender)\n}\n\nfunc Transfer(symbol string, to std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tcheckErr(inst.Token().Transfer(to, amount))\n}\n\nfunc Approve(symbol string, spender std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tcheckErr(inst.Token().Approve(spender, amount))\n}\n\nfunc TransferFrom(symbol string, from, to std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tcheckErr(inst.Token().TransferFrom(from, to, amount))\n}\n\n// faucet.\nfunc Faucet(symbol string) {\n\tinst := mustGetInstance(symbol)\n\tif inst.faucet == 0 {\n\t\tpanic(\"faucet disabled for this token\")\n\t}\n\t// FIXME: add limits?\n\t// FIXME: add payment in gnot?\n\tcaller := std.PrevRealm().Addr()\n\tcheckErr(inst.banker.Mint(caller, inst.faucet))\n}\n\nfunc Mint(symbol string, to std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tinst.admin.AssertCallerIsOwner()\n\tcheckErr(inst.banker.Mint(to, amount))\n}\n\nfunc Burn(symbol string, from std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tinst.admin.AssertCallerIsOwner()\n\tcheckErr(inst.banker.Burn(from, amount))\n}\n\nfunc Render(path string) string {\n\tparts := strings.Split(path, \"/\")\n\tc := len(parts)\n\n\tswitch {\n\tcase path == \"\":\n\t\treturn \"TODO: list existing tokens and admins\"\n\tcase c == 1:\n\t\tsymbol := parts[0]\n\t\tinst := mustGetInstance(symbol)\n\t\treturn inst.banker.RenderHome()\n\tcase c == 3 \u0026\u0026 parts[1] == \"balance\":\n\t\tsymbol := parts[0]\n\t\tinst := mustGetInstance(symbol)\n\t\towner := std.Address(parts[2])\n\t\tbalance := inst.Token().BalanceOf(owner)\n\t\treturn ufmt.Sprintf(\"%d\", balance)\n\tdefault:\n\t\treturn \"404\\n\"\n\t}\n}\n\nfunc mustGetInstance(symbol string) *instance {\n\tt, exists := instances.Get(symbol)\n\tif !exists {\n\t\tpanic(\"token instance does not exist\")\n\t}\n\treturn t.(*instance)\n}\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" + }, + { + "name": "grc20factory_test.gno", + "body": "package foo20\n\nimport (\n\t\"std\"\n\t\"testing\"\n)\n\nfunc TestReadOnlyPublicMethods(t *testing.T) {\n\tadmin := std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\tmanfred := std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\tunknown := std.Address(\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\") // valid but never used.\n\tNewWithAdmin(\"Foo\", \"FOO\", 4, 10_000*1_000_000, 0, admin)\n\tNewWithAdmin(\"Bar\", \"BAR\", 4, 10_000*1_000, 0, admin)\n\tmustGetInstance(\"FOO\").banker.Mint(manfred, 100_000_000)\n\n\ttype test struct {\n\t\tname string\n\t\tbalance uint64\n\t\tfn func() uint64\n\t}\n\n\t// check balances #1.\n\t{\n\t\ttests := []test{\n\t\t\t{\"TotalSupply\", 10_100_000_000, func() uint64 { return TotalSupply(\"FOO\") }},\n\t\t\t{\"BalanceOf(admin)\", 10_000_000_000, func() uint64 { return BalanceOf(\"FOO\", admin) }},\n\t\t\t{\"BalanceOf(manfred)\", 100_000_000, func() uint64 { return BalanceOf(\"FOO\", manfred) }},\n\t\t\t{\"Allowance(admin, manfred)\", 0, func() uint64 { return Allowance(\"FOO\", admin, manfred) }},\n\t\t\t{\"BalanceOf(unknown)\", 0, func() uint64 { return BalanceOf(\"FOO\", unknown) }},\n\t\t}\n\t\tfor _, tc := range tests {\n\t\t\tif tc.fn() != tc.balance {\n\t\t\t\tt.Errorf(\"%s: have: %d want: %d\", tc.name, tc.fn(), tc.balance)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n\n\t// unknown uses the faucet.\n\tstd.TestSetOrigCaller(unknown)\n\tFaucet(\"FOO\")\n\n\t// check balances #2.\n\t{\n\t\ttests := []test{\n\t\t\t{\"TotalSupply\", 10_110_000_000, func() uint64 { return TotalSupply(\"FOO\") }},\n\t\t\t{\"BalanceOf(admin)\", 10_000_000_000, func() uint64 { return BalanceOf(\"FOO\", admin) }},\n\t\t\t{\"BalanceOf(manfred)\", 100_000_000, func() uint64 { return BalanceOf(\"FOO\", manfred) }},\n\t\t\t{\"Allowance(admin, manfred)\", 0, func() uint64 { return Allowance(\"FOO\", admin, manfred) }},\n\t\t\t{\"BalanceOf(unknown)\", 10_000_000, func() uint64 { return BalanceOf(\"FOO\", unknown) }},\n\t\t}\n\t\tfor _, tc := range tests {\n\t\t\tif tc.fn() != tc.balance {\n\t\t\t\tt.Errorf(\"%s: have: %d want: %d\", tc.name, tc.fn(), tc.balance)\n\t\t\t}\n\t\t}\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "groups", + "path": "gno.land/r/demo/groups", + "files": [ + { + "name": "README.md", + "body": "### - test package\n\n ./build/gno test examples/gno.land/r/demo/groups/\n\n### - add pkg\n\n ./build/gnokey maketx addpkg -pkgdir \"examples/gno.land/r/demo/groups\" -deposit 100000000ugnot -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1 \n\n### - create group\n\n ./build/gnokey maketx call -func \"CreateGroup\" -args \"dao_trinity_ngo\" -gas-fee \"1000000ugnot\" -gas-wanted 4000000 -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1 \n\n### - add member\n\n ./build/gnokey maketx call -func \"AddMember\" -args \"1\" -args \"g1hd3gwzevxlqmd3jsf64mpfczag8a8e5j2wdn3c\" -args 12 -args \"i am new user\" -gas-fee \"1000000ugnot\" -gas-wanted \"4000000\" -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1\n\n### - delete member\n\n ./build/gnokey maketx call -func \"DeleteMember\" -args \"1\" -args \"0\" -gas-fee \"1000000ugnot\" -gas-wanted \"4000000\" -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1\n\n### - delete group\n\n ./build/gnokey maketx call -func \"DeleteGroup\" -args \"1\" -gas-fee \"1000000ugnot\" -gas-wanted \"4000000\" -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1\n\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/groups\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" + }, + { + "name": "group.gno", + "body": "package groups\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\ntype GroupID uint64\n\nfunc (gid GroupID) String() string {\n\treturn strconv.Itoa(int(gid))\n}\n\ntype Group struct {\n\tid GroupID\n\turl string\n\tname string\n\tlastMemberID MemberID\n\tmembers avl.Tree\n\tcreator std.Address\n\tcreatedAt time.Time\n}\n\nfunc newGroup(url string, name string, creator std.Address) *Group {\n\tif !reName.MatchString(name) {\n\t\tpanic(\"invalid name: \" + name)\n\t}\n\tif gGroupsByName.Has(name) {\n\t\tpanic(\"Group with such name already exists\")\n\t}\n\treturn \u0026Group{\n\t\tid: incGetGroupID(),\n\t\turl: url,\n\t\tname: name,\n\t\tcreator: creator,\n\t\tmembers: avl.Tree{},\n\t\tcreatedAt: time.Now(),\n\t}\n}\n\nfunc (group *Group) newMember(id MemberID, address std.Address, weight int, metadata string) *Member {\n\tif group.members.Has(address.String()) {\n\t\tpanic(\"this member for this group already exists\")\n\t}\n\treturn \u0026Member{\n\t\tid: id,\n\t\taddress: address,\n\t\tweight: weight,\n\t\tmetadata: metadata,\n\t\tcreatedAt: time.Now(),\n\t}\n}\n\nfunc (group *Group) HasPermission(addr std.Address, perm Permission) bool {\n\tif group.creator != addr {\n\t\treturn false\n\t}\n\treturn isValidPermission(perm)\n}\n\nfunc (group *Group) RenderGroup() string {\n\tstr := \"Group ID: \" + groupIDKey(group.id) + \"\\n\\n\" +\n\t\t\"Group Name: \" + group.name + \"\\n\\n\" +\n\t\t\"Group Creator: \" + usernameOf(group.creator) + \"\\n\\n\" +\n\t\t\"Group createdAt: \" + group.createdAt.String() + \"\\n\\n\" +\n\t\t\"Group Last MemberID: \" + memberIDKey(group.lastMemberID) + \"\\n\\n\"\n\n\tstr += \"Group Members: \\n\\n\"\n\tgroup.members.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tmember := value.(*Member)\n\t\tstr += member.getMemberStr()\n\t\treturn false\n\t})\n\treturn str\n}\n\nfunc (group *Group) deleteGroup() {\n\tgidkey := groupIDKey(group.id)\n\t_, gGroupsRemoved := gGroups.Remove(gidkey)\n\tif !gGroupsRemoved {\n\t\tpanic(\"group does not exist with id \" + group.id.String())\n\t}\n\tgGroupsByName.Remove(group.name)\n}\n\nfunc (group *Group) deleteMember(mid MemberID) {\n\tgidkey := groupIDKey(group.id)\n\tif !gGroups.Has(gidkey) {\n\t\tpanic(\"group does not exist with id \" + group.id.String())\n\t}\n\n\tg := getGroup(group.id)\n\tmidkey := memberIDKey(mid)\n\tg.members.Remove(midkey)\n}\n" + }, + { + "name": "groups.gno", + "body": "package groups\n\nimport (\n\t\"regexp\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\n//----------------------------------------\n// Realm (package) state\n\nvar (\n\tgGroups avl.Tree // id -\u003e *Group\n\tgGroupsCtr int // increments Group.id\n\tgGroupsByName avl.Tree // name -\u003e *Group\n)\n\n//----------------------------------------\n// Constants\n\nvar reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{2,29}$`)\n" + }, + { + "name": "member.gno", + "body": "package groups\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"time\"\n)\n\ntype MemberID uint64\n\ntype Member struct {\n\tid MemberID\n\taddress std.Address\n\tweight int\n\tmetadata string\n\tcreatedAt time.Time\n}\n\nfunc (mid MemberID) String() string {\n\treturn strconv.Itoa(int(mid))\n}\n\nfunc (member *Member) getMemberStr() string {\n\tmemberDataStr := \"\"\n\tmemberDataStr += \"\\t\\t\\t[\" + memberIDKey(member.id) + \", \" + member.address.String() + \", \" + strconv.Itoa(member.weight) + \", \" + member.metadata + \", \" + member.createdAt.String() + \"],\\n\\n\"\n\treturn memberDataStr\n}\n" + }, + { + "name": "misc.gno", + "body": "package groups\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/r/demo/users\"\n)\n\n//----------------------------------------\n// private utility methods\n// XXX ensure these cannot be called from public.\n\nfunc getGroup(gid GroupID) *Group {\n\tgidkey := groupIDKey(gid)\n\tgroup_, exists := gGroups.Get(gidkey)\n\tif !exists {\n\t\tpanic(\"group id (\" + gid.String() + \") does not exists\")\n\t}\n\tgroup := group_.(*Group)\n\treturn group\n}\n\nfunc incGetGroupID() GroupID {\n\tgGroupsCtr++\n\treturn GroupID(gGroupsCtr)\n}\n\nfunc padLeft(str string, length int) string {\n\tif len(str) \u003e= length {\n\t\treturn str\n\t}\n\treturn strings.Repeat(\" \", length-len(str)) + str\n}\n\nfunc padZero(u64 uint64, length int) string {\n\tstr := strconv.Itoa(int(u64))\n\tif len(str) \u003e= length {\n\t\treturn str\n\t}\n\treturn strings.Repeat(\"0\", length-len(str)) + str\n}\n\nfunc groupIDKey(gid GroupID) string {\n\treturn padZero(uint64(gid), 10)\n}\n\nfunc memberIDKey(mid MemberID) string {\n\treturn padZero(uint64(mid), 10)\n}\n\nfunc indentBody(indent string, body string) string {\n\tlines := strings.Split(body, \"\\n\")\n\tres := \"\"\n\tfor i, line := range lines {\n\t\tif i \u003e 0 {\n\t\t\tres += \"\\n\"\n\t\t}\n\t\tres += indent + line\n\t}\n\treturn res\n}\n\n// NOTE: length must be greater than 3.\nfunc summaryOf(str string, length int) string {\n\tlines := strings.SplitN(str, \"\\n\", 2)\n\tline := lines[0]\n\tif len(line) \u003e length {\n\t\tline = line[:(length-3)] + \"...\"\n\t} else if len(lines) \u003e 1 {\n\t\t// len(line) \u003c= 80\n\t\tline = line + \"...\"\n\t}\n\treturn line\n}\n\nfunc displayAddressMD(addr std.Address) string {\n\tuser := users.GetUserByAddress(addr)\n\tif user == nil {\n\t\treturn \"[\" + addr.String() + \"](/r/demo/users:\" + addr.String() + \")\"\n\t}\n\treturn \"[@\" + user.Name + \"](/r/demo/users:\" + user.Name + \")\"\n}\n\nfunc usernameOf(addr std.Address) string {\n\tuser := users.GetUserByAddress(addr)\n\tif user == nil {\n\t\tpanic(\"user not found\")\n\t}\n\treturn user.Name\n}\n\nfunc isValidPermission(perm Permission) bool {\n\treturn perm == EditPermission || perm == DeletePermission\n}\n" + }, + { + "name": "public.gno", + "body": "package groups\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/users\"\n)\n\n//----------------------------------------\n// Public facing functions\n\nfunc GetGroupIDFromName(name string) (GroupID, bool) {\n\tgroupI, exists := gGroupsByName.Get(name)\n\tif !exists {\n\t\treturn 0, false\n\t}\n\treturn groupI.(*Group).id, true\n}\n\nfunc CreateGroup(name string) GroupID {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tusernameOf(caller)\n\turl := \"/r/demo/groups:\" + name\n\tgroup := newGroup(url, name, caller)\n\tgidkey := groupIDKey(group.id)\n\tgGroups.Set(gidkey, group)\n\tgGroupsByName.Set(name, group)\n\treturn group.id\n}\n\nfunc AddMember(gid GroupID, address string, weight int, metadata string) MemberID {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tusernameOf(caller)\n\tgroup := getGroup(gid)\n\tif !group.HasPermission(caller, EditPermission) {\n\t\tpanic(\"unauthorized to edit group\")\n\t}\n\tuser := users.GetUserByAddress(std.Address(address))\n\tif user == nil {\n\t\tpanic(\"unknown address \" + address)\n\t}\n\tmid := group.lastMemberID\n\tmember := group.newMember(mid, std.Address(address), weight, metadata)\n\tmidkey := memberIDKey(mid)\n\tgroup.members.Set(midkey, member)\n\tmid++\n\tgroup.lastMemberID = mid\n\treturn member.id\n}\n\nfunc DeleteGroup(gid GroupID) {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tgroup := getGroup(gid)\n\tif !group.HasPermission(caller, DeletePermission) {\n\t\tpanic(\"unauthorized to delete group\")\n\t}\n\tgroup.deleteGroup()\n}\n\nfunc DeleteMember(gid GroupID, mid MemberID) {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tgroup := getGroup(gid)\n\tif !group.HasPermission(caller, DeletePermission) {\n\t\tpanic(\"unauthorized to delete member\")\n\t}\n\tgroup.deleteMember(mid)\n}\n" + }, + { + "name": "render.gno", + "body": "package groups\n\nimport (\n\t\"strings\"\n)\n\n//----------------------------------------\n// Render functions\n\nfunc RenderGroup(gid GroupID) string {\n\tgroup := getGroup(gid)\n\tif group == nil {\n\t\treturn \"missing Group\"\n\t}\n\treturn group.RenderGroup()\n}\n\nfunc Render(path string) string {\n\tif path == \"\" {\n\t\tstr := \"List of all Groups:\\n\\n\"\n\t\tgGroups.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tgroup := value.(*Group)\n\t\t\tstr += \" * [\" + group.name + \"](\" + group.url + \")\\n\"\n\t\t\treturn false\n\t\t})\n\t\treturn str\n\t}\n\tparts := strings.Split(path, \"/\")\n\tif len(parts) == 1 {\n\t\t// /r/demo/groups:Group_NAME\n\t\tname := parts[0]\n\t\tgroupI, exists := gGroupsByName.Get(name)\n\t\tif !exists {\n\t\t\treturn \"Group does not exist: \" + name\n\t\t}\n\t\treturn groupI.(*Group).RenderGroup()\n\t} else {\n\t\treturn \"unrecognized path \" + path\n\t}\n}\n" + }, + { + "name": "role.gno", + "body": "package groups\n\ntype Permission string\n\nconst (\n\tDeletePermission Permission = \"role:delete\"\n\tEditPermission Permission = \"role:edit\"\n)\n" + }, + { + "name": "z_0_a_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\nimport (\n\t\"gno.land/r/demo/groups\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\tprintln(groups.Render(\"\"))\n}\n\n// Error:\n// user not found\n" + }, + { + "name": "z_0_b_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\nimport (\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\tprintln(groups.Render(\"\"))\n}\n\n// Error:\n// payment must not be less than 20000000\n" + }, + { + "name": "z_0_c_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\tprintln(groups.Render(\"\"))\n}\n\n// Output:\n// 1\n// List of all Groups:\n//\n// * [test_group](/r/demo/groups:test_group)\n" + }, + { + "name": "z_1_a_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser0\", \"my profile 1\")\n\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"gnouser1\")\n\tusers.Invite(test1.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test1)\n\tusers.Register(caller, \"gnouser1\", \"my other profile 1\")\n\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest2 := testutils.TestAddress(\"gnouser2\")\n\tusers.Invite(test2.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test2)\n\tusers.Register(caller, \"gnouser2\", \"my other profile 2\")\n\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest3 := testutils.TestAddress(\"gnouser3\")\n\tusers.Invite(test3.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test3)\n\tusers.Register(caller, \"gnouser3\", \"my other profile 3\")\n\n\tstd.TestSetOrigCaller(caller)\n\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\n\tgroups.AddMember(gid, test3.String(), 32, \"i am from UAE\")\n\tprintln(groups.Render(\"test_group\"))\n}\n\n// Output:\n// 1\n// Group ID: 0000000001\n//\n// Group Name: test_group\n//\n// Group Creator: gnouser0\n//\n// Group createdAt: 2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001\n//\n// Group Last MemberID: 0000000001\n//\n// Group Members:\n//\n// \t\t\t[0000000000, g1vahx7atnv4erxh6lta047h6lta047h6ll85gpy, 32, i am from UAE, 2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001],\n" + }, + { + "name": "z_1_b_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\tgroups.AddMember(2, \"g1vahx7atnv4erxh6lta047h6lta047h6ll85gpy\", 55, \"metadata3\")\n\tprintln(groups.Render(\"\"))\n}\n\n// Error:\n// group id (2) does not exists\n" + }, + { + "name": "z_1_c_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/groups\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\n\t// add member via anon user\n\ttest2 := testutils.TestAddress(\"test2\")\n\tstd.TestSetOrigCaller(test2)\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 9000000}}, nil)\n\n\tgroups.AddMember(gid, test2.String(), 42, \"metadata3\")\n}\n\n// Error:\n// user not found\n" + }, + { + "name": "z_2_a_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nconst admin = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc main() {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(\"\", \"gnouser0\", \"my profile 1\")\n\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest1 := testutils.TestAddress(\"gnouser1\")\n\tusers.Invite(test1.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test1)\n\tusers.Register(caller, \"gnouser1\", \"my other profile 1\")\n\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest2 := testutils.TestAddress(\"gnouser2\")\n\tusers.Invite(test2.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test2)\n\tusers.Register(caller, \"gnouser2\", \"my other profile 2\")\n\n\tstd.TestSetOrigCaller(admin)\n\tusers.GrantInvites(caller.String() + \":1\")\n\t// switch back to caller\n\tstd.TestSetOrigCaller(caller)\n\t// invite another addr\n\ttest3 := testutils.TestAddress(\"gnouser3\")\n\tusers.Invite(test3.String())\n\t// switch to test1\n\tstd.TestSetOrigCaller(test3)\n\tusers.Register(caller, \"gnouser3\", \"my other profile 3\")\n\n\tstd.TestSetOrigCaller(caller)\n\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\n\tgroups.AddMember(gid, test2.String(), 42, \"metadata3\")\n\n\tgroups.DeleteMember(gid, 0)\n\tprintln(groups.RenderGroup(gid))\n}\n\n// Output:\n// 1\n// Group ID: 0000000001\n//\n// Group Name: test_group\n//\n// Group Creator: gnouser0\n//\n// Group createdAt: 2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001\n//\n// Group Last MemberID: 0000000001\n//\n// Group Members:\n" + }, + { + "name": "z_2_b_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\tgroups.DeleteMember(2, 0)\n\tprintln(groups.Render(\"\"))\n}\n\n// Error:\n// group id (2) does not exists\n" + }, + { + "name": "z_2_d_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\n\t// delete member via anon user\n\ttest2 := testutils.TestAddress(\"test2\")\n\tstd.TestSetOrigCaller(test2)\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 9000000}}, nil)\n\n\tgroups.DeleteMember(gid, 0)\n\tprintln(groups.Render(\"\"))\n}\n\n// Error:\n// unauthorized to delete member\n" + }, + { + "name": "z_2_e_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\tgroups.DeleteGroup(gid)\n\tprintln(groups.Render(\"\"))\n}\n\n// Output:\n// 1\n// List of all Groups:\n" + }, + { + "name": "z_2_f_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\tgroups.DeleteGroup(20)\n\tprintln(groups.Render(\"\"))\n}\n\n// Error:\n// group id (20) does not exists\n" + }, + { + "name": "z_2_g_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/groups_test\npackage groups_test\n\n// SEND: 200000000ugnot\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/groups\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar gid groups.GroupID\n\nfunc main() {\n\tusers.Register(\"\", \"gnouser\", \"my profile\")\n\tgid = groups.CreateGroup(\"test_group\")\n\tprintln(gid)\n\n\t// delete group via anon user\n\ttest2 := testutils.TestAddress(\"test2\")\n\tstd.TestSetOrigCaller(test2)\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 9000000}}, nil)\n\n\tgroups.DeleteGroup(gid)\n\tprintln(groups.Render(\"\"))\n}\n\n// Error:\n// unauthorized to delete group\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "keystore", + "path": "gno.land/r/demo/keystore", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/keystore\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "keystore.gno", + "body": "package keystore\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar data avl.Tree\n\nconst (\n\tBaseURL = \"/r/demo/keystore\"\n\tStatusOK = \"ok\"\n\tStatusNoUser = \"user not found\"\n\tStatusNotFound = \"key not found\"\n\tStatusNoWriteAccess = \"no write access\"\n\tStatusCouldNotExecute = \"could not execute\"\n\tStatusNoDatabases = \"no databases\"\n)\n\nfunc init() {\n\tdata = avl.Tree{} // user -\u003e avl.Tree\n}\n\n// KeyStore stores the owner-specific avl.Tree\ntype KeyStore struct {\n\tOwner std.Address\n\tData avl.Tree\n}\n\n// Set will set a value to a key\n// requires write-access (original caller must be caller)\nfunc Set(k, v string) string {\n\torigOwner := std.GetOrigCaller()\n\treturn set(origOwner.String(), k, v)\n}\n\n// set (private) will set a key to value\n// requires write-access (original caller must be caller)\nfunc set(owner, k, v string) string {\n\torigOwner := std.GetOrigCaller()\n\tif origOwner.String() != owner {\n\t\treturn StatusNoWriteAccess\n\t}\n\tvar keystore *KeyStore\n\tkeystoreInterface, exists := data.Get(owner)\n\tif !exists {\n\t\tkeystore = \u0026KeyStore{\n\t\t\tOwner: origOwner,\n\t\t\tData: avl.Tree{},\n\t\t}\n\t\tdata.Set(owner, keystore)\n\t} else {\n\t\tkeystore = keystoreInterface.(*KeyStore)\n\t}\n\tkeystore.Data.Set(k, v)\n\treturn StatusOK\n}\n\n// Remove removes a key\n// requires write-access (original owner must be caller)\nfunc Remove(k string) string {\n\torigOwner := std.GetOrigCaller()\n\treturn remove(origOwner.String(), k)\n}\n\n// remove (private) removes a key\n// requires write-access (original owner must be caller)\nfunc remove(owner, k string) string {\n\torigOwner := std.GetOrigCaller()\n\tif origOwner.String() != owner {\n\t\treturn StatusNoWriteAccess\n\t}\n\tvar keystore *KeyStore\n\tkeystoreInterface, exists := data.Get(owner)\n\tif !exists {\n\t\tkeystore = \u0026KeyStore{\n\t\t\tOwner: origOwner,\n\t\t\tData: avl.Tree{},\n\t\t}\n\t\tdata.Set(owner, keystore)\n\t} else {\n\t\tkeystore = keystoreInterface.(*KeyStore)\n\t}\n\t_, removed := keystore.Data.Remove(k)\n\tif !removed {\n\t\treturn StatusCouldNotExecute\n\t}\n\treturn StatusOK\n}\n\n// Get returns a value for a key\n// read-only\nfunc Get(k string) string {\n\torigOwner := std.GetOrigCaller()\n\treturn remove(origOwner.String(), k)\n}\n\n// get (private) returns a value for a key\n// read-only\nfunc get(owner, k string) string {\n\tkeystoreInterface, exists := data.Get(owner)\n\tif !exists {\n\t\treturn StatusNoUser\n\t}\n\tkeystore := keystoreInterface.(*KeyStore)\n\tval, found := keystore.Data.Get(k)\n\tif !found {\n\t\treturn StatusNotFound\n\t}\n\treturn val.(string)\n}\n\n// Size returns size of database\n// read-only\nfunc Size() string {\n\torigOwner := std.GetOrigCaller()\n\treturn size(origOwner.String())\n}\n\nfunc size(owner string) string {\n\tkeystoreInterface, exists := data.Get(owner)\n\tif !exists {\n\t\treturn StatusNoUser\n\t}\n\tkeystore := keystoreInterface.(*KeyStore)\n\treturn ufmt.Sprintf(\"%d\", keystore.Data.Size())\n}\n\n// Render provides read-only url access to the functions of the keystore\n// \"\" -\u003e show all keystores listed by owner\n// \"owner\" -\u003e show all keys for that owner's keystore\n// \"owner:size\" -\u003e returns size of owner's keystore\n// \"owner:get:key\" -\u003e show value for that key in owner's keystore\nfunc Render(p string) string {\n\tvar response string\n\targs := strings.Split(p, \":\")\n\tnumArgs := len(args)\n\tif p == \"\" {\n\t\tnumArgs = 0\n\t}\n\tswitch numArgs {\n\tcase 0:\n\t\tif data.Size() == 0 {\n\t\t\treturn StatusNoDatabases\n\t\t}\n\t\tdata.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tks := value.(*KeyStore)\n\t\t\tresponse += ufmt.Sprintf(\"- [%s](%s:%s) (%d keys)\\n\", ks.Owner, BaseURL, ks.Owner, ks.Data.Size())\n\t\t\treturn false\n\t\t})\n\tcase 1:\n\t\towner := args[0]\n\t\tkeystoreInterface, exists := data.Get(owner)\n\t\tif !exists {\n\t\t\treturn StatusNoUser\n\t\t}\n\t\tks := keystoreInterface.(*KeyStore)\n\t\ti := 0\n\t\tresponse += ufmt.Sprintf(\"# %s database\\n\\n\", ks.Owner)\n\t\tks.Data.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tresponse += ufmt.Sprintf(\"- %d [%s](%s:%s:get:%s)\\n\", i, key, BaseURL, ks.Owner, key)\n\t\t\ti++\n\t\t\treturn false\n\t\t})\n\tcase 2:\n\t\towner := args[0]\n\t\tcmd := args[1]\n\t\tif cmd == \"size\" {\n\t\t\treturn size(owner)\n\t\t}\n\tcase 3:\n\t\towner := args[0]\n\t\tcmd := args[1]\n\t\tkey := args[2]\n\t\tif cmd == \"get\" {\n\t\t\treturn get(owner, key)\n\t\t}\n\t}\n\n\treturn response\n}\n" + }, + { + "name": "keystore_test.gno", + "body": "package keystore\n\nimport (\n\t\"std\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n)\n\nfunc TestRender(t *testing.T) {\n\tconst (\n\t\tauthor1 std.Address = testutils.TestAddress(\"author1\")\n\t\tauthor2 std.Address = testutils.TestAddress(\"author2\")\n\t)\n\n\ttt := []struct {\n\t\tcaller std.Address\n\t\towner std.Address\n\t\tps []string\n\t\texp string\n\t}{\n\t\t// can set database if the owner is the caller\n\t\t{author1, author1, []string{\"set\", \"hello\", \"gno\"}, StatusOK},\n\t\t{author1, author1, []string{\"size\"}, \"1\"},\n\t\t{author1, author1, []string{\"set\", \"hello\", \"world\"}, StatusOK},\n\t\t{author1, author1, []string{\"size\"}, \"1\"},\n\t\t{author1, author1, []string{\"set\", \"hi\", \"gno\"}, StatusOK},\n\t\t{author1, author1, []string{\"size\"}, \"2\"},\n\t\t// only owner can remove\n\t\t{author1, author1, []string{\"remove\", \"hi\"}, StatusOK},\n\t\t{author1, author1, []string{\"get\", \"hi\"}, StatusNotFound},\n\t\t{author1, author1, []string{\"size\"}, \"1\"},\n\t\t// add back\n\t\t{author1, author1, []string{\"set\", \"hi\", \"gno\"}, StatusOK},\n\t\t{author1, author1, []string{\"size\"}, \"2\"},\n\n\t\t// different owner has different database\n\t\t{author2, author2, []string{\"set\", \"hello\", \"universe\"}, StatusOK},\n\t\t// either author can get the other info\n\t\t{author1, author2, []string{\"get\", \"hello\"}, \"universe\"},\n\t\t// either author can get the other info\n\t\t{author2, author1, []string{\"get\", \"hello\"}, \"world\"},\n\t\t{author1, author2, []string{\"get\", \"hello\"}, \"universe\"},\n\t\t// anyone can view the databases\n\t\t{author1, author2, []string{}, `- [g1v96hg6r0wgc47h6lta047h6lta047h6lm33tq6](/r/demo/keystore:g1v96hg6r0wgc47h6lta047h6lta047h6lm33tq6) (2 keys)\n- [g1v96hg6r0wge97h6lta047h6lta047h6lyz7c00](/r/demo/keystore:g1v96hg6r0wge97h6lta047h6lta047h6lyz7c00) (1 keys)`},\n\t\t// anyone can view the keys in a database\n\t\t{author1, author2, []string{\"\"}, `# g1v96hg6r0wge97h6lta047h6lta047h6lyz7c00 database\n\n- 0 [hello](/r/demo/keystore:g1v96hg6r0wge97h6lta047h6lta047h6lyz7c00:get:hello)`},\n\t}\n\tfor _, tc := range tt {\n\t\tp := \"\"\n\t\tif len(tc.ps) \u003e 0 {\n\t\t\tp = tc.owner.String()\n\t\t\tfor i, psv := range tc.ps {\n\t\t\t\tp += \":\" + psv\n\t\t\t}\n\t\t}\n\t\tp = strings.TrimSuffix(p, \":\")\n\t\tt.Run(p, func(t *testing.T) {\n\t\t\tstd.TestSetOrigCaller(tc.caller)\n\t\t\tvar act string\n\t\t\tif len(tc.ps) \u003e 0 \u0026\u0026 tc.ps[0] == \"set\" {\n\t\t\t\tact = strings.TrimSpace(Set(tc.ps[1], tc.ps[2]))\n\t\t\t} else if len(tc.ps) \u003e 0 \u0026\u0026 tc.ps[0] == \"remove\" {\n\t\t\t\tact = strings.TrimSpace(Remove(tc.ps[1]))\n\t\t\t} else {\n\t\t\t\tact = strings.TrimSpace(Render(p))\n\t\t\t}\n\t\t\tif act != tc.exp {\n\t\t\t\tt.Errorf(\"%v -\u003e '%s', got '%s', wanted '%s'\", tc.ps, p, act, tc.exp)\n\t\t\t}\n\t\t})\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "markdown", + "path": "gno.land/r/demo/markdown_test", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/markdown_test\n" + }, + { + "name": "markdown.gno", + "body": "package markdown\n\n// this package can be used to test markdown rendering engines.\n\nfunc Render(path string) string {\n\toutput := `_imported from https://github.com/markedjs/marked/blob/master/docs/demo/quickref.md_\n\nMarkdown Quick Reference\n========================\n\nThis guide is a very brief overview, with examples, of the syntax that [Markdown] supports. It is itself written in Markdown and you can copy the samples over to the left-hand pane for experimentation. It's shown as *text* and not *rendered HTML*.\n\n[Markdown]: http://daringfireball.net/projects/markdown/\n\n\nSimple Text Formatting\n======================\n\nFirst thing is first. You can use *stars* or _underscores_ for italics. **Double stars** and __double underscores__ for bold. ***Three together*** for ___both___.\n\nParagraphs are pretty easy too. Just have a blank line between chunks of text.\n\n\u003e This chunk of text is in a block quote. Its multiple lines will all be\n\u003e indented a bit from the rest of the text.\n\u003e\n\u003e \u003e Multiple levels of block quotes also work.\n\nSometimes you want to include code, such as when you are explaining how ` + \"`\u003ch1\u003e`\" + ` HTML tags work, or maybe you are a programmer and you are discussing ` + \"`someMethod()`\" + `.\n\nIf you want to include code and have new\nlines preserved, indent the line with a tab\nor at least four spaces:\n\n Extra spaces work here too.\n This is also called preformatted text and it is useful for showing examples.\n The text will stay as text, so any *markdown* or \u003cu\u003eHTML\u003c/u\u003e you add will\n not show up formatted. This way you can show markdown examples in a\n markdown document.\n\n\u003e You can also use preformatted text with your blockquotes\n\u003e as long as you add at least five spaces.\n\n\nHeadings\n========\n\nThere are a couple of ways to make headings. Using three or more equals signs on a line under a heading makes it into an \"h1\" style. Three or more hyphens under a line makes it \"h2\" (slightly smaller). You can also use multiple pound symbols (` + \"`#`\" + `) before and after a heading. Pounds after the title are ignored. Here are some examples:\n\nThis is H1\n==========\n\nThis is H2\n----------\n\n# This is H1\n## This is H2\n### This is H3 with some extra pounds ###\n#### You get the idea ####\n##### I don't need extra pounds at the end\n###### H6 is the max\n\n\nLinks\n=====\n\nLet's link to a few sites. First, let's use the bare URL, like \u003chttps://www.github.com\u003e. Great for text, but ugly for HTML.\nNext is an inline link to [Google](https://www.google.com). A little nicer.\nThis is a reference-style link to [Wikipedia] [1].\nLastly, here's a pretty link to [Yahoo]. The reference-style and pretty links both automatically use the links defined below, but they could be defined *anywhere* in the markdown and are removed from the HTML. The names are also case insensitive, so you can use [YaHoO] and have it link properly.\n\n[1]: https://www.wikipedia.org\n[Yahoo]: https://www.yahoo.com\n\nTitle attributes may be added to links by adding text after a link.\nThis is the [inline link](https://www.bing.com \"Bing\") with a \"Bing\" title.\nYou can also go to [W3C] [2] and maybe visit a [friend].\n\n[2]: https://w3c.org (The W3C puts out specs for web-based things)\n[Friend]: https://facebook.com \"Facebook!\"\n\nEmail addresses in plain text are not linked: test@example.com.\nEmail addresses wrapped in angle brackets are linked: \u003ctest@example.com\u003e.\nThey are also obfuscated so that email harvesting spam robots hopefully won't get them.\n\n\nLists\n=====\n\n* This is a bulleted list\n* Great for shopping lists\n- You can also use hyphens\n+ Or plus symbols\n\nThe above is an \"unordered\" list. Now, on for a bit of order.\n\n1. Numbered lists are also easy\n2. Just start with a number\n3738762. However, the actual number doesn't matter when converted to HTML.\n1. This will still show up as 4.\n\nYou might want a few advanced lists:\n\n- This top-level list is wrapped in paragraph tags\n- This generates an extra space between each top-level item.\n\n- You do it by adding a blank line\n\n- This nested list also has blank lines between the list items.\n\n- How to create nested lists\n 1. Start your regular list\n 2. Indent nested lists with two spaces\n 3. Further nesting means you should indent with two more spaces\n * This line is indented with four spaces.\n\n- List items can be quite lengthy. You can keep typing and either continue\nthem on the next line with no indentation.\n\n- Alternately, if that looks ugly, you can also\n indent the next line a bit for a prettier look.\n\n- You can put large blocks of text in your list by just indenting with two spaces.\n\n This is formatted the same as code, but you can inspect the HTML\n and find that it's just wrapped in a ` + \"`\u003cp\u003e`\" + ` tag and *won't* be shown\n as preformatted text.\n\n You can keep adding more and more paragraphs to a single\n list item by adding the traditional blank line and then keep\n on indenting the paragraphs with two spaces.\n\n You really only need to indent the first line,\nbut that looks ugly.\n\n- Lists support blockquotes\n\n \u003e Just like this example here. By the way, you can\n \u003e nest lists inside blockquotes!\n \u003e - Fantastic!\n\n- Lists support preformatted text\n\n You just need to indent an additional four spaces.\n\n\nEven More\n=========\n\nHorizontal Rule\n---------------\n\nIf you need a horizontal rule you just need to put at least three hyphens, asterisks, or underscores on a line by themselves. You can also even put spaces between the characters.\n\n---\n****************************\n_ _ _ _ _ _ _\n\nThose three all produced horizontal lines. Keep in mind that three hyphens under any text turns that text into a heading, so add a blank like if you use hyphens.\n\nImages\n------\n\nImages work exactly like links, but they have exclamation points in front. They work with references and titles too.\n\n![Google Logo](https://www.google.com/images/errors/logo_sm.gif) and ![Happy].\n\n[Happy]: https://wpclipart.com/smiley/happy/simple_colors/smiley_face_simple_green_small.png (\"Smiley face\")\n\n\nInline HTML\n-----------\n\nIf markdown is too limiting, you can just insert your own \u003cstrike\u003ecrazy\u003c/strike\u003e HTML. Span-level HTML \u003cu\u003ecan *still* use markdown\u003c/u\u003e. Block level elements must be separated from text by a blank line and must not have any spaces before the opening and closing HTML.\n\n\u003cdiv style='font-family: \"Comic Sans MS\", \"Comic Sans\", cursive;'\u003e\nIt is a pity, but markdown does **not** work in here for most markdown parsers.\n[Marked] handles it pretty well.\n\u003c/div\u003e`\n\treturn output\n}\n" + }, + { + "name": "markdown_test.gno", + "body": "package markdown\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestRender(t *testing.T) {\n\toutput := Render(\"\")\n\tif !strings.Contains(output, \"\\nMarkdown Quick Reference\\n\") {\n\t\tt.Errorf(\"invalid output\")\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "eval", + "path": "gno.land/r/demo/math_eval", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/math_eval\n\nrequire (\n\tgno.land/p/demo/math_eval/int32 v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "math_eval.gno", + "body": "// eval realm is capable of evaluating 32-bit integer\n// expressions as they would appear in Go. For example:\n// /r/demo/math_eval:(4+12)/2-1+11*15\npackage eval\n\nimport (\n\tevalint32 \"gno.land/p/demo/math_eval/int32\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nfunc Render(p string) string {\n\tif len(p) == 0 {\n\t\treturn `\nevaluates 32-bit integer expressions. for example:\n\t\t\n[(4+12)/2-1+11*15](/r/demo/math_eval:(4+12)/2-1+11*15)\n\n`\n\t}\n\texpr, err := evalint32.Parse(p)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\tres, err := evalint32.Eval(expr, nil)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\n\treturn ufmt.Sprintf(\"%s = %d\", p, res)\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "memeland", + "path": "gno.land/r/demo/memeland", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/memeland\n\nrequire gno.land/p/demo/memeland v0.0.0-latest\n" + }, + { + "name": "memeland.gno", + "body": "package memeland\n\nimport (\n\t\"std\"\n\t\"time\"\n\n\t\"gno.land/p/demo/memeland\"\n)\n\nvar m *memeland.Memeland\n\nfunc init() {\n\tm = memeland.NewMemeland()\n\tm.TransferOwnership(\"g125em6arxsnj49vx35f0n0z34putv5ty3376fg5\")\n}\n\nfunc PostMeme(data string, timestamp int64) string {\n\treturn m.PostMeme(data, timestamp)\n}\n\nfunc Upvote(id string) string {\n\treturn m.Upvote(id)\n}\n\nfunc GetPostsInRange(startTimestamp, endTimestamp int64, page, pageSize int, sortBy string) string {\n\treturn m.GetPostsInRange(startTimestamp, endTimestamp, page, pageSize, sortBy)\n}\n\nfunc RemovePost(id string) string {\n\treturn m.RemovePost(id)\n}\n\nfunc GetOwner() std.Address {\n\treturn m.Owner()\n}\n\nfunc TransferOwnership(newOwner std.Address) {\n\tif err := m.TransferOwnership(newOwner); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc Render(path string) string {\n\tnumOfMemes := int(m.MemeCounter)\n\tif numOfMemes == 0 {\n\t\treturn \"No memes posted yet! :/\"\n\t}\n\n\t// Default render is get Posts since year 2000 to now\n\treturn m.GetPostsInRange(0, time.Now().Unix(), 1, 10, \"DATE_CREATED\")\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "microblog", + "path": "gno.land/r/demo/microblog", + "files": [ + { + "name": "README.md", + "body": "# microblog realm\n\n## Getting started:\n\n(One-time) Add the microblog package:\n\n```\ngnokey maketx addpkg --pkgpath \"gno.land/p/demo/microblog\" --pkgdir \"examples/gno.land/p/demo/microblog\" \\\n --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast --chainid dev --remote localhost:26657 \u003cYOURKEY\u003e\n```\n\n(One-time) Add the microblog realm:\n\n```\ngnokey maketx addpkg --pkgpath \"gno.land/r/demo/microblog\" --pkgdir \"examples/gno.land/r/demo/microblog\" \\\n --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast --chainid dev --remote localhost:26657 \u003cYOURKEY\u003e\n```\n\nAdd a microblog post:\n\n```\ngnokey maketx call --pkgpath \"gno.land/r/demo/microblog\" --func \"NewPost\" --args \"hello, world\" \\\n --gas-fee \"1000000ugnot\" --gas-wanted \"2000000\" --broadcast --chainid dev --remote localhost:26657 \u003cYOURKEY\u003e\n```" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/microblog\n\nrequire (\n\tgno.land/p/demo/microblog v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" + }, + { + "name": "microblog.gno", + "body": "// Microblog is a website with shortform posts from users.\n// The API is simple - \"AddPost\" takes markdown and\n// adds it to the users site.\n// The microblog location is determined by the user address\n// /r/demo/microblog:\u003cYOUR-ADDRESS\u003e\npackage microblog\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/microblog\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\ttitle = \"gno-based microblog\"\n\tprefix = \"/r/demo/microblog:\"\n\tm *microblog.Microblog\n)\n\nfunc init() {\n\tm = microblog.NewMicroblog(title, prefix)\n}\n\nfunc renderHome() string {\n\toutput := ufmt.Sprintf(\"# %s\\n\\n\", m.Title)\n\toutput += \"# pages\\n\\n\"\n\n\tfor _, page := range m.GetPages() {\n\t\tif u := users.GetUserByAddress(page.Author); u != nil {\n\t\t\toutput += ufmt.Sprintf(\"- [%s (%s)](%s%s)\\n\", u.Name, page.Author.String(), m.Prefix, page.Author.String())\n\t\t} else {\n\t\t\toutput += ufmt.Sprintf(\"- [%s](%s%s)\\n\", page.Author.String(), m.Prefix, page.Author.String())\n\t\t}\n\t}\n\n\treturn output\n}\n\nfunc renderUser(user string) string {\n\tsilo, found := m.Pages.Get(user)\n\tif !found {\n\t\treturn \"404\" // StatusNotFound\n\t}\n\n\treturn PageToString((silo.(*microblog.Page)))\n}\n\nfunc Render(path string) string {\n\tparts := strings.Split(path, \"/\")\n\n\tisHome := path == \"\"\n\tisUser := len(parts) == 1\n\n\tswitch {\n\tcase isHome:\n\t\treturn renderHome()\n\n\tcase isUser:\n\t\treturn renderUser(parts[0])\n\t}\n\n\treturn \"404\" // StatusNotFound\n}\n\nfunc PageToString(p *microblog.Page) string {\n\to := \"\"\n\tif u := users.GetUserByAddress(p.Author); u != nil {\n\t\to += ufmt.Sprintf(\"# [%s](/r/demo/users:%s)\\n\\n\", u, u)\n\t\to += ufmt.Sprintf(\"%s\\n\\n\", u.Profile)\n\t}\n\to += ufmt.Sprintf(\"## [%s](/r/demo/microblog:%s)\\n\\n\", p.Author, p.Author)\n\n\to += ufmt.Sprintf(\"joined %s, last updated %s\\n\\n\", p.CreatedAt.Format(\"2006-02-01\"), p.LastPosted.Format(\"2006-02-01\"))\n\to += \"## feed\\n\\n\"\n\tfor _, u := range p.GetPosts() {\n\t\to += u.String() + \"\\n\\n\"\n\t}\n\treturn o\n}\n\n// NewPost takes a single argument (post markdown) and\n// adds a post to the address of the caller.\nfunc NewPost(text string) string {\n\tif err := m.NewPost(text); err != nil {\n\t\treturn \"unable to add new post\"\n\t}\n\treturn \"added new post\"\n}\n\nfunc Register(name, profile string) string {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(caller, name, profile)\n\treturn \"OK\"\n}\n" + }, + { + "name": "microblog_test.gno", + "body": "package microblog\n\nimport (\n\t\"std\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n)\n\nfunc TestMicroblog(t *testing.T) {\n\tconst (\n\t\tauthor1 std.Address = testutils.TestAddress(\"author1\")\n\t\tauthor2 std.Address = testutils.TestAddress(\"author2\")\n\t)\n\n\tstd.TestSetOrigCaller(author1)\n\n\tif Render(\"/wrongpath\") != \"404\" {\n\t\tt.Fatalf(\"rendering not giving 404\")\n\t}\n\tif Render(\"\") == \"404\" {\n\t\tt.Fatalf(\"rendering / should not give 404\")\n\t}\n\tif err := m.NewPost(\"goodbyte, web2\"); err != nil {\n\t\tt.Fatalf(\"could not create post\")\n\t}\n\tif _, err := m.GetPage(author1.String()); err != nil {\n\t\tt.Fatalf(\"silo should exist\")\n\t}\n\tif _, err := m.GetPage(\"no such author\"); err == nil {\n\t\tt.Fatalf(\"silo should not exist\")\n\t}\n\n\tstd.TestSetOrigCaller(author2)\n\n\tif err := m.NewPost(\"hello, web3\"); err != nil {\n\t\tt.Fatalf(\"could not create post\")\n\t}\n\tif err := m.NewPost(\"hello again, web3\"); err != nil {\n\t\tt.Fatalf(\"could not create post\")\n\t}\n\tif err := m.NewPost(\"hi again,\\n web4?\"); err != nil {\n\t\tt.Fatalf(\"could not create post\")\n\t}\n\n\tprintln(\"--- MICROBLOG ---\\n\\n\")\n\tif rendering := Render(\"\"); rendering != `# gno-based microblog\n\n# pages\n\n- [g1v96hg6r0wgc47h6lta047h6lta047h6lm33tq6](/r/demo/microblog:g1v96hg6r0wgc47h6lta047h6lta047h6lm33tq6)\n- [g1v96hg6r0wge97h6lta047h6lta047h6lyz7c00](/r/demo/microblog:g1v96hg6r0wge97h6lta047h6lta047h6lyz7c00)\n` {\n\t\tt.Fatalf(\"incorrect rendering /: '%s'\", rendering)\n\t}\n\n\tif rendering := strings.TrimSpace(Render(author1.String())); rendering != `## [g1v96hg6r0wgc47h6lta047h6lta047h6lm33tq6](/r/demo/microblog:g1v96hg6r0wgc47h6lta047h6lta047h6lm33tq6)\n\njoined 2009-13-02, last updated 2009-13-02\n\n## feed\n\n\u003e goodbyte, web2\n\u003e\n\u003e *Fri, 13 Feb 2009 23:31:30 UTC*` {\n\t\tt.Fatalf(\"incorrect rendering /: '%s'\", rendering)\n\t}\n\n\tif rendering := strings.TrimSpace(Render(author2.String())); rendering != `## [g1v96hg6r0wge97h6lta047h6lta047h6lyz7c00](/r/demo/microblog:g1v96hg6r0wge97h6lta047h6lta047h6lyz7c00)\n\njoined 2009-13-02, last updated 2009-13-02\n\n## feed\n\n\u003e hi again,\n\u003e\n\u003e web4?\n\u003e\n\u003e *Fri, 13 Feb 2009 23:31:30 UTC*\n\n\u003e hello again, web3\n\u003e\n\u003e *Fri, 13 Feb 2009 23:31:30 UTC*\n\n\u003e hello, web3\n\u003e\n\u003e *Fri, 13 Feb 2009 23:31:30 UTC*` {\n\t\tt.Fatalf(\"incorrect rendering /: '%s'\", rendering)\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "nft", + "path": "gno.land/r/demo/nft", + "files": [ + { + "name": "README.md", + "body": "NFT's are all the rage these days, for various reasons.\n\nI read over EIP-721 which appears to be the de-facto NFT standard on Ethereum. Then, made a sample implementation of EIP-721 (let's here called GRC-721). The implementation isn't complete, but it demonstrates the main functionality.\n\n- [EIP-721](https://eips.ethereum.org/EIPS/eip-721)\n- [gno.land/r/demo/nft/nft.go](https://gno.land/r/demo/nft/nft.go)\n- [zrealm_nft3.go test](https://github.com/gnolang/gno/blob/master/tests/files2/zrealm_nft3.gno)\n\nIn short, this demonstrates how to implement Ethereum contract interfaces in gno.land; by using only standard Go language features.\n\nPlease leave a comment ([guide](https://gno.land/r/demo/boards:gnolang/1)).\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/nft\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/grc/grc721 v0.0.0-latest\n)\n" + }, + { + "name": "nft.gno", + "body": "package nft\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/grc/grc721\"\n)\n\ntype token struct {\n\tgrc721.IGRC721 // implements the GRC721 interface\n\n\ttokenCounter int\n\ttokens avl.Tree // grc721.TokenID -\u003e *NFToken{}\n\toperators avl.Tree // owner std.Address -\u003e operator std.Address\n}\n\ntype NFToken struct {\n\towner std.Address\n\tapproved std.Address\n\ttokenID grc721.TokenID\n\tdata string\n}\n\nvar gToken = \u0026token{}\n\nfunc GetToken() *token { return gToken }\n\nfunc (grc *token) nextTokenID() grc721.TokenID {\n\tgrc.tokenCounter++\n\ts := strconv.Itoa(grc.tokenCounter)\n\treturn grc721.TokenID(s)\n}\n\nfunc (grc *token) getToken(tid grc721.TokenID) (*NFToken, bool) {\n\ttoken, ok := grc.tokens.Get(string(tid))\n\tif !ok {\n\t\treturn nil, false\n\t}\n\treturn token.(*NFToken), true\n}\n\nfunc (grc *token) Mint(to std.Address, data string) grc721.TokenID {\n\ttid := grc.nextTokenID()\n\tgrc.tokens.Set(string(tid), \u0026NFToken{\n\t\towner: to,\n\t\ttokenID: tid,\n\t\tdata: data,\n\t})\n\treturn tid\n}\n\nfunc (grc *token) BalanceOf(owner std.Address) (count int64) {\n\tpanic(\"not yet implemented\")\n}\n\nfunc (grc *token) OwnerOf(tid grc721.TokenID) std.Address {\n\ttoken, ok := grc.getToken(tid)\n\tif !ok {\n\t\tpanic(\"token does not exist\")\n\t}\n\treturn token.owner\n}\n\n// XXX not fully implemented yet.\nfunc (grc *token) SafeTransferFrom(from, to std.Address, tid grc721.TokenID) {\n\tgrc.TransferFrom(from, to, tid)\n\t// When transfer is complete, this function checks if `_to` is a smart\n\t// contract (code size \u003e 0). If so, it calls `onERC721Received` on\n\t// `_to` and throws if the return value is not\n\t// `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`.\n\t// XXX ensure \"to\" is a realm with onERC721Received() signature.\n}\n\nfunc (grc *token) TransferFrom(from, to std.Address, tid grc721.TokenID) {\n\tcaller := std.GetCallerAt(2)\n\ttoken, ok := grc.getToken(tid)\n\t// Throws if `_tokenId` is not a valid NFT.\n\tif !ok {\n\t\tpanic(\"token does not exist\")\n\t}\n\t// Throws unless `msg.sender` is the current owner, an authorized\n\t// operator, or the approved address for this NFT.\n\tif caller != token.owner \u0026\u0026 caller != token.approved {\n\t\toperator, ok := grc.operators.Get(token.owner.String())\n\t\tif !ok || caller != operator.(std.Address) {\n\t\t\tpanic(\"unauthorized\")\n\t\t}\n\t}\n\t// Throws if `_from` is not the current owner.\n\tif from != token.owner {\n\t\tpanic(\"from is not the current owner\")\n\t}\n\t// Throws if `_to` is the zero address.\n\tif to == \"\" {\n\t\tpanic(\"to cannot be empty\")\n\t}\n\t// Good.\n\ttoken.owner = to\n}\n\nfunc (grc *token) Approve(approved std.Address, tid grc721.TokenID) {\n\tcaller := std.GetCallerAt(2)\n\ttoken, ok := grc.getToken(tid)\n\t// Throws if `_tokenId` is not a valid NFT.\n\tif !ok {\n\t\tpanic(\"token does not exist\")\n\t}\n\t// Throws unless `msg.sender` is the current owner,\n\t// or an authorized operator.\n\tif caller != token.owner {\n\t\toperator, ok := grc.operators.Get(token.owner.String())\n\t\tif !ok || caller != operator.(std.Address) {\n\t\t\tpanic(\"unauthorized\")\n\t\t}\n\t}\n\t// Good.\n\ttoken.approved = approved\n}\n\n// XXX make it work for set of operators.\nfunc (grc *token) SetApprovalForAll(operator std.Address, approved bool) {\n\tcaller := std.GetCallerAt(2)\n\tgrc.operators.Set(caller.String(), operator)\n}\n\nfunc (grc *token) GetApproved(tid grc721.TokenID) std.Address {\n\ttoken, ok := grc.getToken(tid)\n\t// Throws if `_tokenId` is not a valid NFT.\n\tif !ok {\n\t\tpanic(\"token does not exist\")\n\t}\n\treturn token.approved\n}\n\n// XXX make it work for set of operators\nfunc (grc *token) IsApprovedForAll(owner, operator std.Address) bool {\n\toperator2, ok := grc.operators.Get(owner.String())\n\tif !ok {\n\t\treturn false\n\t}\n\treturn operator == operator2.(std.Address)\n}\n" + }, + { + "name": "z_0_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/nft_test\npackage nft_test\n\nimport (\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/nft\"\n)\n\nfunc main() {\n\taddr1 := testutils.TestAddress(\"addr1\")\n\t// addr2 := testutils.TestAddress(\"addr2\")\n\tgrc721 := nft.GetToken()\n\ttid := grc721.Mint(addr1, \"NFT#1\")\n\tprintln(grc721.OwnerOf(tid))\n\tprintln(addr1)\n}\n\n// Output:\n// g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5\n// g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5\n\n// Realm:\n// switchrealm[\"gno.land/r/demo/nft\"]\n// switchrealm[\"gno.land/r/demo/nft\"]\n// c[67c479d3d51d4056b2f4111d5352912a00be311e:11]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"std.Address\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"std.Address\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/grc/grc721.TokenID\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"1\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"NFT#1\"\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:11\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:10\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[67c479d3d51d4056b2f4111d5352912a00be311e:10]={\n// \"ObjectInfo\": {\n// \"ID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:10\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:9\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/nft.NFToken\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"564a9e78be869bd258fc3c9ad56f5a75ed68818f\",\n// \"ObjectID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:11\"\n// }\n// }\n// }\n// c[67c479d3d51d4056b2f4111d5352912a00be311e:9]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"16\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.StringValue\",\n// \"value\": \"1\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/r/demo/nft.NFToken\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"b53ffc464e1b5655d19b9d5277f3491717c24aca\",\n// \"ObjectID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:10\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"64\"\n// }\n// },\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:9\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:8\",\n// \"RefCount\": \"1\"\n// }\n// }\n// c[67c479d3d51d4056b2f4111d5352912a00be311e:8]={\n// \"ObjectInfo\": {\n// \"ID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:8\",\n// \"ModTime\": \"0\",\n// \"OwnerID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:5\",\n// \"RefCount\": \"1\"\n// },\n// \"Value\": {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"b1d928b3716b147c92730e8d234162bec2f0f2fc\",\n// \"ObjectID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:9\"\n// }\n// }\n// }\n// u[67c479d3d51d4056b2f4111d5352912a00be311e:5]={\n// \"Fields\": [\n// {\n// \"T\": {\n// \"@type\": \"/gno.PointerType\",\n// \"Elt\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Node\"\n// }\n// },\n// \"V\": {\n// \"@type\": \"/gno.PointerValue\",\n// \"Base\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"b229b824842ec3e7f2341e33d0fa0ca77af2f480\",\n// \"ObjectID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:8\"\n// },\n// \"Index\": \"0\",\n// \"TV\": null\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:5\",\n// \"ModTime\": \"7\",\n// \"OwnerID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:4\",\n// \"RefCount\": \"1\"\n// }\n// }\n// u[67c479d3d51d4056b2f4111d5352912a00be311e:4]={\n// \"Fields\": [\n// {},\n// {\n// \"N\": \"AQAAAAAAAAA=\",\n// \"T\": {\n// \"@type\": \"/gno.PrimitiveType\",\n// \"value\": \"32\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Tree\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"1e0b9dddb406b4f50500a022266a4cb8a4ea38c6\",\n// \"ObjectID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:5\"\n// }\n// },\n// {\n// \"T\": {\n// \"@type\": \"/gno.RefType\",\n// \"ID\": \"gno.land/p/demo/avl.Tree\"\n// },\n// \"V\": {\n// \"@type\": \"/gno.RefValue\",\n// \"Hash\": \"05ab6746ea84b55ca133806af215d99a1c4b045e\",\n// \"ObjectID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:6\"\n// }\n// }\n// ],\n// \"ObjectInfo\": {\n// \"ID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:4\",\n// \"ModTime\": \"7\",\n// \"OwnerID\": \"67c479d3d51d4056b2f4111d5352912a00be311e:3\",\n// \"RefCount\": \"1\"\n// }\n// }\n// switchrealm[\"gno.land/r/demo/nft\"]\n// switchrealm[\"gno.land/r/demo/nft_test\"]\n" + }, + { + "name": "z_1_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/nft_test\npackage nft_test\n\nimport (\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/nft\"\n)\n\nfunc main() {\n\taddr1 := testutils.TestAddress(\"addr1\")\n\taddr2 := testutils.TestAddress(\"addr2\")\n\tgrc721 := nft.GetToken()\n\ttid := grc721.Mint(addr1, \"NFT#1\")\n\tprintln(grc721.OwnerOf(tid))\n\tprintln(addr1)\n\tgrc721.TransferFrom(addr1, addr2, tid)\n}\n\n// Error:\n// unauthorized\n" + }, + { + "name": "z_2_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/nft_test\npackage nft_test\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/nft\"\n)\n\nfunc main() {\n\tcaller := std.GetCallerAt(1)\n\taddr1 := testutils.TestAddress(\"addr1\")\n\t// addr2 := testutils.TestAddress(\"addr2\")\n\tgrc721 := nft.GetToken()\n\ttid := grc721.Mint(caller, \"NFT#1\")\n\tprintln(grc721.OwnerOf(tid))\n\tprintln(addr1)\n\tgrc721.TransferFrom(caller, addr1, tid)\n}\n\n// Output:\n// g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n// g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5\n" + }, + { + "name": "z_3_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/nft_test\npackage nft_test\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/nft\"\n)\n\nfunc main() {\n\tcaller := std.GetCallerAt(1)\n\taddr1 := testutils.TestAddress(\"addr1\")\n\taddr2 := testutils.TestAddress(\"addr2\")\n\tgrc721 := nft.GetToken()\n\ttid := grc721.Mint(caller, \"NFT#1\")\n\tprintln(grc721.OwnerOf(tid))\n\tprintln(addr1)\n\tgrc721.Approve(caller, tid) // approve self.\n\tgrc721.TransferFrom(caller, addr1, tid)\n\tgrc721.TransferFrom(addr1, addr2, tid)\n}\n\n// Output:\n// g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n// g1v9jxgu33ta047h6lta047h6lta047h6l43dqc5\n" + }, + { + "name": "z_4_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/nft_test\npackage nft_test\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/nft\"\n)\n\nfunc main() {\n\tcaller := std.GetCallerAt(1)\n\taddr1 := testutils.TestAddress(\"addr1\")\n\taddr2 := testutils.TestAddress(\"addr2\")\n\tgrc721 := nft.GetToken()\n\ttid := grc721.Mint(caller, \"NFT#1\")\n\tprintln(grc721.OwnerOf(tid))\n\tprintln(addr1)\n\tgrc721.Approve(caller, tid) // approve self.\n\tgrc721.TransferFrom(caller, addr1, tid)\n\tgrc721.Approve(\"\", tid) // approve addr1.\n\tgrc721.TransferFrom(addr1, addr2, tid)\n}\n\n// Error:\n// unauthorized\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "releases_example", + "path": "gno.land/r/demo/releases_example", + "files": [ + { + "name": "dummy.gno", + "body": "package releases_example\n\nfunc init() {\n\t// dummy example data\n\tchangelog.NewRelease(\n\t\t\"v1\",\n\t\t\"r/demo/examples_example_v1\",\n\t\t\"initial release\",\n\t)\n\tchangelog.NewRelease(\n\t\t\"v2\",\n\t\t\"r/demo/examples_example_v2\",\n\t\t\"various improvements\",\n\t)\n}\n" + }, + { + "name": "example.gno", + "body": "// this package demonstrates a way to manage contract releases.\npackage releases_example\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/releases\"\n)\n\nvar (\n\tchangelog = releases.NewChangelog(\"example_app\")\n\tadmin = std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\") // @administrator\n)\n\nfunc init() {\n\t// FIXME: admin = std.GetCreator()\n}\n\nfunc NewRelease(name, url, notes string) {\n\tcaller := std.GetOrigCaller()\n\tif caller != admin {\n\t\tpanic(\"restricted area\")\n\t}\n\tchangelog.NewRelease(name, url, notes)\n}\n\nfunc UpdateAdmin(address std.Address) {\n\tcaller := std.GetOrigCaller()\n\tif caller != admin {\n\t\tpanic(\"restricted area\")\n\t}\n\tadmin = address\n}\n\nfunc Render(path string) string {\n\treturn changelog.Render(path)\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/releases_example\n\nrequire gno.land/p/demo/releases v0.0.0-latest\n" + }, + { + "name": "releases0_filetest.gno", + "body": "package main\n\nimport (\n\t\"gno.land/p/demo/releases\"\n)\n\nfunc main() {\n\tprintln(\"-----------\")\n\tchangelog := releases.NewChangelog(\"example\")\n\tprint(changelog.Render(\"\"))\n\n\tprintln(\"-----------\")\n\tchangelog.NewRelease(\"v1\", \"r/blahblah\", \"* initial version\")\n\tprint(changelog.Render(\"\"))\n\n\tprintln(\"-----------\")\n\tchangelog.NewRelease(\"v2\", \"r/blahblah2\", \"* various improvements\\n* new shiny logo\")\n\tprint(changelog.Render(\"\"))\n\n\tprintln(\"-----------\")\n\tprint(changelog.Latest().Render())\n}\n\n// Output:\n// -----------\n// # example\n//\n// -----------\n// # example\n//\n// ## [example v1 (latest)](r/blahblah)\n//\n// * initial version\n//\n// -----------\n// # example\n//\n// ## [example v2 (latest)](r/blahblah2)\n//\n// * various improvements\n// * new shiny logo\n//\n// ## [example v1](r/blahblah)\n//\n// * initial version\n//\n// -----------\n// ## [example v2 (latest)](r/blahblah2)\n//\n// * various improvements\n// * new shiny logo\n" + }, + { + "name": "releases1_filetest.gno", + "body": "package main\n\nimport (\n\t\"gno.land/r/demo/releases_example\"\n)\n\nfunc main() {\n\tprintln(\"-----------\")\n\tprint(releases_example.Render(\"\"))\n\n\tprintln(\"-----------\")\n\tprint(releases_example.Render(\"v1\"))\n\n\tprintln(\"-----------\")\n\tprint(releases_example.Render(\"v42\"))\n}\n\n// Output:\n// -----------\n// # example_app\n//\n// ## [example_app v2 (latest)](r/demo/examples_example_v2)\n//\n// various improvements\n//\n// ## [example_app v1](r/demo/examples_example_v1)\n//\n// initial release\n//\n// -----------\n// ## [example_app v1](r/demo/examples_example_v1)\n//\n// initial release\n//\n// -----------\n// no such release\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "tamagotchi", + "path": "gno.land/r/demo/tamagotchi", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/tamagotchi\n\nrequire (\n\tgno.land/p/demo/tamagotchi v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "realm.gno", + "body": "package tamagotchi\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/tamagotchi\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar t *tamagotchi.Tamagotchi\n\nfunc init() {\n\tReset(\"gnome#0\")\n}\n\nfunc Reset(optionalName string) string {\n\tname := optionalName\n\tif name == \"\" {\n\t\theight := std.GetHeight()\n\t\tname = ufmt.Sprintf(\"gnome#%d\", height)\n\t}\n\n\tt = tamagotchi.New(name)\n\n\treturn ufmt.Sprintf(\"A new tamagotchi is born. Their name is %s %s.\", t.Name(), t.Face())\n}\n\nfunc Feed() string {\n\tt.Feed()\n\treturn t.Markdown()\n}\n\nfunc Play() string {\n\tt.Play()\n\treturn t.Markdown()\n}\n\nfunc Heal() string {\n\tt.Heal()\n\treturn t.Markdown()\n}\n\nfunc Render(path string) string {\n\ttama := t.Markdown()\n\tlinks := `Actions:\n* [Feed](/r/demo/tamagotchi?help\u0026__func=Feed)\n* [Play](/r/demo/tamagotchi?help\u0026__func=Play)\n* [Heal](/r/demo/tamagotchi?help\u0026__func=Heal)\n* [Reset](/r/demo/tamagotchi?help\u0026__func=Reset)\n`\n\n\treturn tama + \"\\n\\n\" + links\n}\n" + }, + { + "name": "z0_filetest.gno", + "body": "package main\n\nimport (\n\t\"gno.land/r/demo/tamagotchi\"\n)\n\nfunc main() {\n\ttamagotchi.Reset(\"tamagnotchi\")\n\tprintln(tamagotchi.Render(\"\"))\n}\n\n// Output:\n// # tamagnotchi 😃\n//\n// * age: 0\n// * hunger: 50\n// * happiness: 50\n// * health: 50\n// * sleepy: 0\n//\n// Actions:\n// * [Feed](/r/demo/tamagotchi?help\u0026__func=Feed)\n// * [Play](/r/demo/tamagotchi?help\u0026__func=Play)\n// * [Heal](/r/demo/tamagotchi?help\u0026__func=Heal)\n// * [Reset](/r/demo/tamagotchi?help\u0026__func=Reset)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "crossrealm", + "path": "gno.land/r/demo/tests/crossrealm", + "files": [ + { + "name": "crossrealm.gno", + "body": "package crossrealm\n\nimport (\n\t\"gno.land/p/demo/tests/p_crossrealm\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype LocalStruct struct {\n\tA int\n}\n\nfunc (ls *LocalStruct) String() string {\n\treturn ufmt.Sprintf(\"LocalStruct{%d}\", ls.A)\n}\n\n// local is saved locally in this realm\nvar local *LocalStruct\n\nfunc init() {\n\tlocal = \u0026LocalStruct{A: 123}\n}\n\n// Make1 returns a local object wrapped by a p struct\nfunc Make1() *p_crossrealm.Container {\n\treturn \u0026p_crossrealm.Container{\n\t\tA: 1,\n\t\tB: local,\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/tests/crossrealm\n\nrequire (\n\tgno.land/p/demo/tests/p_crossrealm v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "tests_foo", + "path": "gno.land/r/demo/tests_foo", + "files": [ + { + "name": "foo.gno", + "body": "package tests_foo\n\nimport (\n\t\"gno.land/r/demo/tests\"\n)\n\n// for testing gno.land/r/demo/tests/interfaces.go\n\ntype FooStringer struct {\n\tFieldA string\n}\n\nfunc (fs *FooStringer) String() string {\n\treturn \"\u0026FooStringer{\" + fs.FieldA + \"}\"\n}\n\nfunc AddFooStringer(fa string) {\n\ttests.AddStringer(\u0026FooStringer{fa})\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/demo/tests_foo\n\nrequire gno.land/r/demo/tests v0.0.0-latest\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "todolistrealm", + "path": "gno.land/r/demo/todolist", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/todolist\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/seqid v0.0.0-latest\n\tgno.land/p/demo/todolist v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "todolist.gno", + "body": "package todolistrealm\n\nimport (\n\t\"bytes\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/seqid\"\n\t\"gno.land/p/demo/todolist\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// State variables\nvar (\n\ttodolistTree *avl.Tree\n\ttlid seqid.ID\n)\n\n// Constructor\nfunc init() {\n\ttodolistTree = avl.NewTree()\n}\n\nfunc NewTodoList(title string) (int, string) {\n\t// Create new Todolist\n\ttl := todolist.NewTodoList(title)\n\t// Update AVL tree with new state\n\ttlid.Next()\n\ttodolistTree.Set(strconv.Itoa(int(tlid)), tl)\n\treturn int(tlid), \"created successfully\"\n}\n\nfunc AddTask(todolistID int, title string) string {\n\t// Get Todolist from AVL tree\n\ttl, ok := todolistTree.Get(strconv.Itoa(todolistID))\n\tif !ok {\n\t\tpanic(\"Todolist not found\")\n\t}\n\n\t// get the number of tasks in the todolist\n\tid := tl.(*todolist.TodoList).Tasks.Size()\n\n\t// create the task\n\ttask := todolist.NewTask(title)\n\n\t// Cast raw data from tree into Todolist struct\n\ttl.(*todolist.TodoList).AddTask(id, task)\n\n\treturn \"task added successfully\"\n}\n\nfunc ToggleTaskStatus(todolistID int, taskID int) string {\n\t// Get Todolist from AVL tree\n\ttl, ok := todolistTree.Get(strconv.Itoa(todolistID))\n\tif !ok {\n\t\tpanic(\"Todolist not found\")\n\t}\n\n\t// Get the task from the todolist\n\ttask, found := tl.(*todolist.TodoList).Tasks.Get(strconv.Itoa(taskID))\n\tif !found {\n\t\tpanic(\"Task not found\")\n\t}\n\n\t// Change the status of the task\n\ttodolist.ToggleTaskStatus(task.(*todolist.Task))\n\n\treturn \"task status changed successfully\"\n}\n\nfunc RemoveTask(todolistID int, taskID int) string {\n\t// Get Todolist from AVL tree\n\ttl, ok := todolistTree.Get(strconv.Itoa(todolistID))\n\tif !ok {\n\t\tpanic(\"Todolist not found\")\n\t}\n\n\t// Get the task from the todolist\n\t_, ok = tl.(*todolist.TodoList).Tasks.Get(strconv.Itoa(taskID))\n\tif !ok {\n\t\tpanic(\"Task not found\")\n\t}\n\n\t// Change the status of the task\n\ttl.(*todolist.TodoList).RemoveTask(strconv.Itoa(taskID))\n\n\treturn \"task status changed successfully\"\n}\n\nfunc RemoveTodoList(todolistID int) string {\n\t// Get Todolist from AVL tree\n\t_, ok := todolistTree.Get(strconv.Itoa(todolistID))\n\tif !ok {\n\t\tpanic(\"Todolist not found\")\n\t}\n\n\t// Remove the todolist\n\ttodolistTree.Remove(strconv.Itoa(todolistID))\n\n\treturn \"Todolist removed successfully\"\n}\n\nfunc Render(path string) string {\n\tif path == \"\" {\n\t\treturn renderHomepage()\n\t}\n\n\treturn \"unknown page\"\n}\n\nfunc renderHomepage() string {\n\t// Define empty buffer\n\tvar b bytes.Buffer\n\n\tb.WriteString(\"# Welcome to ToDolist\\n\\n\")\n\n\t// If no todolists have been created\n\tif todolistTree.Size() == 0 {\n\t\tb.WriteString(\"### No todolists available currently!\")\n\t\treturn b.String()\n\t}\n\n\t// Iterate through AVL tree\n\ttodolistTree.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t// cast raw data from tree into Todolist struct\n\t\ttl := value.(*todolist.TodoList)\n\n\t\t// Add Todolist name\n\t\tb.WriteString(\n\t\t\tufmt.Sprintf(\n\t\t\t\t\"## Todolist #%s: %s\\n\",\n\t\t\t\tkey, // Todolist ID\n\t\t\t\ttl.GetTodolistTitle(),\n\t\t\t),\n\t\t)\n\n\t\t// Add Todolist owner\n\t\tb.WriteString(\n\t\t\tufmt.Sprintf(\n\t\t\t\t\"#### Todolist owner : %s\\n\",\n\t\t\t\ttl.GetTodolistOwner(),\n\t\t\t),\n\t\t)\n\n\t\t// List all todos that are currently Todolisted\n\t\tif todos := tl.GetTasks(); len(todos) \u003e 0 {\n\t\t\tb.WriteString(\n\t\t\t\tufmt.Sprintf(\"Currently Todo tasks: %d\\n\\n\", len(todos)),\n\t\t\t)\n\n\t\t\tfor index, todo := range todos {\n\t\t\t\tb.WriteString(\n\t\t\t\t\tufmt.Sprintf(\"#%d - %s \", index, todo.Title),\n\t\t\t\t)\n\t\t\t\t// displays a checked box if task is marked as done, an empty box if not\n\t\t\t\tif todo.Done {\n\t\t\t\t\tb.WriteString(\n\t\t\t\t\t\t\"☑\\n\\n\",\n\t\t\t\t\t)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tb.WriteString(\n\t\t\t\t\t\"☐\\n\\n\",\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\tb.WriteString(\"No tasks in this list currently\\n\")\n\t\t}\n\n\t\tb.WriteString(\"\\n\")\n\t\treturn false\n\t})\n\n\treturn b.String()\n}\n" + }, + { + "name": "todolist_test.gno", + "body": "package todolistrealm\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/todolist\"\n)\n\nvar (\n\tnode interface{}\n\ttdl *todolist.TodoList\n)\n\nfunc TestNewTodoList(t *testing.T) {\n\ttitle := \"My Todo List\"\n\ttlid, _ := NewTodoList(title)\n\tif tlid != 1 {\n\t\tt.Errorf(\"Expected tlid to be 1, but got %d\", tlid)\n\t}\n\n\t// get the todolist node from the tree\n\tnode, _ = todolistTree.Get(strconv.Itoa(tlid))\n\t// convert the node to a TodoList struct\n\ttdl = node.(*todolist.TodoList)\n\n\tif tdl.Title != title {\n\t\tt.Errorf(\"Expected title to be %s, but got %s\", title, tdl.Title)\n\t}\n\tif tdl.Owner != std.GetOrigCaller() {\n\t\tt.Errorf(\"Expected owner to be %s, but got %s\", std.GetOrigCaller(), tdl.Owner)\n\t}\n\tif len(tdl.GetTasks()) != 0 {\n\t\tt.Errorf(\"Expected no tasks in the todo list, but got %d tasks\", len(tdl.GetTasks()))\n\t}\n}\n\nfunc TestAddTask(t *testing.T) {\n\tAddTask(1, \"Task 1\")\n\n\ttasks := tdl.GetTasks()\n\tif len(tasks) != 1 {\n\t\tt.Errorf(\"Expected 1 task in the todo list, but got %d tasks\", len(tasks))\n\t}\n\n\tif tasks[0].Title != \"Task 1\" {\n\t\tt.Errorf(\"Expected task title to be 'Task 1', but got '%s'\", tasks[0].Title)\n\t}\n\n\tif tasks[0].Done {\n\t\tt.Errorf(\"Expected task to be not done, but it is marked as done\")\n\t}\n}\n\nfunc TestToggleTaskStatus(t *testing.T) {\n\tToggleTaskStatus(1, 0)\n\ttask := tdl.GetTasks()[0]\n\n\tif !task.Done {\n\t\tt.Errorf(\"Expected task to be done, but it is not marked as done\")\n\t}\n\n\tToggleTaskStatus(1, 0)\n\n\tif task.Done {\n\t\tt.Errorf(\"Expected task to be not done, but it is marked as done\")\n\t}\n}\n\nfunc TestRemoveTask(t *testing.T) {\n\tRemoveTask(1, 0)\n\ttasks := tdl.GetTasks()\n\n\tif len(tasks) != 0 {\n\t\tt.Errorf(\"Expected no tasks in the todo list, but got %d tasks\", len(tasks))\n\t}\n}\n\nfunc TestRemoveTodoList(t *testing.T) {\n\tRemoveTodoList(1)\n\n\tif todolistTree.Size() != 0 {\n\t\tt.Errorf(\"Expected no tasks in the todo list, but got %d tasks\", todolistTree.Size())\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "types", + "path": "gno.land/r/demo/types", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/types\n\nrequire gno.land/p/demo/avl v0.0.0-latest\n" + }, + { + "name": "types.gno", + "body": "// package to test types behavior in various conditions (TXs, imports).\npackage types\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\nvar (\n\tgInt int = -42\n\tgUint uint = 42\n\tgString string = \"a string\"\n\tgStringSlice []string = []string{\"a\", \"string\", \"slice\"}\n\tgError error = errors.New(\"an error\")\n\tgIntSlice []int = []int{-42, 0, 42}\n\tgUintSlice []uint = []uint{0, 42, 84}\n\tgTree avl.Tree\n\t// gInterface = interface{}{-42, \"a string\", uint(42)}\n)\n\nfunc init() {\n\tgTree.Set(\"a\", \"content of A\")\n\tgTree.Set(\"b\", \"content of B\")\n}\n\nfunc Noop() {}\nfunc RetTimeNow() time.Time { return time.Now() }\nfunc RetString() string { return gString }\nfunc RetStringPointer() *string { return \u0026gString }\nfunc RetUint() uint { return gUint }\nfunc RetInt() int { return gInt }\nfunc RetUintPointer() *uint { return \u0026gUint }\nfunc RetIntPointer() *int { return \u0026gInt }\nfunc RetTree() avl.Tree { return gTree }\nfunc RetIntSlice() []int { return gIntSlice }\nfunc RetUintSlice() []uint { return gUintSlice }\nfunc RetStringSlice() []string { return gStringSlice }\nfunc RetError() error { return gError }\nfunc Panic() { panic(\"PANIC!\") }\n\n// TODO: floats\n// TODO: typed errors\n// TODO: ret interface\n// TODO: recover\n// TODO: take types as input\n\nfunc Render(path string) string {\n\treturn \"package to test data types.\"\n}\n" + }, + { + "name": "types_test.gno", + "body": "package types\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "ui", + "path": "gno.land/r/demo/ui", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/ui\n\nrequire gno.land/p/demo/ui v0.0.0-latest\n" + }, + { + "name": "ui.gno", + "body": "package ui\n\nimport \"gno.land/p/demo/ui\"\n\nfunc Render(path string) string {\n\t// TODO: build this realm as a demo one with one page per feature.\n\n\t// TODO: pagination\n\t// TODO: non-standard markdown\n\t// TODO: error, warn\n\t// TODO: header\n\t// TODO: HTML\n\t// TODO: toc\n\t// TODO: forms\n\t// TODO: comments\n\n\tdom := ui.DOM{\n\t\tPrefix: \"r/demo/ui:\",\n\t}\n\n\tdom.Title = \"UI Demo\"\n\n\tdom.Header.Append(ui.Breadcrumb{\n\t\tui.Link{Text: \"foo\", Path: \"foo\"},\n\t\tui.Link{Text: \"bar\", Path: \"foo/bar\"},\n\t})\n\n\tdom.Body.Append(\n\t\tui.Paragraph(\"Simple UI demonstration.\"),\n\t\tui.BulletList{\n\t\t\tui.Text(\"a text\"),\n\t\t\tui.Link{Text: \"a relative link\", Path: \"foobar\"},\n\t\t\tui.Text(\"another text\"),\n\t\t\t// ui.H1(\"a H1 text\"),\n\t\t\tui.Bold(\"a bold text\"),\n\t\t\tui.Italic(\"italic text\"),\n\t\t\tui.Text(\"raw markdown with **bold** text in the middle.\"),\n\t\t\tui.Code(\"some inline code\"),\n\t\t\tui.Link{Text: \"a remote link\", URL: \"https://gno.land\"},\n\t\t},\n\t)\n\n\tdom.Footer.Append(ui.Text(\"I'm the footer.\"))\n\tdom.Body.Append(ui.Text(\"another string.\"))\n\tdom.Body.Append(ui.Paragraph(\"a paragraph.\"), ui.HR{})\n\n\treturn dom.String()\n}\n" + }, + { + "name": "ui_test.gno", + "body": "package ui\n\nimport \"testing\"\n\nfunc TestRender(t *testing.T) {\n\tgot := Render(\"\")\n\texpected := \"# UI Demo\\n\\n[foo](r/demo/ui:foo) / [bar](r/demo/ui:foo/bar)\\n\\n\\nSimple UI demonstration.\\n\\n- a text\\n- [a relative link](r/demo/ui:foobar)\\n- another text\\n- **a bold text**\\n- _italic text_\\n- raw markdown with **bold** text in the middle.\\n- `some inline code`\\n- [a remote link](https://gno.land)\\n\\nanother string.\\n\\na paragraph.\\n\\n\\n---\\n\\n\\nI'm the footer.\\n\\n\"\n\n\tif got != expected {\n\t\tt.Errorf(\"-\\nexpected: %q\\ngot: %q.\", expected, got)\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "userbook", + "path": "gno.land/r/demo/userbook", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/userbook\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/mux v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "userbook.gno", + "body": "// This realm demonstrates a small userbook system working with gnoweb\npackage userbook\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/mux\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype Signup struct {\n\taccount string\n\theight int64\n}\n\n// signups - keep a slice of signed up addresses efficient pagination\nvar signups []Signup\n\n// tracker - keep track of who signed up\nvar (\n\ttracker *avl.Tree\n\trouter *mux.Router\n)\n\nconst (\n\tdefaultPageSize = 20\n\tpathArgument = \"number\"\n\tsubPath = \"page/{\" + pathArgument + \"}\"\n\tsignUpEvent = \"SignUp\"\n)\n\nfunc init() {\n\t// Set up tracker tree\n\ttracker = avl.NewTree()\n\n\t// Set up route handling\n\trouter = mux.NewRouter()\n\trouter.HandleFunc(\"\", renderHelper)\n\trouter.HandleFunc(subPath, renderHelper)\n\n\t// Sign up the deployer\n\tSignUp()\n}\n\nfunc SignUp() string {\n\t// Get transaction caller\n\tcaller := std.PrevRealm().Addr().String()\n\theight := std.GetHeight()\n\n\t// Check if the user is already signed up\n\tif _, exists := tracker.Get(caller); exists {\n\t\tpanic(caller + \" is already signed up!\")\n\t}\n\n\t// Sign up the user\n\ttracker.Set(caller, struct{}{})\n\tsignup := Signup{\n\t\tcaller,\n\t\theight,\n\t}\n\n\tsignups = append(signups, signup)\n\tstd.Emit(signUpEvent, \"SignedUpAccount\", signup.account)\n\n\treturn ufmt.Sprintf(\"%s added to userbook up at block #%d!\", signup.account, signup.height)\n}\n\nfunc GetSignupsInRange(page, pageSize int) ([]Signup, int) {\n\tif page \u003c 1 {\n\t\tpanic(\"page number cannot be less than 1\")\n\t}\n\n\tif pageSize \u003c 1 || pageSize \u003e 50 {\n\t\tpanic(\"page size must be from 1 to 50\")\n\t}\n\n\t// Pagination\n\t// Calculate indexes\n\tstartIndex := (page - 1) * pageSize\n\tendIndex := startIndex + pageSize\n\n\t// If page does not contain any users\n\tif startIndex \u003e= len(signups) {\n\t\treturn nil, -1\n\t}\n\n\t// If page contains fewer users than the page size\n\tif endIndex \u003e len(signups) {\n\t\tendIndex = len(signups)\n\t}\n\n\treturn signups[startIndex:endIndex], endIndex\n}\n\nfunc renderHelper(res *mux.ResponseWriter, req *mux.Request) {\n\ttotalSignups := len(signups)\n\tres.Write(\"# Welcome to UserBook!\\n\\n\")\n\n\t// Get URL parameter\n\tpage, err := strconv.Atoi(req.GetVar(\"number\"))\n\tif err != nil {\n\t\tpage = 1 // render first page on bad input\n\t}\n\n\t// Fetch paginated signups\n\tfetchedSignups, endIndex := GetSignupsInRange(page, defaultPageSize)\n\t// Handle empty page case\n\tif len(fetchedSignups) == 0 {\n\t\tres.Write(\"No users on this page!\\n\\n\")\n\t\tres.Write(\"---\\n\\n\")\n\t\tres.Write(\"[Back to Page #1](/r/demo/userbook:page/1)\\n\\n\")\n\t\treturn\n\t}\n\n\t// Write page title\n\tres.Write(ufmt.Sprintf(\"## UserBook - Page #%d:\\n\\n\", page))\n\n\t// Write signups\n\tpageStartIndex := defaultPageSize * (page - 1)\n\tfor i, signup := range fetchedSignups {\n\t\tout := ufmt.Sprintf(\"#### User #%d - %s - signed up at Block #%d\\n\", pageStartIndex+i, signup.account, signup.height)\n\t\tres.Write(out)\n\t}\n\n\tres.Write(\"---\\n\\n\")\n\n\t// Write UserBook info\n\tlatestSignupIndex := totalSignups - 1\n\tres.Write(ufmt.Sprintf(\"#### Total users: %d\\n\", totalSignups))\n\tres.Write(ufmt.Sprintf(\"#### Latest signup: User #%d at Block #%d\\n\", latestSignupIndex, signups[latestSignupIndex].height))\n\n\tres.Write(\"---\\n\\n\")\n\n\t// Write page number\n\tres.Write(ufmt.Sprintf(\"You're viewing page #%d\", page))\n\n\t// Write navigation buttons\n\tvar prevPage string\n\tvar nextPage string\n\t// If we are on any page that is not the first page\n\tif page \u003e 1 {\n\t\tprevPage = ufmt.Sprintf(\" - [Previous page](/r/demo/userbook:page/%d)\", page-1)\n\t}\n\n\t// If there are more pages after the current one\n\tif endIndex \u003c totalSignups {\n\t\tnextPage = ufmt.Sprintf(\" - [Next page](/r/demo/userbook:page/%d)\\n\\n\", page+1)\n\t}\n\n\tres.Write(prevPage)\n\tres.Write(nextPage)\n}\n\nfunc Render(path string) string {\n\treturn router.Render(path)\n}\n" + }, + { + "name": "userbook_test.gno", + "body": "package userbook\n\nimport (\n\t\"std\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nfunc TestRender(t *testing.T) {\n\t// Sign up 20 users + deployer\n\tfor i := 0; i \u003c 20; i++ {\n\t\taddrName := ufmt.Sprintf(\"test%d\", i)\n\t\tcaller := testutils.TestAddress(addrName)\n\t\tstd.TestSetOrigCaller(caller)\n\t\tSignUp()\n\t}\n\n\ttestCases := []struct {\n\t\tname string\n\t\tnextPage bool\n\t\tprevPage bool\n\t\tpath string\n\t\texpectedNumberOfUsers int\n\t}{\n\t\t{\n\t\t\tname: \"1st page render\",\n\t\t\tnextPage: true,\n\t\t\tprevPage: false,\n\t\t\tpath: \"page/1\",\n\t\t\texpectedNumberOfUsers: 20,\n\t\t},\n\t\t{\n\t\t\tname: \"2nd page render\",\n\t\t\tnextPage: false,\n\t\t\tprevPage: true,\n\t\t\tpath: \"page/2\",\n\t\t\texpectedNumberOfUsers: 1,\n\t\t},\n\t\t{\n\t\t\tname: \"Invalid path render\",\n\t\t\tnextPage: true,\n\t\t\tprevPage: false,\n\t\t\tpath: \"page/invalidtext\",\n\t\t\texpectedNumberOfUsers: 20,\n\t\t},\n\t\t{\n\t\t\tname: \"Empty Page\",\n\t\t\tnextPage: false,\n\t\t\tprevPage: false,\n\t\t\tpath: \"page/1000\",\n\t\t\texpectedNumberOfUsers: 0,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := Render(tc.path)\n\t\t\tnumUsers := countUsers(got)\n\n\t\t\tif tc.prevPage \u0026\u0026 !strings.Contains(got, \"Previous page\") {\n\t\t\t\tt.Fatalf(\"expected to find Previous page, didn't find it\")\n\t\t\t}\n\t\t\tif tc.nextPage \u0026\u0026 !strings.Contains(got, \"Next page\") {\n\t\t\t\tt.Fatalf(\"expected to find Next page, didn't find it\")\n\t\t\t}\n\n\t\t\tif tc.expectedNumberOfUsers != numUsers {\n\t\t\t\tt.Fatalf(\"expected %d, got %d users\", tc.expectedNumberOfUsers, numUsers)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc countUsers(input string) int {\n\treturn strings.Count(input, \"#### User #\")\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "wugnot", + "path": "gno.land/r/demo/wugnot", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/demo/wugnot\n\nrequire (\n\tgno.land/p/demo/grc/grc20 v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" + }, + { + "name": "wugnot.gno", + "body": "package wugnot\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/grc/grc20\"\n\t\"gno.land/p/demo/ufmt\"\n\tpusers \"gno.land/p/demo/users\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbanker *grc20.Banker = grc20.NewBanker(\"wrapped GNOT\", \"wugnot\", 0)\n\tToken = banker.Token()\n)\n\nconst (\n\tugnotMinDeposit uint64 = 1000\n\twugnotMinDeposit uint64 = 1\n)\n\nfunc Deposit() {\n\tcaller := std.PrevRealm().Addr()\n\tsent := std.GetOrigSend()\n\tamount := sent.AmountOf(\"ugnot\")\n\n\trequire(uint64(amount) \u003e= ugnotMinDeposit, ufmt.Sprintf(\"Deposit below minimum: %d/%d ugnot.\", amount, ugnotMinDeposit))\n\tcheckErr(banker.Mint(caller, uint64(amount)))\n}\n\nfunc Withdraw(amount uint64) {\n\trequire(amount \u003e= wugnotMinDeposit, ufmt.Sprintf(\"Deposit below minimum: %d/%d wugnot.\", amount, wugnotMinDeposit))\n\n\tcaller := std.PrevRealm().Addr()\n\tpkgaddr := std.CurrentRealm().Addr()\n\tcallerBal := Token.BalanceOf(caller)\n\trequire(amount \u003c= callerBal, ufmt.Sprintf(\"Insufficient balance: %d available, %d needed.\", callerBal, amount))\n\n\t// send swapped ugnots to qcaller\n\tstdBanker := std.GetBanker(std.BankerTypeRealmSend)\n\tsend := std.Coins{{\"ugnot\", int64(amount)}}\n\tstdBanker.SendCoins(pkgaddr, caller, send)\n\tcheckErr(banker.Burn(caller, amount))\n}\n\nfunc Render(path string) string {\n\tparts := strings.Split(path, \"/\")\n\tc := len(parts)\n\n\tswitch {\n\tcase path == \"\":\n\t\treturn banker.RenderHome()\n\tcase c == 2 \u0026\u0026 parts[0] == \"balance\":\n\t\towner := std.Address(parts[1])\n\t\tbalance := Token.BalanceOf(owner)\n\t\treturn ufmt.Sprintf(\"%d\", balance)\n\tdefault:\n\t\treturn \"404\"\n\t}\n}\n\nfunc TotalSupply() uint64 { return Token.TotalSupply() }\n\nfunc BalanceOf(owner pusers.AddressOrName) uint64 {\n\townerAddr := users.Resolve(owner)\n\treturn Token.BalanceOf(ownerAddr)\n}\n\nfunc Allowance(owner, spender pusers.AddressOrName) uint64 {\n\townerAddr := users.Resolve(owner)\n\tspenderAddr := users.Resolve(spender)\n\treturn Token.Allowance(ownerAddr, spenderAddr)\n}\n\nfunc Transfer(to pusers.AddressOrName, amount uint64) {\n\ttoAddr := users.Resolve(to)\n\tcheckErr(Token.Transfer(toAddr, amount))\n}\n\nfunc Approve(spender pusers.AddressOrName, amount uint64) {\n\tspenderAddr := users.Resolve(spender)\n\tcheckErr(Token.Approve(spenderAddr, amount))\n}\n\nfunc TransferFrom(from, to pusers.AddressOrName, amount uint64) {\n\tfromAddr := users.Resolve(from)\n\ttoAddr := users.Resolve(to)\n\tcheckErr(Token.TransferFrom(fromAddr, toAddr, amount))\n}\n\nfunc require(condition bool, msg string) {\n\tif !condition {\n\t\tpanic(msg)\n\t}\n}\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" + }, + { + "name": "z0_filetest.gno", + "body": "// PKGPATH: gno.land/r/demo/wugnot_test\npackage wugnot_test\n\nimport (\n\t\"fmt\"\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/demo/wugnot\"\n\n\tpusers \"gno.land/p/demo/users\"\n)\n\nvar (\n\taddr1 = testutils.TestAddress(\"test1\")\n\taddrc = std.DerivePkgAddr(\"gno.land/r/demo/wugnot\")\n\taddrt = std.DerivePkgAddr(\"gno.land/r/demo/wugnot_test\")\n)\n\nfunc main() {\n\tstd.TestSetOrigPkgAddr(addrc)\n\tstd.TestIssueCoins(addrc, std.Coins{{\"ugnot\", 100000001}}) // TODO: remove this\n\n\t// issue ugnots\n\tstd.TestIssueCoins(addr1, std.Coins{{\"ugnot\", 100000001}})\n\n\t// print initial state\n\tprintBalances()\n\t// println(wugnot.Render(\"queues\"))\n\t// println(\"A -\", wugnot.Render(\"\"))\n\n\tstd.TestSetOrigCaller(addr1)\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 123_400}}, nil)\n\twugnot.Deposit()\n\tprintBalances()\n\twugnot.Withdraw(4242)\n\tprintBalances()\n}\n\nfunc printBalances() {\n\tprintSingleBalance := func(name string, addr std.Address) {\n\t\twugnotBal := wugnot.BalanceOf(pusers.AddressOrName(addr))\n\t\tstd.TestSetOrigCaller(addr)\n\t\trobanker := std.GetBanker(std.BankerTypeReadonly)\n\t\tcoins := robanker.GetCoins(addr).AmountOf(\"ugnot\")\n\t\tfmt.Printf(\"| %-13s | addr=%s | wugnot=%-5d | ugnot=%-9d |\\n\",\n\t\t\tname, addr, wugnotBal, coins)\n\t}\n\tprintln(\"-----------\")\n\tprintSingleBalance(\"wugnot_test\", addrt)\n\tprintSingleBalance(\"wugnot\", addrc)\n\tprintSingleBalance(\"addr1\", addr1)\n\tprintln(\"-----------\")\n}\n\n// Output:\n// -----------\n// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=0 | ugnot=200000000 |\n// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=100000001 |\n// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 |\n// -----------\n// -----------\n// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=123400 | ugnot=200000000 |\n// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=100000001 |\n// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 |\n// -----------\n// -----------\n// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=119158 | ugnot=200004242 |\n// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=99995759 |\n// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 |\n// -----------\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "gnoblog", + "path": "gno.land/r/gnoland/blog", + "files": [ + { + "name": "admin.gno", + "body": "package gnoblog\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/context\"\n\t\"gno.land/p/gov/proposal\"\n)\n\nvar (\n\tadminAddr std.Address\n\tmoderatorList avl.Tree\n\tcommenterList avl.Tree\n\tinPause bool\n)\n\nfunc init() {\n\t// adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis.\n\tadminAddr = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\"\n}\n\nfunc AdminSetAdminAddr(addr std.Address) {\n\tassertIsAdmin()\n\tadminAddr = addr\n}\n\nfunc AdminSetInPause(state bool) {\n\tassertIsAdmin()\n\tinPause = state\n}\n\nfunc AdminAddModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), true)\n}\n\nfunc AdminRemoveModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), false) // FIXME: delete instead?\n}\n\nfunc DaoAddPost(ctx context.Context, slug, title, body, publicationDate, authors, tags string) {\n\tproposal.AssertContextApprovedByGovDAO(ctx)\n\tcaller := std.DerivePkgAddr(\"gno.land/r/gov/dao\")\n\taddPost(caller, slug, title, body, publicationDate, authors, tags)\n}\n\nfunc ModAddPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\tcaller := std.GetOrigCaller()\n\taddPost(caller, slug, title, body, publicationDate, authors, tags)\n}\n\nfunc addPost(caller std.Address, slug, title, body, publicationDate, authors, tags string) {\n\tvar tagList []string\n\tif tags != \"\" {\n\t\ttagList = strings.Split(tags, \",\")\n\t}\n\tvar authorList []string\n\tif authors != \"\" {\n\t\tauthorList = strings.Split(authors, \",\")\n\t}\n\n\terr := b.NewPost(caller, slug, title, body, publicationDate, authorList, tagList)\n\n\tcheckErr(err)\n}\n\nfunc ModEditPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.GetPost(slug).Update(title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc ModRemovePost(slug string) {\n\tassertIsModerator()\n\n\tb.RemovePost(slug)\n}\n\nfunc ModAddCommenter(addr std.Address) {\n\tassertIsModerator()\n\tcommenterList.Set(addr.String(), true)\n}\n\nfunc ModDelCommenter(addr std.Address) {\n\tassertIsModerator()\n\tcommenterList.Set(addr.String(), false) // FIXME: delete instead?\n}\n\nfunc ModDelComment(slug string, index int) {\n\tassertIsModerator()\n\n\terr := b.GetPost(slug).DeleteComment(index)\n\tcheckErr(err)\n}\n\nfunc isAdmin(addr std.Address) bool {\n\treturn addr == adminAddr\n}\n\nfunc isModerator(addr std.Address) bool {\n\t_, found := moderatorList.Get(addr.String())\n\treturn found\n}\n\nfunc isCommenter(addr std.Address) bool {\n\t_, found := commenterList.Get(addr.String())\n\treturn found\n}\n\nfunc assertIsAdmin() {\n\tcaller := std.GetOrigCaller()\n\tif !isAdmin(caller) {\n\t\tpanic(\"access restricted.\")\n\t}\n}\n\nfunc assertIsModerator() {\n\tcaller := std.GetOrigCaller()\n\tif isAdmin(caller) || isModerator(caller) {\n\t\treturn\n\t}\n\tpanic(\"access restricted\")\n}\n\nfunc assertIsCommenter() {\n\tcaller := std.GetOrigCaller()\n\tif isAdmin(caller) || isModerator(caller) || isCommenter(caller) {\n\t\treturn\n\t}\n\tpanic(\"access restricted\")\n}\n\nfunc assertNotInPause() {\n\tif inPause {\n\t\tpanic(\"access restricted (pause)\")\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/gnoland/blog\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/blog v0.0.0-latest\n\tgno.land/p/demo/context v0.0.0-latest\n\tgno.land/p/gov/proposal v0.0.0-latest\n)\n" + }, + { + "name": "gnoblog.gno", + "body": "package gnoblog\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/blog\"\n)\n\nvar b = \u0026blog.Blog{\n\tTitle: \"Gnoland's Blog\",\n\tPrefix: \"/r/gnoland/blog:\",\n}\n\nfunc AddComment(postSlug, comment string) {\n\tassertIsCommenter()\n\tassertNotInPause()\n\n\tcaller := std.GetOrigCaller()\n\terr := b.GetPost(postSlug).AddComment(caller, comment)\n\tcheckErr(err)\n}\n\nfunc Render(path string) string {\n\treturn b.Render(path)\n}\n\nfunc RenderLastPostsWidget(limit int) string {\n\treturn b.RenderLastPostsWidget(limit)\n}\n\nfunc PostExists(slug string) bool {\n\tif b.GetPost(slug) == nil {\n\t\treturn false\n\t}\n\treturn true\n}\n" + }, + { + "name": "gnoblog_test.gno", + "body": "package gnoblog\n\nimport (\n\t\"std\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestPackage(t *testing.T) {\n\tstd.TestSetOrigCaller(std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\"))\n\n\tauthor := std.GetOrigCaller()\n\n\t// by default, no posts.\n\t{\n\t\tgot := Render(\"\")\n\t\texpected := `\n# Gnoland's Blog\n\nNo posts.\n`\n\t\tassertMDEquals(t, got, expected)\n\t}\n\n\t// create two posts, list post.\n\t{\n\t\tModAddPost(\"slug1\", \"title1\", \"body1\", \"2022-05-20T13:17:22Z\", \"moul\", \"tag1,tag2\")\n\t\tModAddPost(\"slug2\", \"title2\", \"body2\", \"2022-05-20T13:17:23Z\", \"moul\", \"tag1,tag3\")\n\t\tgot := Render(\"\")\n\t\texpected := `\n\t# Gnoland's Blog\n\n\u003cdiv class='columns-3'\u003e\u003cdiv\u003e\n\n### [title2](/r/gnoland/blog:p/slug2)\n 20 May 2022\n\u003c/div\u003e\u003cdiv\u003e\n\n### [title1](/r/gnoland/blog:p/slug1)\n 20 May 2022\n\u003c/div\u003e\u003c/div\u003e\n\t`\n\t\tassertMDEquals(t, got, expected)\n\t}\n\n\t// view post.\n\t{\n\t\tgot := Render(\"p/slug2\")\n\t\texpected := `\n\u003cmain class='gno-tmpl-page'\u003e\n\n# title2\n\nbody2\n\n---\n\nTags: [#tag1](/r/gnoland/blog:t/tag1) [#tag3](/r/gnoland/blog:t/tag3)\n\nWritten by moul on 20 May 2022\n\nPublished by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq to Gnoland's Blog\n\n---\n\u003cdetails\u003e\u003csummary\u003eComment section\u003c/summary\u003e\n\n\u003c/details\u003e\n\u003c/main\u003e\n\n\t`\n\t\tassertMDEquals(t, got, expected)\n\t}\n\n\t// list by tags.\n\t{\n\t\tgot := Render(\"t/invalid\")\n\t\texpected := \"# [Gnoland's Blog](/r/gnoland/blog:) / t / invalid\\n\\nNo posts.\"\n\t\tassertMDEquals(t, got, expected)\n\n\t\tgot = Render(\"t/tag2\")\n\t\texpected = `\n# [Gnoland's Blog](/r/gnoland/blog:) / t / tag2\n\n\u003cdiv\u003e\n\n### [title1](/r/gnoland/blog:p/slug1)\n 20 May 2022\n\u003c/div\u003e\n\t`\n\t\tassertMDEquals(t, got, expected)\n\t}\n\n\t// add comments.\n\t{\n\t\tAddComment(\"slug1\", \"comment1\")\n\t\tAddComment(\"slug2\", \"comment2\")\n\t\tAddComment(\"slug1\", \"comment3\")\n\t\tAddComment(\"slug2\", \"comment4\")\n\t\tAddComment(\"slug1\", \"comment5\")\n\t\tgot := Render(\"p/slug2\")\n\t\texpected := `\u003cmain class='gno-tmpl-page'\u003e\n\n# title2\n\nbody2\n\n---\n\nTags: [#tag1](/r/gnoland/blog:t/tag1) [#tag3](/r/gnoland/blog:t/tag3)\n\nWritten by moul on 20 May 2022\n\nPublished by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq to Gnoland's Blog\n\n---\n\u003cdetails\u003e\u003csummary\u003eComment section\u003c/summary\u003e\n\n\u003ch5\u003ecomment4\n\n\u003c/h5\u003e\u003ch6\u003eby g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 13 Feb 09 23:31 UTC\u003c/h6\u003e\n\n---\n\n\u003ch5\u003ecomment2\n\n\u003c/h5\u003e\u003ch6\u003eby g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 13 Feb 09 23:31 UTC\u003c/h6\u003e\n\n---\n\n\u003c/details\u003e\n\u003c/main\u003e\n\t`\n\t\tassertMDEquals(t, got, expected)\n\t}\n\n\t// edit post.\n\t{\n\t\toldTitle := \"title2\"\n\t\toldDate := \"2022-05-20T13:17:23Z\"\n\n\t\tModEditPost(\"slug2\", oldTitle, \"body2++\", oldDate, \"manfred\", \"tag1,tag4\")\n\t\tgot := Render(\"p/slug2\")\n\t\texpected := `\u003cmain class='gno-tmpl-page'\u003e\n\n# title2\n\nbody2++\n\n---\n\nTags: [#tag1](/r/gnoland/blog:t/tag1) [#tag4](/r/gnoland/blog:t/tag4)\n\nWritten by manfred on 20 May 2022\n\nPublished by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq to Gnoland's Blog\n\n---\n\u003cdetails\u003e\u003csummary\u003eComment section\u003c/summary\u003e\n\n\u003ch5\u003ecomment4\n\n\u003c/h5\u003e\u003ch6\u003eby g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 13 Feb 09 23:31 UTC\u003c/h6\u003e\n\n---\n\n\u003ch5\u003ecomment2\n\n\u003c/h5\u003e\u003ch6\u003eby g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 13 Feb 09 23:31 UTC\u003c/h6\u003e\n\n---\n\n\u003c/details\u003e\n\u003c/main\u003e\n\t`\n\t\tassertMDEquals(t, got, expected)\n\n\t\thome := Render(\"\")\n\n\t\tif strings.Count(home, oldTitle) != 1 {\n\t\t\tt.Errorf(\"post not edited properly\")\n\t\t}\n\t\t// Edits work everything except title, slug, and publicationDate\n\t\t// Edits to the above will cause duplication on the blog home page\n\t}\n\n\t{ // Test remove functionality\n\t\ttitle := \"example title\"\n\t\tslug := \"testSlug1\"\n\t\tModAddPost(slug, title, \"body1\", \"2022-05-25T13:17:22Z\", \"moul\", \"tag1,tag2\")\n\n\t\tgot := Render(\"\")\n\n\t\tif !strings.Contains(got, title) {\n\t\t\tt.Errorf(\"post was not added properly\")\n\t\t}\n\n\t\tpostRender := Render(\"p/\" + slug)\n\n\t\tif !strings.Contains(postRender, title) {\n\t\t\tt.Errorf(\"post not rendered properly\")\n\t\t}\n\n\t\tModRemovePost(slug)\n\t\tgot = Render(\"\")\n\n\t\tif strings.Contains(got, title) {\n\t\t\tt.Errorf(\"post was not removed\")\n\t\t}\n\n\t\tpostRender = Render(\"p/\" + slug)\n\n\t\tassertMDEquals(t, postRender, \"404\")\n\t}\n\n\t// TODO: pagination.\n\t// TODO: ?format=...\n\n\t// all 404s\n\t{\n\t\tnotFoundPaths := []string{\n\t\t\t\"p/slug3\",\n\t\t\t\"p\",\n\t\t\t\"p/\",\n\t\t\t\"x/x\",\n\t\t\t\"t\",\n\t\t\t\"t/\",\n\t\t\t\"/\",\n\t\t\t\"p/slug1/\",\n\t\t}\n\t\tfor _, notFoundPath := range notFoundPaths {\n\t\t\tgot := Render(notFoundPath)\n\t\t\texpected := \"404\"\n\t\t\tif got != expected {\n\t\t\t\tt.Errorf(\"path %q: expected %q, got %q.\", notFoundPath, expected, got)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc assertMDEquals(t *testing.T, got, expected string) {\n\tt.Helper()\n\texpected = strings.TrimSpace(expected)\n\tgot = strings.TrimSpace(got)\n\tif expected != got {\n\t\tt.Errorf(\"invalid render output.\\nexpected %q.\\ngot %q.\", expected, got)\n\t}\n}\n" + }, + { + "name": "util.gno", + "body": "package gnoblog\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "events", + "path": "gno.land/r/gnoland/events", + "files": [ + { + "name": "events.gno", + "body": "package events\n\nimport (\n\t\"gno.land/p/demo/ui\"\n)\n\n// XXX: p/demo/ui API is crappy, we need to make it more idiomatic\n// XXX: use an updatable block system to update content from a DAO\n// XXX: var blocks avl.Tree\n\nfunc Render(_ string) string {\n\tdom := ui.DOM{Prefix: \"r/gnoland/events:\"}\n\tdom.Title = \"Gno.land Core Team Attends Industry Events \u0026 Meetups\"\n\tdom.Classes = []string{\"gno-tmpl-section\"}\n\n\t// body\n\tdom.Body.Append(introSection()...)\n\tdom.Body.Append(ui.HR{})\n\tdom.Body.Append(upcomingEvents()...)\n\tdom.Body.Append(ui.HR{})\n\tdom.Body.Append(pastEvents()...)\n\n\treturn dom.String()\n}\n\nfunc introSection() ui.Element {\n\treturn ui.Element{\n\t\tui.Paragraph(\"If you’re interested in building web3 with us, catch up with gno.land in person at one of our industry events. We’re looking to connect with developers and like-minded thinkers who can contribute to the growth of our platform.\"),\n\t}\n}\n\nfunc upcomingEvents() ui.Element {\n\treturn ui.Element{\n\t\tui.H2(\"Upcoming Events\"),\n\t\tui.Text(`\u003cdiv class=\"columns-3\"\u003e\n\u003cdiv class=\"column\"\u003e\n\n### GopherCon EU\n- Come Meet Us at our Booth\n- Berlin, June 17 - 20, 2024\n\n[Learn More](https://gophercon.eu/)\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### GopherCon US\n- Come Meet Us at our Booth\n- Chicago, July 7 - 10, 2024\n\n[Learn More](https://www.gophercon.com/)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### Nebular Summit\n- Join our workshop\n- Brussels, July 12 - 13, 2024\n\n[Learn More](https://nebular.builders/)\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003c/div\u003e\u003c!-- end columns-3--\u003e`),\n\t}\n}\n\nfunc pastEvents() ui.Element {\n\treturn ui.Element{\n\t\tui.H2(\"Past Events\"),\n\t\tui.Text(`\u003cdiv class=\"columns-3\"\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### Gno @ Golang Serbia\n\n- **Join the meetup**\n- Belgrade, May 23, 2024\n\n[Learn more](https://gno.land/r/gnoland/blog:p/gnomes-in-serbia)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### Intro to Gno Tokyo\n\n- **Join the meetup**\n- Tokyo, April 11, 2024\n\n[Learn more](https://gno.land/r/gnoland/blog:p/gno-tokyo)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### Go to Gno Seoul\n\n- **Join the workshop**\n- Seoul, March 23, 2024\n\n[Learn more](https://medium.com/onbloc/go-to-gno-recap-intro-to-the-gno-stack-with-memeland-284a43d7f620)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### GopherCon US\n\n- **Come Meet Us at our Booth**\n- San Diego, September 26 - 29, 2023\n\n[Learn more](https://www.gophercon.com/)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### GopherCon EU\n\n- **Come Meet Us at our Booth**\n- Berlin, July 26 - 29, 2023\n\n[Learn more](https://gophercon.eu/)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### Nebular Summit Gno.land for Developers\n\n- Paris, July 24 - 25, 2023\n- Manfred Touron\n\n[Learn more](https://www.nebular.builders/)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### EthCC\n\n- **Come Meet Us at our Booth**\n- Paris, July 17 - 20, 2023\n- Manfred Touron\n\n[Learn more](https://www.ethcc.io/)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### Eth Seoul\n\n- **The Evolution of Smart Contracts: A Journey into Gno.land**\n- Seoul, June 3, 2023\n- Manfred Touron\n\n[Learn more](https://2023.ethseoul.org/)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### BUIDL Asia\n\n- **Proof of Contribution in Gno.land**\n- Seoul, June 6, 2023\n- Manfred Touron\n\n[Learn more](https://www.buidl.asia/)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Game Developer Conference\n\n- **Side Event: Web3 Gaming Apps Powered by Gno**\n- San Francisco, Mach 23, 2023\n- Jae Kwon\n\n[Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### EthDenver\n\n- **Side Event: Discover Gno.land**\n- Denver, Feb 24 - Mar 5, 2023\n- Jae Kwon\n\n[Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Istanbul Blockchain Week\n\n- Istanbul, Nov 14 - 17, 2022\n- Manfred Touron\n\n[Watch the talk](https://www.youtube.com/watch?v=JX0gdWT0Cg4)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Web Summit Buckle Up and Build with Cosmos\n\n- Lisbon, Nov 1 - 4, 2022\n- Manfred Touron\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Cosmoverse\n\n- Medallin, Sept 26 - 28, 2022\n- Manfred Touron\n\n[Watch the talk](https://www.youtube.com/watch?v=6s1zG7hgxMk)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Berlin Blockchain Week Buckle Up and Build with Cosmos\n\n- Berlin, Sept 11 - 18, 2022\n\n[Watch the talk](https://www.youtube.com/watch?v=hCLErPgnavI)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003c/div\u003e\u003c!-- end columns-3--\u003e`),\n\t}\n}\n" + }, + { + "name": "events_filetest.gno", + "body": "package main\n\nimport \"gno.land/r/gnoland/events\"\n\nfunc main() {\n\tprintln(events.Render(\"\"))\n}\n\n// Output:\n// \u003cmain class='gno-tmpl-section'\u003e\n//\n// # Gno.land Core Team Attends Industry Events \u0026 Meetups\n//\n//\n// If you’re interested in building web3 with us, catch up with gno.land in person at one of our industry events. We’re looking to connect with developers and like-minded thinkers who can contribute to the growth of our platform.\n//\n//\n// ---\n//\n// ## Upcoming Events\n//\n// \u003cdiv class=\"columns-3\"\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### GopherCon EU\n// - Come Meet Us at our Booth\n// - Berlin, June 17 - 20, 2024\n//\n// [Learn More](https://gophercon.eu/)\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### GopherCon US\n// - Come Meet Us at our Booth\n// - Chicago, July 7 - 10, 2024\n//\n// [Learn More](https://www.gophercon.com/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Nebular Summit\n// - Join our workshop\n// - Brussels, July 12 - 13, 2024\n//\n// [Learn More](https://nebular.builders/)\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003c/div\u003e\u003c!-- end columns-3--\u003e\n//\n// ---\n//\n// ## Past Events\n//\n// \u003cdiv class=\"columns-3\"\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Gno @ Golang Serbia\n//\n// - **Join the meetup**\n// - Belgrade, May 23, 2024\n//\n// [Learn more](https://gno.land/r/gnoland/blog:p/gnomes-in-serbia)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Intro to Gno Tokyo\n//\n// - **Join the meetup**\n// - Tokyo, April 11, 2024\n//\n// [Learn more](https://gno.land/r/gnoland/blog:p/gno-tokyo)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Go to Gno Seoul\n//\n// - **Join the workshop**\n// - Seoul, March 23, 2024\n//\n// [Learn more](https://medium.com/onbloc/go-to-gno-recap-intro-to-the-gno-stack-with-memeland-284a43d7f620)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### GopherCon US\n//\n// - **Come Meet Us at our Booth**\n// - San Diego, September 26 - 29, 2023\n//\n// [Learn more](https://www.gophercon.com/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### GopherCon EU\n//\n// - **Come Meet Us at our Booth**\n// - Berlin, July 26 - 29, 2023\n//\n// [Learn more](https://gophercon.eu/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Nebular Summit Gno.land for Developers\n//\n// - Paris, July 24 - 25, 2023\n// - Manfred Touron\n//\n// [Learn more](https://www.nebular.builders/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### EthCC\n//\n// - **Come Meet Us at our Booth**\n// - Paris, July 17 - 20, 2023\n// - Manfred Touron\n//\n// [Learn more](https://www.ethcc.io/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Eth Seoul\n//\n// - **The Evolution of Smart Contracts: A Journey into Gno.land**\n// - Seoul, June 3, 2023\n// - Manfred Touron\n//\n// [Learn more](https://2023.ethseoul.org/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### BUIDL Asia\n//\n// - **Proof of Contribution in Gno.land**\n// - Seoul, June 6, 2023\n// - Manfred Touron\n//\n// [Learn more](https://www.buidl.asia/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Game Developer Conference\n//\n// - **Side Event: Web3 Gaming Apps Powered by Gno**\n// - San Francisco, Mach 23, 2023\n// - Jae Kwon\n//\n// [Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### EthDenver\n//\n// - **Side Event: Discover Gno.land**\n// - Denver, Feb 24 - Mar 5, 2023\n// - Jae Kwon\n//\n// [Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Istanbul Blockchain Week\n//\n// - Istanbul, Nov 14 - 17, 2022\n// - Manfred Touron\n//\n// [Watch the talk](https://www.youtube.com/watch?v=JX0gdWT0Cg4)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Web Summit Buckle Up and Build with Cosmos\n//\n// - Lisbon, Nov 1 - 4, 2022\n// - Manfred Touron\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Cosmoverse\n//\n// - Medallin, Sept 26 - 28, 2022\n// - Manfred Touron\n//\n// [Watch the talk](https://www.youtube.com/watch?v=6s1zG7hgxMk)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Berlin Blockchain Week Buckle Up and Build with Cosmos\n//\n// - Berlin, Sept 11 - 18, 2022\n//\n// [Watch the talk](https://www.youtube.com/watch?v=hCLErPgnavI)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003c/div\u003e\u003c!-- end columns-3--\u003e\n//\n// \u003c/main\u003e\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/gnoland/events\n\nrequire gno.land/p/demo/ui v0.0.0-latest\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "faucet", + "path": "gno.land/r/gnoland/faucet", + "files": [ + { + "name": "admin.gno", + "body": "package faucet\n\nimport (\n\t\"errors\"\n\t\"std\"\n)\n\nfunc AdminSetInPause(inPause bool) string {\n\tif err := assertIsAdmin(); err != nil {\n\t\treturn err.Error()\n\t}\n\tgInPause = inPause\n\treturn \"\"\n}\n\nfunc AdminSetMessage(message string) string {\n\tif err := assertIsAdmin(); err != nil {\n\t\treturn err.Error()\n\t}\n\tgMessage = message\n\treturn \"\"\n}\n\nfunc AdminSetTransferLimit(amount int64) string {\n\tif err := assertIsAdmin(); err != nil {\n\t\treturn err.Error()\n\t}\n\tgLimit = std.NewCoin(\"ugnot\", amount)\n\treturn \"\"\n}\n\nfunc AdminSetAdminAddr(addr std.Address) string {\n\tif err := assertIsAdmin(); err != nil {\n\t\treturn err.Error()\n\t}\n\tgAdminAddr = addr\n\treturn \"\"\n}\n\nfunc AdminAddController(addr std.Address) string {\n\tif err := assertIsAdmin(); err != nil {\n\t\treturn err.Error()\n\t}\n\n\tsize := gControllers.Size()\n\n\tif size \u003e= gControllersMaxSize {\n\t\treturn \"can not add more controllers than allowed\"\n\t}\n\n\tif gControllers.Has(addr.String()) {\n\t\treturn addr.String() + \" exists, no need to add.\"\n\t}\n\n\tgControllers.Set(addr.String(), addr)\n\n\treturn \"\"\n}\n\nfunc AdminRemoveController(addr std.Address) string {\n\tif err := assertIsAdmin(); err != nil {\n\t\treturn err.Error()\n\t}\n\n\tif !gControllers.Has(addr.String()) {\n\t\treturn addr.String() + \" is not on the controller list\"\n\t}\n\n\t_, ok := gControllers.Remove(addr.String())\n\n\t// it not should happen.\n\t// we will check anyway to prevent issues in the underline implementation.\n\n\tif !ok {\n\t\treturn addr.String() + \" is not on the controller list\"\n\t}\n\n\treturn \"\"\n}\n\nfunc assertIsAdmin() error {\n\tcaller := std.GetOrigCaller()\n\tif caller != gAdminAddr {\n\t\treturn errors.New(\"restricted for admin\")\n\t}\n\treturn nil\n}\n" + }, + { + "name": "faucet.gno", + "body": "package faucet\n\nimport (\n\t\"errors\"\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar (\n\t// configurable by admin.\n\tgAdminAddr std.Address = std.Address(\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\")\n\tgControllers = avl.NewTree()\n\tgControllersMaxSize = 10 // limit it to 10\n\tgInPause = false\n\tgMessage = \"# Community Faucet.\\n\\n\"\n\n\t// internal vars, for stats.\n\tgTotalTransferred std.Coins\n\tgTotalTransfers = uint(0)\n\n\t// per request limit, 350 gnot\n\tgLimit std.Coin = std.NewCoin(\"ugnot\", 350000000)\n)\n\nfunc Transfer(to std.Address, send int64) string {\n\tif err := assertIsController(); err != nil {\n\t\treturn err.Error()\n\t}\n\n\tif gInPause {\n\t\treturn errors.New(\"faucet in pause\").Error()\n\t}\n\n\t// limit the per request\n\tif send \u003e gLimit.Amount {\n\t\treturn errors.New(\"Per request limit \" + gLimit.String() + \" exceed\").Error()\n\t}\n\tsendCoins := std.Coins{std.NewCoin(\"ugnot\", send)}\n\n\tgTotalTransferred = gTotalTransferred.Add(sendCoins)\n\tgTotalTransfers++\n\n\tbanker := std.GetBanker(std.BankerTypeRealmSend)\n\tpkgaddr := std.CurrentRealm().Addr()\n\tbanker.SendCoins(pkgaddr, to, sendCoins)\n\treturn \"\"\n}\n\nfunc GetPerTransferLimit() int64 {\n\treturn gLimit.Amount\n}\n\nfunc Render(_ string) string {\n\tbanker := std.GetBanker(std.BankerTypeRealmSend)\n\tbalance := banker.GetCoins(std.CurrentRealm().Addr())\n\n\toutput := gMessage\n\tif gInPause {\n\t\toutput += \"Status: inactive.\\n\"\n\t} else {\n\t\toutput += \"Status: active.\\n\"\n\t}\n\toutput += ufmt.Sprintf(\"Balance: %s.\\n\", balance.String())\n\toutput += ufmt.Sprintf(\"Total transfers: %s (in %d times).\\n\\n\", gTotalTransferred.String(), gTotalTransfers)\n\n\toutput += \"Package address: \" + std.CurrentRealm().Addr().String() + \"\\n\\n\"\n\toutput += ufmt.Sprintf(\"Admin: %s\\n\\n \", gAdminAddr.String())\n\toutput += ufmt.Sprintf(\"Controllers:\\n\\n \")\n\n\tfor i := 0; i \u003c gControllers.Size(); i++ {\n\t\t_, v := gControllers.GetByIndex(i)\n\t\toutput += ufmt.Sprintf(\"%s \", v.(std.Address))\n\t}\n\n\toutput += \"\\n\\n\"\n\toutput += ufmt.Sprintf(\"Per request limit: %s\\n\\n\", gLimit.String())\n\n\treturn output\n}\n\nfunc assertIsController() error {\n\tcaller := std.GetOrigCaller()\n\n\tok := gControllers.Has(caller.String())\n\tif !ok {\n\t\treturn errors.New(caller.String() + \" is not on the controller list\")\n\t}\n\treturn nil\n}\n" + }, + { + "name": "faucet_test.gno", + "body": "package faucet\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/gnoland/faucet\"\n)\n\nfunc TestPackage(t *testing.T) {\n\tvar (\n\t\tadminaddr = std.Address(\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\")\n\t\tfaucetaddr = std.DerivePkgAddr(\"gno.land/r/gnoland/faucet\")\n\t\tcontrolleraddr1 = testutils.TestAddress(\"controller1\")\n\t\tcontrolleraddr2 = testutils.TestAddress(\"controller2\")\n\t\tcontrolleraddr3 = testutils.TestAddress(\"controller3\")\n\t\tcontrolleraddr4 = testutils.TestAddress(\"controller4\")\n\t\tcontrolleraddr5 = testutils.TestAddress(\"controller5\")\n\t\tcontrolleraddr6 = testutils.TestAddress(\"controller6\")\n\t\tcontrolleraddr7 = testutils.TestAddress(\"controller7\")\n\t\tcontrolleraddr8 = testutils.TestAddress(\"controller8\")\n\t\tcontrolleraddr9 = testutils.TestAddress(\"controller9\")\n\t\tcontrolleraddr10 = testutils.TestAddress(\"controller10\")\n\t\tcontrolleraddr11 = testutils.TestAddress(\"controller11\")\n\n\t\ttest1addr = testutils.TestAddress(\"test1\")\n\t)\n\t// deposit 1000gnot to faucet contract\n\tstd.TestIssueCoins(faucetaddr, std.Coins{{\"ugnot\", 1000000000}})\n\tassertBalance(t, faucetaddr, 1200000000)\n\n\t// by default, balance is empty, and as a user I cannot call Transfer, or Admin commands.\n\n\tassertBalance(t, test1addr, 0)\n\tstd.TestSetOrigCaller(test1addr)\n\tassertErr(t, faucet.Transfer(test1addr, 1000000))\n\n\tassertErr(t, faucet.AdminAddController(controlleraddr1))\n\tstd.TestSetOrigCaller(controlleraddr1)\n\tassertErr(t, faucet.Transfer(test1addr, 1000000))\n\n\t// as an admin, add the controller to contract and deposit more 2000gnot to contract\n\tstd.TestSetOrigCaller(adminaddr)\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr1))\n\tassertBalance(t, faucetaddr, 1200000000)\n\n\t// now, send some tokens as controller.\n\tstd.TestSetOrigCaller(controlleraddr1)\n\tassertNoErr(t, faucet.Transfer(test1addr, 1000000))\n\tassertBalance(t, test1addr, 1000000)\n\tassertNoErr(t, faucet.Transfer(test1addr, 1000000))\n\tassertBalance(t, test1addr, 2000000)\n\tassertBalance(t, faucetaddr, 1198000000)\n\n\t// remove controller\n\t// as an admin, remove controller\n\tstd.TestSetOrigCaller(adminaddr)\n\tassertNoErr(t, faucet.AdminRemoveController(controlleraddr1))\n\tstd.TestSetOrigCaller(controlleraddr1)\n\tassertErr(t, faucet.Transfer(test1addr, 1000000))\n\n\t// duplicate controller\n\tstd.TestSetOrigCaller(adminaddr)\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr1))\n\tassertErr(t, faucet.AdminAddController(controlleraddr1))\n\t// add more than more than allowed controllers\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr2))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr3))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr4))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr5))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr6))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr7))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr8))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr9))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr10))\n\tassertErr(t, faucet.AdminAddController(controlleraddr11))\n\n\t// send more than per transfer limit\n\tstd.TestSetOrigCaller(adminaddr)\n\tfaucet.AdminSetTransferLimit(300000000)\n\tstd.TestSetOrigCaller(controlleraddr1)\n\tassertErr(t, faucet.Transfer(test1addr, 301000000))\n\n\t// block transefer from the address not on the controllers list.\n\tstd.TestSetOrigCaller(controlleraddr11)\n\tassertErr(t, faucet.Transfer(test1addr, 1000000))\n}\n\nfunc assertErr(t *testing.T, err string) {\n\tt.Helper()\n\n\tif err == \"\" {\n\t\tt.Logf(\"info: got err: %v\", err)\n\t\tt.Errorf(\"expected an error, got nil.\")\n\t}\n}\n\nfunc assertNoErr(t *testing.T, err string) {\n\tt.Helper()\n\tif err != \"\" {\n\t\tt.Errorf(\"got err: %v.\", err)\n\t}\n}\n\nfunc assertBalance(t *testing.T, addr std.Address, expectedBal int64) {\n\tt.Helper()\n\tbanker := std.GetBanker(std.BankerTypeReadonly)\n\tcoins := banker.GetCoins(addr)\n\tgot := coins.AmountOf(\"ugnot\")\n\n\tif expectedBal != got {\n\t\tt.Errorf(\"invalid balance: expected %d, got %d.\", expectedBal, got)\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/gnoland/faucet\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" + }, + { + "name": "z0_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/gnoland/faucet\"\n)\n\n// mints ugnot to current realm\nfunc init() {\n\tfacuetaddr := std.DerivePkgAddr(\"gno.land/r/gnoland/faucet\")\n\tstd.TestIssueCoins(facuetaddr, std.Coins{{\"ugnot\", 200000000}})\n}\n\n// assert render with empty path and no controllers\nfunc main() {\n\tprintln(faucet.Render(\"\"))\n}\n\n// Output:\n// # Community Faucet.\n//\n// Status: active.\n// Balance: 200000000ugnot.\n// Total transfers: (in 0 times).\n//\n// Package address: g1ttrq7mp4zy6dssnmgyyktnn4hcj3ys8xhju0n7\n//\n// Admin: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\n//\n// Controllers:\n//\n//\n//\n// Per request limit: 350000000ugnot\n" + }, + { + "name": "z1_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/gnoland/faucet\"\n)\n\n// mints ugnot to current realm\nfunc init() {\n\tfacuetaddr := std.DerivePkgAddr(\"gno.land/r/gnoland/faucet\")\n\tstd.TestIssueCoins(facuetaddr, std.Coins{{\"ugnot\", 200000000}})\n}\n\n// assert render with a path and no controllers\nfunc main() {\n\tprintln(faucet.Render(\"path\"))\n}\n\n// Output:\n// # Community Faucet.\n//\n// Status: active.\n// Balance: 200000000ugnot.\n// Total transfers: (in 0 times).\n//\n// Package address: g1ttrq7mp4zy6dssnmgyyktnn4hcj3ys8xhju0n7\n//\n// Admin: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\n//\n// Controllers:\n//\n//\n//\n// Per request limit: 350000000ugnot\n" + }, + { + "name": "z2_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/gnoland/faucet\"\n)\n\n// mints ugnot to current realm\nfunc init() {\n\tfacuetaddr := std.DerivePkgAddr(\"gno.land/r/gnoland/faucet\")\n\tstd.TestIssueCoins(facuetaddr, std.Coins{{\"ugnot\", 200000000}})\n}\n\n// assert render with empty path and 2 controllers\nfunc main() {\n\tvar (\n\t\tadminaddr = std.Address(\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\")\n\t\tcontrolleraddr1 = testutils.TestAddress(\"controller1\")\n\t\tcontrolleraddr2 = testutils.TestAddress(\"controller2\")\n\t)\n\tstd.TestSetOrigCaller(adminaddr)\n\terr := faucet.AdminAddController(controlleraddr1)\n\tif err != \"\" {\n\t\tpanic(err)\n\t}\n\terr = faucet.AdminAddController(controlleraddr2)\n\tif err != \"\" {\n\t\tpanic(err)\n\t}\n\tprintln(faucet.Render(\"\"))\n}\n\n// Output:\n// # Community Faucet.\n//\n// Status: active.\n// Balance: 200000000ugnot.\n// Total transfers: (in 0 times).\n//\n// Package address: g1ttrq7mp4zy6dssnmgyyktnn4hcj3ys8xhju0n7\n//\n// Admin: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\n//\n// Controllers:\n//\n// g1vdhkuarjdakxcetjx9047h6lta047h6lsdacav g1vdhkuarjdakxcetjxf047h6lta047h6lnrev3v\n//\n// Per request limit: 350000000ugnot\n" + }, + { + "name": "z3_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/gnoland/faucet\"\n)\n\n// mints coints to current realm\nfunc init() {\n\tfacuetaddr := std.DerivePkgAddr(\"gno.land/r/gnoland/faucet\")\n\tstd.TestIssueCoins(facuetaddr, std.Coins{{\"ugnot\", 200000000}})\n}\n\n// assert render with 2 controllers and 2 transfers\nfunc main() {\n\tvar (\n\t\tadminaddr = std.Address(\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\")\n\t\tcontrolleraddr1 = testutils.TestAddress(\"controller1\")\n\t\tcontrolleraddr2 = testutils.TestAddress(\"controller2\")\n\t\ttestaddr1 = testutils.TestAddress(\"test1\")\n\t\ttestaddr2 = testutils.TestAddress(\"test2\")\n\t)\n\tstd.TestSetOrigCaller(adminaddr)\n\terr := faucet.AdminAddController(controlleraddr1)\n\tif err != \"\" {\n\t\tpanic(err)\n\t}\n\terr = faucet.AdminAddController(controlleraddr2)\n\tif err != \"\" {\n\t\tpanic(err)\n\t}\n\tstd.TestSetOrigCaller(controlleraddr1)\n\terr = faucet.Transfer(testaddr1, 1000000)\n\tif err != \"\" {\n\t\tpanic(err)\n\t}\n\tstd.TestSetOrigCaller(controlleraddr2)\n\terr = faucet.Transfer(testaddr1, 2000000)\n\tif err != \"\" {\n\t\tpanic(err)\n\t}\n\tprintln(faucet.Render(\"\"))\n}\n\n// Output:\n// # Community Faucet.\n//\n// Status: active.\n// Balance: 197000000ugnot.\n// Total transfers: 3000000ugnot (in 2 times).\n//\n// Package address: g1ttrq7mp4zy6dssnmgyyktnn4hcj3ys8xhju0n7\n//\n// Admin: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\n//\n// Controllers:\n//\n// g1vdhkuarjdakxcetjx9047h6lta047h6lsdacav g1vdhkuarjdakxcetjxf047h6lta047h6lnrev3v\n//\n// Per request limit: 350000000ugnot\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "ghverify", + "path": "gno.land/r/gnoland/ghverify", + "files": [ + { + "name": "README.md", + "body": "# ghverify\n\nThis realm is intended to enable off chain gno address to github handle verification.\nThe steps are as follows:\n- A user calls `RequestVerification` and provides a github handle. This creates a new static oracle feed.\n- An off-chain agent controlled by the owner of this realm requests current feeds using the `GnorkleEntrypoint` function and provides a message of `\"request\"`\n- The agent receives the task information that includes the github handle and the gno address. It performs the verification step by checking whether this github user has the address in a github repository it controls.\n- The agent publishes the result of the verification by calling `GnorkleEntrypoint` with a message structured like: `\"ingest,\u003ctask id\u003e,\u003cverification status\u003e\"`. The verification status is `OK` if verification succeeded and any other value if it failed.\n- The oracle feed's ingester processes the verification and the handle to address mapping is written to the avl trees that exist as ghverify realm variables." + }, + { + "name": "contract.gno", + "body": "package ghverify\n\nimport (\n\t\"errors\"\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/gnorkle/feeds/static\"\n\t\"gno.land/p/demo/gnorkle/gnorkle\"\n\t\"gno.land/p/demo/gnorkle/message\"\n)\n\nconst (\n\t// The agent should send this value if it has verified the github handle.\n\tverifiedResult = \"OK\"\n)\n\nvar (\n\townerAddress = std.GetOrigCaller()\n\toracle *gnorkle.Instance\n\tpostHandler postGnorkleMessageHandler\n\n\thandleToAddressMap = avl.NewTree()\n\taddressToHandleMap = avl.NewTree()\n)\n\nfunc init() {\n\toracle = gnorkle.NewInstance()\n\toracle.AddToWhitelist(\"\", []string{string(ownerAddress)})\n}\n\ntype postGnorkleMessageHandler struct{}\n\n// Handle does post processing after a message is ingested by the oracle feed. It extracts the value to realm\n// storage and removes the feed from the oracle.\nfunc (h postGnorkleMessageHandler) Handle(i *gnorkle.Instance, funcType message.FuncType, feed gnorkle.Feed) error {\n\tif funcType != message.FuncTypeIngest {\n\t\treturn nil\n\t}\n\n\tresult, _, consumable := feed.Value()\n\tif !consumable {\n\t\treturn nil\n\t}\n\n\t// The value is consumable, meaning the ingestion occurred, so we can remove the feed from the oracle\n\t// after saving it to realm storage.\n\tdefer oracle.RemoveFeed(feed.ID())\n\n\t// Couldn't verify; nothing to do.\n\tif result.String != verifiedResult {\n\t\treturn nil\n\t}\n\n\tfeedTasks := feed.Tasks()\n\tif len(feedTasks) != 1 {\n\t\treturn errors.New(\"expected feed to have exactly one task\")\n\t}\n\n\ttask, ok := feedTasks[0].(*verificationTask)\n\tif !ok {\n\t\treturn errors.New(\"expected ghverify task\")\n\t}\n\n\thandleToAddressMap.Set(task.githubHandle, task.gnoAddress)\n\taddressToHandleMap.Set(task.gnoAddress, task.githubHandle)\n\treturn nil\n}\n\n// RequestVerification creates a new static feed with a single task that will\n// instruct an agent to verify the github handle / gno address pair.\nfunc RequestVerification(githubHandle string) {\n\tgnoAddress := string(std.GetOrigCaller())\n\tif err := oracle.AddFeeds(\n\t\tstatic.NewSingleValueFeed(\n\t\t\tgnoAddress,\n\t\t\t\"string\",\n\t\t\t\u0026verificationTask{\n\t\t\t\tgnoAddress: gnoAddress,\n\t\t\t\tgithubHandle: githubHandle,\n\t\t\t},\n\t\t),\n\t); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// GnorkleEntrypoint is the entrypoint to the gnorkle oracle handler.\nfunc GnorkleEntrypoint(message string) string {\n\tresult, err := oracle.HandleMessage(message, postHandler)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn result\n}\n\n// SetOwner transfers ownership of the contract to the given address.\nfunc SetOwner(owner std.Address) {\n\tif ownerAddress != std.GetOrigCaller() {\n\t\tpanic(\"only the owner can set a new owner\")\n\t}\n\n\townerAddress = owner\n\n\t// In the context of this contract, the owner is the only one that can\n\t// add new feeds to the oracle.\n\toracle.ClearWhitelist(\"\")\n\toracle.AddToWhitelist(\"\", []string{string(ownerAddress)})\n}\n\n// GetHandleByAddress returns the github handle associated with the given gno address.\nfunc GetHandleByAddress(address string) string {\n\tif value, ok := addressToHandleMap.Get(address); ok {\n\t\treturn value.(string)\n\t}\n\n\treturn \"\"\n}\n\n// GetAddressByHandle returns the gno address associated with the given github handle.\nfunc GetAddressByHandle(handle string) string {\n\tif value, ok := handleToAddressMap.Get(handle); ok {\n\t\treturn value.(string)\n\t}\n\n\treturn \"\"\n}\n\n// Render returns a json object string will all verified handle -\u003e address mappings.\nfunc Render(_ string) string {\n\tresult := \"{\"\n\tvar appendComma bool\n\thandleToAddressMap.Iterate(\"\", \"\", func(handle string, address interface{}) bool {\n\t\tif appendComma {\n\t\t\tresult += \",\"\n\t\t}\n\n\t\tresult += `\"` + handle + `\": \"` + address.(string) + `\"`\n\t\tappendComma = true\n\n\t\treturn true\n\t})\n\n\treturn result + \"}\"\n}\n" + }, + { + "name": "contract_test.gno", + "body": "package ghverify\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n)\n\nfunc TestVerificationLifecycle(t *testing.T) {\n\tdefaultAddress := std.GetOrigCaller()\n\tuserAddress := std.Address(testutils.TestAddress(\"user\"))\n\n\t// Verify request returns no feeds.\n\tresult := GnorkleEntrypoint(\"request\")\n\tif result != \"[]\" {\n\t\tt.Fatalf(\"expected empty request result, got %s\", result)\n\t}\n\n\t// Make a verification request with the created user.\n\tstd.TestSetOrigCaller(userAddress)\n\tRequestVerification(\"deelawn\")\n\n\t// A subsequent request from the same address should panic because there is\n\t// already a feed with an ID of this user's address.\n\tvar errMsg string\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\terrMsg = r.(error).Error()\n\t\t\t}\n\t\t}()\n\t\tRequestVerification(\"deelawn\")\n\t}()\n\tif errMsg != \"feed already exists\" {\n\t\tt.Fatalf(\"expected feed already exists, got %s\", errMsg)\n\t}\n\n\t// Verify the request returns no feeds for this non-whitelisted user.\n\tresult = GnorkleEntrypoint(\"request\")\n\tif result != \"[]\" {\n\t\tt.Fatalf(\"expected empty request result, got %s\", result)\n\t}\n\n\t// Set the caller back to the whitelisted user and verify that the feed data\n\t// returned matches what should have been created by the `RequestVerification`\n\t// invocation.\n\tstd.TestSetOrigCaller(defaultAddress)\n\tresult = GnorkleEntrypoint(\"request\")\n\texpResult := `[{\"id\":\"` + string(userAddress) + `\",\"type\":\"0\",\"value_type\":\"string\",\"tasks\":[{\"gno_address\":\"` +\n\t\tstring(userAddress) + `\",\"github_handle\":\"deelawn\"}]}]`\n\tif result != expResult {\n\t\tt.Fatalf(\"expected request result %s, got %s\", expResult, result)\n\t}\n\n\t// Try to trigger feed ingestion from the non-authorized user.\n\tstd.TestSetOrigCaller(userAddress)\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\terrMsg = r.(error).Error()\n\t\t\t}\n\t\t}()\n\t\tGnorkleEntrypoint(\"ingest,\" + string(userAddress) + \",OK\")\n\t}()\n\tif errMsg != \"caller not whitelisted\" {\n\t\tt.Fatalf(\"expected caller not whitelisted, got %s\", errMsg)\n\t}\n\n\t// Set the caller back to the whitelisted user and transfer contract ownership.\n\tstd.TestSetOrigCaller(defaultAddress)\n\tSetOwner(userAddress)\n\n\t// Now trigger the feed ingestion from the user and new owner and only whitelisted address.\n\tstd.TestSetOrigCaller(userAddress)\n\tGnorkleEntrypoint(\"ingest,\" + string(userAddress) + \",OK\")\n\n\t// Verify the ingestion autocommitted the value and triggered the post handler.\n\tdata := Render(\"\")\n\texpResult = `{\"deelawn\": \"` + string(userAddress) + `\"}`\n\tif data != expResult {\n\t\tt.Fatalf(\"expected render data %s, got %s\", expResult, data)\n\t}\n\n\t// Finally make sure the feed was cleaned up after the data was committed.\n\tresult = GnorkleEntrypoint(\"request\")\n\tif result != \"[]\" {\n\t\tt.Fatalf(\"expected empty request result, got %s\", result)\n\t}\n\n\t// Check that the accessor functions are working as expected.\n\tif handle := GetHandleByAddress(string(userAddress)); handle != \"deelawn\" {\n\t\tt.Fatalf(\"expected deelawn, got %s\", handle)\n\t}\n\tif address := GetAddressByHandle(\"deelawn\"); address != string(userAddress) {\n\t\tt.Fatalf(\"expected %s, got %s\", string(userAddress), address)\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/gnoland/ghverify\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/gnorkle/feeds/static v0.0.0-latest\n\tgno.land/p/demo/gnorkle/gnorkle v0.0.0-latest\n\tgno.land/p/demo/gnorkle/message v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n)\n" + }, + { + "name": "task.gno", + "body": "package ghverify\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n)\n\ntype verificationTask struct {\n\tgnoAddress string\n\tgithubHandle string\n}\n\n// MarshalJSON marshals the task contents to JSON.\nfunc (t *verificationTask) MarshalJSON() ([]byte, error) {\n\tbuf := new(bytes.Buffer)\n\tw := bufio.NewWriter(buf)\n\n\tw.Write(\n\t\t[]byte(`{\"gno_address\":\"` + t.gnoAddress + `\",\"github_handle\":\"` + t.githubHandle + `\"}`),\n\t)\n\n\tw.Flush()\n\treturn buf.Bytes(), nil\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "home", + "path": "gno.land/r/gnoland/home", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/gnoland/home\n\nrequire (\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/ui v0.0.0-latest\n\tgno.land/r/gnoland/blog v0.0.0-latest\n)\n" + }, + { + "name": "home.gno", + "body": "package home\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/demo/ui\"\n\tblog \"gno.land/r/gnoland/blog\"\n)\n\n// XXX: p/demo/ui API is crappy, we need to make it more idiomatic\n// XXX: use an updatable block system to update content from a DAO\n// XXX: var blocks avl.Tree\n\nfunc Render(_ string) string {\n\tdom := ui.DOM{Prefix: \"r/gnoland/home:\"}\n\tdom.Title = \"Welcome to gno.land\"\n\tdom.Classes = []string{\"gno-tmpl-section\"}\n\n\t// body\n\tdom.Body.Append(introSection()...)\n\n\tdom.Body.Append(ui.Jumbotron(discoverLinks()))\n\n\tdom.Body.Append(\n\t\tui.Columns{3, []ui.Element{\n\t\t\tlastBlogposts(4),\n\t\t\tupcomingEvents(4),\n\t\t\tlastContributions(4),\n\t\t}},\n\t)\n\n\tdom.Body.Append(ui.HR{})\n\tdom.Body.Append(playgroundSection()...)\n\tdom.Body.Append(ui.HR{})\n\tdom.Body.Append(packageStaffPicks()...)\n\tdom.Body.Append(ui.HR{})\n\tdom.Body.Append(worxDAO()...)\n\tdom.Body.Append(ui.HR{})\n\t// footer\n\tdom.Footer.Append(\n\t\tui.Columns{2, []ui.Element{\n\t\t\tsocialLinks(),\n\t\t\tquoteOfTheBlock(),\n\t\t}},\n\t)\n\n\t// Testnet disclaimer\n\tdom.Footer.Append(\n\t\tui.HR{},\n\t\tui.Bold(\"This is a testnet.\"),\n\t\tui.Text(\"Package names are not guaranteed to be available for production.\"),\n\t)\n\n\treturn dom.String()\n}\n\nfunc lastBlogposts(limit int) ui.Element {\n\tposts := blog.RenderLastPostsWidget(limit)\n\treturn ui.Element{\n\t\tui.H3(\"Latest Blogposts\"),\n\t\tui.Text(posts),\n\t}\n}\n\nfunc lastContributions(limit int) ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"Latest Contributions\"),\n\t\t// TODO: import r/gh to\n\t\tui.Link{Text: \"View latest contributions\", URL: \"https://github.com/gnolang/gno/pulls\"},\n\t}\n}\n\nfunc upcomingEvents(limit int) ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"Upcoming Events\"),\n\t\t// TODO: replace with r/gnoland/events\n\t\tui.Text(\"[View upcoming events](/events)\"),\n\t}\n}\n\nfunc introSection() ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"We’re building gno.land, set to become the leading open-source smart contract platform, using Gno, an interpreted and fully deterministic variation of the Go programming language for succinct and composable smart contracts.\"),\n\t\tui.Paragraph(\"With transparent and timeless code, gno.land is the next generation of smart contract platforms, serving as the “GitHub” of the ecosystem, with realms built using fully transparent, auditable code that anyone can inspect and reuse.\"),\n\t\tui.Paragraph(\"Intuitive and easy to use, gno.land lowers the barrier to web3 and makes censorship-resistant platforms accessible to everyone. If you want to help lay the foundations of a fairer and freer world, join us today.\"),\n\t}\n}\n\nfunc worxDAO() ui.Element {\n\t// WorxDAO\n\t// XXX(manfred): please, let me finish a v0, then we can iterate\n\t// highest level == highest responsibility\n\t// teams are responsible for components they don't owne\n\t// flag : realm maintainers VS facilitators\n\t// teams\n\t// committee of trustees to create the directory\n\t// each directory is a name, has a parent and have groups\n\t// homepage team - blocks aggregating events\n\t// XXX: TODO\n\t/*`\n\t# Directory\n\n\t* gno.land (owned by group)\n\t *\n\t* gnovm\n\t * gnolang (language)\n\t * gnovm\n\t - current challenges / concerns / issues\n\t* tm2\n\t * amino\n\t *\n\n\t## Contributors\n\t``*/\n\treturn ui.Element{\n\t\tui.H3(\"Contributions (WorxDAO \u0026 GoR)\"),\n\t\t// TODO: GoR dashboard + WorxDAO topics\n\t\tui.Text(`coming soon`),\n\t}\n}\n\nfunc quoteOfTheBlock() ui.Element {\n\tquotes := []string{\n\t\t\"Gno is for Truth.\",\n\t\t\"Gno is for Social Coordination.\",\n\t\t\"Gno is _not only_ for DeFi.\",\n\t\t\"Now, you Gno.\",\n\t\t\"Come for the Go, Stay for the Gno.\",\n\t}\n\theight := std.GetHeight()\n\tidx := int(height) % len(quotes)\n\tqotb := quotes[idx]\n\n\treturn ui.Element{\n\t\tui.H3(ufmt.Sprintf(\"Quote of the ~Day~ Block#%d\", height)),\n\t\tui.Quote(qotb),\n\t}\n}\n\nfunc socialLinks() ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"Socials\"),\n\t\tui.BulletList{\n\t\t\t// XXX: improve UI to support a nice GO api for such links\n\t\t\tui.Text(\"Check out our [community projects](https://github.com/gnolang/awesome-gno)\"),\n\t\t\tui.Text(\"![Discord](static/img/ico-discord.svg) [Discord](https://discord.gg/S8nKUqwkPn)\"),\n\t\t\tui.Text(\"![Twitter](static/img/ico-twitter.svg) [Twitter](https://twitter.com/_gnoland)\"),\n\t\t\tui.Text(\"![Youtube](static/img/ico-youtube.svg) [Youtube](https://www.youtube.com/@_gnoland)\"),\n\t\t\tui.Text(\"![Telegram](static/img/ico-telegram.svg) [Telegram](https://t.me/gnoland)\"),\n\t\t},\n\t}\n}\n\nfunc playgroundSection() ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"[Gno Playground](https://play.gno.land)\"),\n\t\tui.Paragraph(`Gno Playground is a web application designed for building, running, testing, and interacting\nwith your Gno code, enhancing your understanding of the Gno language. With Gno Playground, you can share your code,\nexecute tests, deploy your realms and packages to gno.land, and explore a multitude of other features.`),\n\t\tui.Paragraph(\"Experience the convenience of code sharing and rapid experimentation with [Gno Playground](https://play.gno.land).\"),\n\t}\n}\n\nfunc packageStaffPicks() ui.Element {\n\t// XXX: make it modifiable from a DAO\n\treturn ui.Element{\n\t\tui.H3(\"Explore New Packages and Realms\"),\n\t\tui.Columns{\n\t\t\t3,\n\t\t\t[]ui.Element{\n\t\t\t\t{\n\t\t\t\t\tui.H4(\"[r/gnoland](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/gnoland)\"),\n\t\t\t\t\tui.BulletList{\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/blog\"},\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/dao\"},\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/faucet\"},\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/home\"},\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/pages\"},\n\t\t\t\t\t},\n\t\t\t\t\tui.H4(\"[r/sys](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/sys)\"),\n\t\t\t\t\tui.BulletList{\n\t\t\t\t\t\tui.Link{URL: \"r/sys/names\"},\n\t\t\t\t\t\tui.Link{URL: \"r/sys/rewards\"},\n\t\t\t\t\t\tui.Link{URL: \"r/sys/validators\"},\n\t\t\t\t\t},\n\t\t\t\t}, {\n\t\t\t\t\tui.H4(\"[r/demo](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo)\"),\n\t\t\t\t\tui.BulletList{\n\t\t\t\t\t\tui.Link{URL: \"r/demo/boards\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/users\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/banktest\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/foo20\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/foo721\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/microblog\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/nft\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/types\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/art/gnoface\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/art/millipede\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/groups\"},\n\t\t\t\t\t\tui.Text(\"...\"),\n\t\t\t\t\t},\n\t\t\t\t}, {\n\t\t\t\t\tui.H4(\"[p/demo](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo)\"),\n\t\t\t\t\tui.BulletList{\n\t\t\t\t\t\tui.Link{URL: \"p/demo/avl\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/blog\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/ui\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/ufmt\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/merkle\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/bf\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/flow\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/gnode\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/grc/grc20\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/grc/grc721\"},\n\t\t\t\t\t\tui.Text(\"...\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc discoverLinks() ui.Element {\n\treturn ui.Element{\n\t\tui.Text(`\u003cdiv class=\"columns-3\"\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Learn about gno.land\n\n- [About](/about)\n- [GitHub](https://github.com/gnolang)\n- [Blog](/blog)\n- [Events](/events)\n- Tokenomics (soon)\n- [Partners, Fund, Grants](/partners)\n- [Explore the Ecosystem](/ecosystem)\n- [Careers](https://jobs.lever.co/allinbits?department=Gno.land)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### Build with Gno\n\n- [Write Gno in the browser](https://play.gno.land)\n- [Read about the Gno Language](/gnolang)\n- [Visit the official documentation](https://docs.gno.land)\n- [Gno by Example](https://gno-by-example.com/)\n- [Efficient local development for Gno](https://docs.gno.land/gno-tooling/cli/gno-tooling-gnodev)\n- [Get testnet GNOTs](https://faucet.gno.land)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Explore the universe\n\n- [Discover demo packages](https://github.com/gnolang/gno/tree/master/examples)\n- [Gnoscan](https://gnoscan.io)\n- [Portal Loop](https://docs.gno.land/concepts/portal-loop)\n- Testnet 4 (upcoming)\n- [Testnet 3](https://test3.gno.land/) (archive)\n- [Testnet 2](https://test2.gno.land/) (archive)\n- Testnet Faucet Hub (soon)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003c/div\u003e\u003c!-- end columns-3--\u003e`),\n\t}\n}\n" + }, + { + "name": "home_filetest.gno", + "body": "package main\n\nimport \"gno.land/r/gnoland/home\"\n\nfunc main() {\n\tprintln(home.Render(\"\"))\n}\n\n// Output:\n// \u003cmain class='gno-tmpl-section'\u003e\n//\n// # Welcome to gno.land\n//\n// ### We’re building gno.land, set to become the leading open-source smart contract platform, using Gno, an interpreted and fully deterministic variation of the Go programming language for succinct and composable smart contracts.\n//\n//\n// With transparent and timeless code, gno.land is the next generation of smart contract platforms, serving as the “GitHub” of the ecosystem, with realms built using fully transparent, auditable code that anyone can inspect and reuse.\n//\n//\n// Intuitive and easy to use, gno.land lowers the barrier to web3 and makes censorship-resistant platforms accessible to everyone. If you want to help lay the foundations of a fairer and freer world, join us today.\n//\n// \u003cdiv class=\"jumbotron\"\u003e\n//\n// \u003cdiv class=\"columns-3\"\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Learn about gno.land\n//\n// - [About](/about)\n// - [GitHub](https://github.com/gnolang)\n// - [Blog](/blog)\n// - [Events](/events)\n// - Tokenomics (soon)\n// - [Partners, Fund, Grants](/partners)\n// - [Explore the Ecosystem](/ecosystem)\n// - [Careers](https://jobs.lever.co/allinbits?department=Gno.land)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Build with Gno\n//\n// - [Write Gno in the browser](https://play.gno.land)\n// - [Read about the Gno Language](/gnolang)\n// - [Visit the official documentation](https://docs.gno.land)\n// - [Gno by Example](https://gno-by-example.com/)\n// - [Efficient local development for Gno](https://docs.gno.land/gno-tooling/cli/gno-tooling-gnodev)\n// - [Get testnet GNOTs](https://faucet.gno.land)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Explore the universe\n//\n// - [Discover demo packages](https://github.com/gnolang/gno/tree/master/examples)\n// - [Gnoscan](https://gnoscan.io)\n// - [Portal Loop](https://docs.gno.land/concepts/portal-loop)\n// - Testnet 4 (upcoming)\n// - [Testnet 3](https://test3.gno.land/) (archive)\n// - [Testnet 2](https://test2.gno.land/) (archive)\n// - Testnet Faucet Hub (soon)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003c/div\u003e\u003c!-- end columns-3--\u003e\n// \u003c/div\u003e\u003c!-- /jumbotron --\u003e\n//\n// \u003cdiv class=\"columns-3\"\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Latest Blogposts\n//\n// No posts.\n// \u003c/div\u003e\u003c!-- /column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Upcoming Events\n//\n// [View upcoming events](/events)\n// \u003c/div\u003e\u003c!-- /column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Latest Contributions\n//\n// [View latest contributions](https://github.com/gnolang/gno/pulls)\n// \u003c/div\u003e\u003c!-- /column--\u003e\n// \u003c/div\u003e\u003c!-- /columns-3 --\u003e\n//\n//\n// ---\n//\n// ### [Gno Playground](https://play.gno.land)\n//\n//\n// Gno Playground is a web application designed for building, running, testing, and interacting\n// with your Gno code, enhancing your understanding of the Gno language. With Gno Playground, you can share your code,\n// execute tests, deploy your realms and packages to gno.land, and explore a multitude of other features.\n//\n//\n// Experience the convenience of code sharing and rapid experimentation with [Gno Playground](https://play.gno.land).\n//\n//\n// ---\n//\n// ### Explore New Packages and Realms\n//\n// \u003cdiv class=\"columns-3\"\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// #### [r/gnoland](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/gnoland)\n//\n// - [r/gnoland/blog](r/gnoland/blog)\n// - [r/gnoland/dao](r/gnoland/dao)\n// - [r/gnoland/faucet](r/gnoland/faucet)\n// - [r/gnoland/home](r/gnoland/home)\n// - [r/gnoland/pages](r/gnoland/pages)\n//\n// #### [r/sys](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/sys)\n//\n// - [r/sys/names](r/sys/names)\n// - [r/sys/rewards](r/sys/rewards)\n// - [r/sys/validators](r/sys/validators)\n//\n// \u003c/div\u003e\u003c!-- /column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// #### [r/demo](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo)\n//\n// - [r/demo/boards](r/demo/boards)\n// - [r/demo/users](r/demo/users)\n// - [r/demo/banktest](r/demo/banktest)\n// - [r/demo/foo20](r/demo/foo20)\n// - [r/demo/foo721](r/demo/foo721)\n// - [r/demo/microblog](r/demo/microblog)\n// - [r/demo/nft](r/demo/nft)\n// - [r/demo/types](r/demo/types)\n// - [r/demo/art/gnoface](r/demo/art/gnoface)\n// - [r/demo/art/millipede](r/demo/art/millipede)\n// - [r/demo/groups](r/demo/groups)\n// - ...\n//\n// \u003c/div\u003e\u003c!-- /column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// #### [p/demo](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo)\n//\n// - [p/demo/avl](p/demo/avl)\n// - [p/demo/blog](p/demo/blog)\n// - [p/demo/ui](p/demo/ui)\n// - [p/demo/ufmt](p/demo/ufmt)\n// - [p/demo/merkle](p/demo/merkle)\n// - [p/demo/bf](p/demo/bf)\n// - [p/demo/flow](p/demo/flow)\n// - [p/demo/gnode](p/demo/gnode)\n// - [p/demo/grc/grc20](p/demo/grc/grc20)\n// - [p/demo/grc/grc721](p/demo/grc/grc721)\n// - ...\n//\n// \u003c/div\u003e\u003c!-- /column--\u003e\n// \u003c/div\u003e\u003c!-- /columns-3 --\u003e\n//\n//\n// ---\n//\n// ### Contributions (WorxDAO \u0026 GoR)\n//\n// coming soon\n//\n// ---\n//\n//\n// \u003cdiv class=\"columns-2\"\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Socials\n//\n// - Check out our [community projects](https://github.com/gnolang/awesome-gno)\n// - ![Discord](static/img/ico-discord.svg) [Discord](https://discord.gg/S8nKUqwkPn)\n// - ![Twitter](static/img/ico-twitter.svg) [Twitter](https://twitter.com/_gnoland)\n// - ![Youtube](static/img/ico-youtube.svg) [Youtube](https://www.youtube.com/@_gnoland)\n// - ![Telegram](static/img/ico-telegram.svg) [Telegram](https://t.me/gnoland)\n//\n// \u003c/div\u003e\u003c!-- /column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Quote of the ~Day~ Block#123\n//\n// \u003e Now, you Gno.\n//\n// \u003c/div\u003e\u003c!-- /column--\u003e\n// \u003c/div\u003e\u003c!-- /columns-2 --\u003e\n//\n//\n// ---\n//\n// **This is a testnet.**\n// Package names are not guaranteed to be available for production.\n//\n// \u003c/main\u003e\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "monit", + "path": "gno.land/r/gnoland/monit", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/gnoland/monit\n\nrequire (\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/watchdog v0.0.0-latest\n)\n" + }, + { + "name": "monit.gno", + "body": "// Package monit links a monitoring system with the chain in both directions.\n//\n// The agent will periodically call Incr() and verify that the value is always\n// higher than the previously known one. The contract will store the last update\n// time and use it to detect whether or not the monitoring agent is functioning\n// correctly.\npackage monit\n\nimport (\n\t\"std\"\n\t\"time\"\n\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/demo/watchdog\"\n)\n\nvar (\n\tcounter int\n\tlastUpdate time.Time\n\tlastCaller std.Address\n\twd = watchdog.Watchdog{Duration: 5 * time.Minute}\n\towner = ownable.New() // TODO: replace with -\u003e ownable.NewWithAddress...\n\twatchdogDuration = 5 * time.Minute\n)\n\n// Incr increments the counter and informs the watchdog that we're alive.\n// This function can be called by anyone.\nfunc Incr() int {\n\tcounter++\n\tlastUpdate = time.Now()\n\tlastCaller = std.PrevRealm().Addr()\n\twd.Alive()\n\treturn counter\n}\n\n// Reset resets the realm state.\n// This function can only be called by the admin.\nfunc Reset() {\n\tif owner.CallerIsOwner() != nil { // TODO: replace with owner.AssertCallerIsOwner\n\t\tpanic(\"unauthorized\")\n\t}\n\tcounter = 0\n\tlastCaller = std.PrevRealm().Addr()\n\tlastUpdate = time.Now()\n\twd = watchdog.Watchdog{Duration: 5 * time.Minute}\n}\n\nfunc Render(_ string) string {\n\tstatus := wd.Status()\n\treturn ufmt.Sprintf(\n\t\t\"counter=%d\\nlast update=%s\\nlast caller=%s\\nstatus=%s\",\n\t\tcounter, lastUpdate, lastCaller, status,\n\t)\n}\n\n// TransferOwnership transfers ownership to a new owner. This is a proxy to\n// ownable.Ownable.TransferOwnership.\nfunc TransferOwnership(newOwner std.Address) { owner.TransferOwnership(newOwner) }\n" + }, + { + "name": "monit_test.gno", + "body": "package monit\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/uassert\"\n)\n\nfunc TestPackage(t *testing.T) {\n\t// initial state, watchdog is KO.\n\t{\n\t\texpected := `counter=0\nlast update=0001-01-01 00:00:00 +0000 UTC\nlast caller=\nstatus=KO`\n\t\tgot := Render(\"\")\n\t\tuassert.Equal(t, expected, got)\n\t}\n\n\t// call Incr(), watchdog is OK.\n\tIncr()\n\tIncr()\n\tIncr()\n\t{\n\t\texpected := `counter=3\nlast update=2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001\nlast caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\nstatus=OK`\n\t\tgot := Render(\"\")\n\t\tuassert.Equal(t, expected, got)\n\t}\n\n\t/* XXX: improve tests once we've the missing std.TestSkipTime feature\n\t\t// wait 1h, watchdog is KO.\n\t\tuse std.TestSkipTime(time.Hour)\n\t\t{\n\t\t\texpected := `counter=3\n\tlast update=2009-02-13 22:31:30 +0000 UTC m=+1234564290.000000001\n\tlast caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n\tstatus=KO`\n\t\t\tgot := Render(\"\")\n\t\t\tuassert.Equal(t, expected, got)\n\t\t}\n\n\t\t// call Incr(), watchdog is OK.\n\t\tIncr()\n\t\t{\n\t\t\texpected := `counter=4\n\tlast update=2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001\n\tlast caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n\tstatus=OK`\n\t\t\tgot := Render(\"\")\n\t\t\tuassert.Equal(t, expected, got)\n\t\t}\n\t*/\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "gnopages", + "path": "gno.land/r/gnoland/pages", + "files": [ + { + "name": "admin.gno", + "body": "package gnopages\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\nvar (\n\tadminAddr std.Address\n\tmoderatorList avl.Tree\n\tinPause bool\n)\n\nfunc init() {\n\t// adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis.\n\tadminAddr = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\"\n}\n\nfunc AdminSetAdminAddr(addr std.Address) {\n\tassertIsAdmin()\n\tadminAddr = addr\n}\n\nfunc AdminSetInPause(state bool) {\n\tassertIsAdmin()\n\tinPause = state\n}\n\nfunc AdminAddModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), true)\n}\n\nfunc AdminRemoveModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), false) // XXX: delete instead?\n}\n\nfunc ModAddPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\tcaller := std.GetOrigCaller()\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.NewPost(caller, slug, title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc ModEditPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.GetPost(slug).Update(title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc isAdmin(addr std.Address) bool {\n\treturn addr == adminAddr\n}\n\nfunc isModerator(addr std.Address) bool {\n\t_, found := moderatorList.Get(addr.String())\n\treturn found\n}\n\nfunc assertIsAdmin() {\n\tcaller := std.GetOrigCaller()\n\tif !isAdmin(caller) {\n\t\tpanic(\"access restricted.\")\n\t}\n}\n\nfunc assertIsModerator() {\n\tcaller := std.GetOrigCaller()\n\tif isAdmin(caller) || isModerator(caller) {\n\t\treturn\n\t}\n\tpanic(\"access restricted\")\n}\n\nfunc assertNotInPause() {\n\tif inPause {\n\t\tpanic(\"access restricted (pause)\")\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/gnoland/pages\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/blog v0.0.0-latest\n)\n" + }, + { + "name": "page_about.gno", + "body": "package gnopages\n\nfunc init() {\n\tpath := \"about\"\n\ttitle := \"Gno.land Is A Platform To Write Smart Contracts In Gno\"\n\t// XXX: description := \"On gno.land, developers write smart contracts and other blockchain apps using Gno without learning a language that’s exclusive to a single ecosystem.\"\n\tbody := `\nGno.land is a next-generation smart contract platform using Gno, an interpreted version of the general-purpose Go\nprogramming language. On gno.land, smart contracts can be uploaded on-chain only by publishing their full source code,\nmaking it trivial to verify the contract or fork it into an improved version. With a system to publish reusable code \nlibraries on-chain, gno.land serves as the “GitHub” of the ecosystem, with realms built using fully transparent, \nauditable code that anyone can inspect and reuse.\n\nGno.land addresses many pressing issues in the blockchain space, starting with the ease of use and intuitiveness of\nsmart contract platforms. Developers can write smart contracts without having to learn a new language that’s exclusive \nto a single ecosystem or limited by design. Go developers can easily port their existing web apps to gno.land or build\nnew ones from scratch, making web3 vastly more accessible.\n\nSecured by Proof of Contribution (PoC), a DAO-managed Proof-of-Authority consensus mechanism, gno.land prioritizes \nfairness and merit, rewarding the people most active on the platform. PoC restructures the financial incentives that \noften corrupt blockchain projects, opting instead to reward contributors for their work based on expertise, commitment, and \nalignment. \n\nOne of our inspirations for gno.land is the gospels, which built a system of moral code that lasted thousands of years.\nBy observing a minimal production implementation, gno.land’s design will endure over time and serve as a reference for \nfuture generations with censorship-resistant tools that improve their understanding of the world. \n`\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:22Z\", nil, nil)\n}\n" + }, + { + "name": "page_ecosystem.gno", + "body": "package gnopages\n\nfunc init() {\n\tvar (\n\t\tpath = \"ecosystem\"\n\t\ttitle = \"Discover Gno.land Ecosystem Projects \u0026 Initiatives\"\n\t\t// XXX: description = \"Dive further into the gno.land ecosystem and discover the core infrastructure, projects, smart contracts, and tooling we’re building.\"\n\t\tbody = `\n### [Gno Playground](https://play.gno.land)\n\nGno Playground is a simple web interface that lets you write, test, and experiment with your Gno code to improve your \nunderstanding of the Gno language. You can share your code, run unit tests, deploy your realms and packages, and execute\nfunctions in your code using the repo. \n\nVisit the playground at [play.gno.land](https://play.gno.land)!\n\n### [Gnoscan](https://gnoscan.io)\n\nDeveloped by the Onbloc team, Gnoscan is gno.land’s blockchain explorer. Anyone can use Gnoscan to easily find\ninformation that resides on the gno.land blockchain, such as wallet addresses, TX hashes, blocks, and contracts. \nGnoscan makes our on-chain data easy to read and intuitive to discover.\n\nExplore the gno.land blockchain at [gnoscan.io](https://gnoscan.io)!\n\n### Adena\n\nAdena is a user-friendly non-custodial wallet for gno.land. Open-source and developed by Onbloc, Adena allows gnomes to \ninteract easily with the chain. With an emphasis on UX, Adena is built to handle millions of realms and tokens with a\nhigh-quality interface, support for NFTs and custom tokens, and seamless integration.\n\n### Gnoswap\n\nGnoswap is currently under development and led by the Onbloc team. Gnoswap will be the first DEX on gno.land and is an \nautomated market maker (AMM) protocol written in Gno that allows for permissionless token exchanges on the platform.\n\n### Flippando\n\nFlippando is a simple on-chain memory game, ported from Solidity to Gno, which starts with an empty matrix to flip tiles\non to see what’s underneath. If the tiles match, they remain uncovered; if not, they are briefly shown, and the player \nmust memorize their colors until the entire matrix is uncovered. The end result can be minted as an NFT, which can later\nbe assembled into bigger, more complex NFTs, creating a digital “painting” with the uncovered tiles.\n`\n\t)\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:23Z\", nil, nil)\n}\n" + }, + { + "name": "page_gnolang.gno", + "body": "package gnopages\n\nfunc init() {\n\tvar (\n\t\tpath = \"gnolang\"\n\t\ttitle = \"About the Gno, the Language for Gno.land\"\n\t\t// TODO fix broken images\n\t\tbody = `\n\n[Gno](https://github.com/gnolang/gno) is an interpretation of the widely-used Go (Golang) programming language for blockchain created by Cosmos co-founder Jae Kwon in 2022 to mark a new era in smart contracting. Gno is ~99% identical to Go, so Go programmers can start coding in Gno right away, with a minimal learning curve. For example, Gno comes with blockchain-specific standard libraries, but any code that doesn’t use blockchain-specific logic can run in Go with minimal processing. Libraries that don’t make sense in the blockchain context, such as network or operating-system access, are not available in Gno. Otherwise, Gno loads and uses many standard libraries that power Go, so most of the parsing of the source code is the same.\n\nUnder the hood, the Gno code is parsed into an abstract syntax tree (AST) and the AST itself is used in the interpreter, rather than bytecode as in many virtual machines such as Java, Python, or Wasm. This makes even the GnoVM accessible to any Go programmer. The novel design of the intuitive GnoVM interpreter allows Gno to freeze and resume the program by persisting and loading the entire memory state. Gno is deterministic, auto-persisted, and auto-Merkle-ized, allowing (smart contract) programs to be succinct, as the programmer doesn’t have to serialize and deserialize objects to persist them into a database (unlike programming applications with the Cosmos SDK).\n\n## How Gno Differs from Go\n\n![Gno and Go differences](static/img/gno-language/go-and-gno.jpg)\n\nThe composable nature of Go/Gno allows for type-checked interactions between contracts, making gno.land safer and more powerful, as well as operationally cheaper and faster. Smart contracts on gno.land are light, simple, more focused, and easily interoperable—a network of interconnected contracts rather than siloed monoliths that limit interactions with other contracts.\n\n![Example of Gno code](static/img/gno-language/code-example.jpg)\n\n## Gno Inherits Go’s Built-in Security Features\n\nGo supports secure programming through exported/non-exported fields, enabling a “least-authority” design. It is easy to create objects and APIs that expose only what should be accessible to callers while hiding what should not be simply by the capitalization of letters, thus allowing a succinct representation of secure logic that can be called by multiple users.\n\nAnother major advantage of Go is that the language comes with an ecosystem of great tooling, like the compiler and third-party tools that statically analyze code. Gno inherits these advantages from Go directly to create a smart contract programming language that provides embedding, composability, type-check safety, and garbage collection, helping developers to write secure code relying on the compiler, parser, and interpreter to give warning alerts for common mistakes.\n\n## Gno vs Solidity\n\nThe most widely-adopted smart contract language today is Ethereum’s EVM-compatible Solidity. With bytecode built from the ground up and Turing complete, Solidity opened up a world of possibilities for decentralized applications (dApps) and there are currently more than 10 million contracts deployed on Ethereum. However, Solidity provides limited tooling and its EVM has a stack limit and computational inefficiencies.\n\nSolidity is designed for one purpose only (writing smart contracts) and is bound by the limitations of the EVM. In addition, developers have to learn several languages if they want to understand the whole stack or work across different ecosystems. Gno aspires to exceed Solidity on multiple fronts (and other smart contract languages like CosmWasm or Substrate) as every part of the stack is written in Gno. It’s easy for developers to understand the entire system just by studying a relatively small code base.\n\n## Gno Is Essential for the Wider Adoption of Web3\n\nGno makes imports as easy as they are in web2 with runtime-based imports for seamless dependency flow comprehension, and support for complex structs, beyond primitive types. Gno is ultimately cost-effective as dependencies are loaded once, enabling remote function calls as local, and providing automatic and independent per-realm state persistence.\n\nUsing Gno, developers can rapidly accelerate application development and adopt a modular structure by reusing and reassembling existing modules without building from scratch. They can embed one structure inside another in an intuitive way while preserving localism, and the language specification is simple, successfully balancing practicality and minimalism.\n\nThe Go language is so well designed that the Gno smart contract system will become the new gold standard for smart contract development and other blockchain applications. As a programming language that is universally adopted, secure, composable, and complete, Gno is essential for the broader adoption of web3 and its sustainable growth.`\n\t)\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:25Z\", nil, nil)\n}\n" + }, + { + "name": "page_gor.gno", + "body": "package gnopages\n\nfunc init() {\n\tpath := \"gor\"\n\ttitle := \"Game of Realms - A Contest For The Best Contributors\"\n\t// XXX: description := \"Game of Realms is the first high-stakes competition held in two phases to find the best contributors to the gno.land platform with a 133,700 ATOM prize pool.\"\n\tbody := `\n\n\u003cdiv class=\"jumbotron\"\u003e\n\n### Game of Realms\n\nThe first high-stakes contest will see participants compete for tiered membership to co-own the gno.land blockchain. A series of complex technical and non-technical tasks will challenge contributors to create innovative patterns that push the chain to new limits. Start building the foundation for tomorrow through key smart contracts and other contributions that change our understanding of the world.\n\n\u003c/div\u003e\u003c!-- end jumbotron--\u003e\n\nThe competition is currently in phase one – for advanced developers only.\n\nOnce the necessary tools to start phase two are ready, we’ll open up the competition to newer devs and non-technical contributors.\n\nIf you want to stack ATOM rewards and play a key role in the success of gno.land and web3, read more about Game of Realms or open a [PR](https://github.com/gnolang/gno/) today.\n\n\u003cdiv\u003e\n\n\u003cdiv role=\"tablist\" aria-labelledby=\"tablist-1\" class=\"tabs\"\u003e\n\u003cdiv class=\"columns-2\"\u003e\n\u003cdiv\u003e\n\n## Phase I. (ongoing)\n\n- \u003cbutton id=\"tab-1\" type=\"button\" role=\"tab\" aria-selected=\"true\" aria-controls=\"tabpanel-1\"\u003eEvaluation DAO (30%)\u003c/button\u003e\n\n- \u003cbutton id=\"tab-2\" type=\"button\" role=\"tab\" aria-selected=\"false\" aria-controls=\"tabpanel-2\" tabindex=\"-1\"\u003eTutorials (80%)\u003c/button\u003e\n\n- \u003cbutton id=\"tab-3\" type=\"button\" role=\"tab\" aria-selected=\"false\" aria-controls=\"tabpanel-3\" tabindex=\"-1\"\u003eGovernance Module (25%)\u003c/button\u003e\n\n\u003c/div\u003e\n\u003cdiv\u003e\n\n## Phase II. (Locked)\n\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n\u003cdiv class=\"jumbotron\"\u003e\n\n\u003cdiv id=\"tabpanel-1\" role=\"tabpanel\" tabindex=\"0\" aria-labelledby=\"tab-1\" class=\"\"\u003e\n\n## Evaluation DAO\n\nThis complex challenge seeks your skills in DAO development and implementation and is one of the most important challenges of phase one. The Evaluation DAO will ensure that contributions in Game of Realms and the gno.land platform are fairly rewarded.\n\n\u003cdiv class=\"accordion gor-accordion\"\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger is-muted\" aria-controls=\"acc-1\" id=\"accpanel-1\"\u003e \u0026#9745; \u003cspan class=\"is-finished\"\u003eClarifying this issue\u003c/span\u003e — [100%\u0026nbsp;completed] \u003c/button\u003e\n\n\u003cdiv id=\"acc-1\" role=\"region\" aria-labelledby=\"accpanel-1\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"true\" class=\"accordion-trigger\" aria-controls=\"acc-2\" id=\"accpanel-2\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eRetrospectives \u0026 investigations\u003c/span\u003e — [20%\u0026nbsp;In progress]\u003c/button\u003e\n\n\u003cdiv id=\"acc-2\" role=\"region\" aria-labelledby=\"accpanel-2\" class=\"accordion-panel\"\u003e\n\nGame of Realms participants and core contributors are still in discussions, proposing additional ideas, and seeing how the proposal for the Evaluation DAO evolves over time.\n\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"true\" class=\"accordion-trigger\" aria-controls=\"acc-3\" id=\"accpanel-3\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eHuman specs — definitions, rules, examples\u003c/span\u003e — [20%\u0026nbsp;In progress]\u003c/button\u003e\n\n\u003cdiv id=\"acc-3\" role=\"region\" aria-labelledby=\"accpanel-3\" class=\"accordion-panel\"\u003e\n\nSee [GitHub issue 519](https://github.com/gnolang/gno/issues/519) for the most up-to-date discussion so far on how voting should work for the DAO, what the responsibilities are, how to join, etc.\n\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-4\" id=\"accpanel-4\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eTechnical specs and interfaces\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-4\" role=\"region\" aria-labelledby=\"accpanel-4\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-5\" id=\"accpanel-5\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eImplementation\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-5\" role=\"region\" aria-labelledby=\"accpanel-5\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-6\" id=\"accpanel-6\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eDocumentation\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-6\" role=\"region\" aria-labelledby=\"accpanel-6\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-7\" id=\"accpanel-7\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eBootstrapping plan\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-7\" role=\"region\" aria-labelledby=\"accpanel-7\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003c/div\u003e\n\u003c/div\u003e\n\n\u003cdiv id=\"tabpanel-2\" role=\"tabpanel\" tabindex=\"0\" aria-labelledby=\"tab-2\" class=\"\"\u003e\n\n## Tutorials\n\nTo progress to phase two of the competition, we need high-quality tutorials, guides, and documentation from phase one participants. Help to create materials that will onboard more contributors to gno.land.\n\n\u003cdiv class=\"accordion gor-accordion\"\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger is-muted\" aria-controls=\"acc-8\" id=\"accpanel-8\"\u003e \u0026#9745; \u003cspan class=\"is-finished\"\u003eClarifying this issue\u003c/span\u003e — [100%\u0026nbsp;completed] \u003c/button\u003e\n\n\u003cdiv id=\"acc-8\" role=\"region\" aria-labelledby=\"accpanel-8\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger is-muted\" aria-controls=\"acc-9\" id=\"accpanel-9\"\u003e \u0026#9745; \u003cspan class=\"is-finished\"\u003eRetrospectives \u0026 investigations\u003c/span\u003e — [100%\u0026nbsp;completed]\u003c/button\u003e\n\n\u003cdiv id=\"acc-9\" role=\"region\" aria-labelledby=\"accpanel-9\" class=\"accordion-panel is-muted is-hidden\"\u003e\n\nHow to create, present, and house the tutorials has been established with productive discussions between core contributors and external participants.\n\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger is-muted\" aria-controls=\"acc-10\" id=\"accpanel-10\"\u003e \u0026#9745; \u003cspan class=\"is-finished\"\u003eHuman specs — definitions, rules, examples\u003c/span\u003e — [100%\u0026nbsp;completed]\u003c/button\u003e\n\n\u003cdiv id=\"acc-10\" role=\"region\" aria-labelledby=\"accpanel-10\" class=\"accordion-panel is-muted is-hidden\"\u003e\n\nWe followed a collaborative approach to defining the scope of the work and creating a series of tutorials and videos, Gno by Example, to explain core concepts and show newcomers how to write in the Gno programming language. Gno docs and tutorials will be community-run so that anyone can contribute. Onbloc’s [developer portal](https://docs.onbloc.xyz/) is an excellent onramp to gno.land currently. We will soon be releasing a documentation instance to house all tutorials.\n\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger is-muted\" aria-controls=\"acc-11\" id=\"accpanel-11\"\u003e \u0026#9745; \u003cspan class=\"is-finished\"\u003eTechnical specs and interfaces\u003c/span\u003e — [100%\u0026nbsp;completed]\u003c/button\u003e\n\n\u003cdiv id=\"acc-11\" role=\"region\" aria-labelledby=\"accpanel-11\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-12\" id=\"accpanel-12\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eImplementation\u003c/span\u003e — [80%\u0026nbsp;In progress]\u003c/button\u003e\n\n\u003cdiv id=\"acc-12\" role=\"region\" aria-labelledby=\"accpanel-12\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-13\" id=\"accpanel-13\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eBootstrapping plan\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-13\" role=\"region\" aria-labelledby=\"accpanel-13\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003c/div\u003e\n\u003c/div\u003e\n\n\u003cdiv id=\"tabpanel-3\" role=\"tabpanel\" tabindex=\"0\" aria-labelledby=\"tab-3\" class=\"\"\u003e\n\n## Governance Module\n\nCan you define and implement a governance contract suite that rivals existing ones, such as the Cosmos Hub? Show us how! We’re looking for the fairest and most efficient governance solution possible.\n\n\u003cdiv class=\"accordion gor-accordion\"\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger is-muted\" aria-controls=\"acc-14\" id=\"accpanel-14\"\u003e \u0026#9745; \u003cspan class=\"is-finished\"\u003eClarifying this issue\u003c/span\u003e — [100%\u0026nbsp;completed] \u003c/button\u003e\n\n\u003cdiv id=\"acc-14\" role=\"region\" aria-labelledby=\"accpanel-14\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"true\" class=\"accordion-trigger\" aria-controls=\"acc-15\" id=\"accpanel-15\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eRetrospectives \u0026 investigations\u003c/span\u003e — [60%\u0026nbsp;In progress]\u003c/button\u003e\n\n\u003cdiv id=\"acc-15\" role=\"region\" aria-labelledby=\"accpanel-15\" class=\"accordion-panel\"\u003e\n\nGame of Realms participants and core contributors have made significant progress teaming up to complete this challenge but discussions and additional ideas are still ongoing.\n\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-16\" id=\"accpanel-16\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eHuman specs — definitions, rules, examples\u003c/span\u003e — [20%\u0026nbsp;In progress]\u003c/button\u003e\n\n\u003cdiv id=\"acc-16\" role=\"region\" aria-labelledby=\"accpanel-16\" class=\"accordion-panel is-muted is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-17\" id=\"accpanel-17\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eTechnical specs and interfaces\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-17\" role=\"region\" aria-labelledby=\"accpanel-17\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-18\" id=\"accpanel-18\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eImplementation\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-18\" role=\"region\" aria-labelledby=\"accpanel-18\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-19\" id=\"accpanel-19\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eDocumentation\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-19\" role=\"region\" aria-labelledby=\"accpanel-19\" class=\"accordion-panel is-hidden\"\u003e\u003c/div\u003e\n\n\u003cbutton type=\"button\" aria-expanded=\"false\" class=\"accordion-trigger\" aria-controls=\"acc-20\" id=\"accpanel-20\"\u003e \u0026#9744; \u003cspan class=\"is-underline\"\u003eBootstrapping plan\u003c/span\u003e — [0%\u0026nbsp;Stand-by]\u003c/button\u003e\n\n\u003cdiv id=\"acc-20\" role=\"region\" aria-labelledby=\"accpanel-20\" class=\"accordion-panel is-hidden\"\u003e\n\u003c/div\u003e\n\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n## Register Now\n\n\u003c!-- mailchimp --\u003e\n\u003cdiv id=\"mc_embed_signup\"\u003e\n\u003cform action=\"https://land.us18.list-manage.com/subscribe/post?u=8befe3303cf82796d2c1a1aff\u0026amp;id=5499ca154b\u0026amp;f_id=008d70e7f0\" method=\"post\" id=\"mc-embedded-subscribe-form\" name=\"mc-embedded-subscribe-form\" class=\"validate\" target=\"_self\"\u003e\n \u003clabel for=\"mce-EMAIL\"\u003eLeave your email to be informed about Game of Realms\u003c/label\u003e\n \u003cdiv id=\"mc_embed_signup_scroll\"\u003e\n \t\u003cdiv class=\"mc-field-group\"\u003e\n \t\t\u003cinput type=\"email\" value=\"\" name=\"EMAIL\" class=\"required email\" id=\"mce-EMAIL\" placeholder=\"Type your email here\" required\u003e\n \t\t\u003cinput type=\"submit\" value=\"Subscribe\" name=\"subscribe\" id=\"mc-embedded-subscribe\" class=\"button\"\u003e\n \t\u003c/div\u003e\n \t\u003cdiv hidden=\"true\"\u003e\u003cinput type=\"hidden\" name=\"tags\" value=\"2525514\"\u003e\u003c/div\u003e\n \t\u003cdiv id=\"mce-responses\" class=\"clear\"\u003e\n \t\t\u003cdiv class=\"response\" id=\"mce-error-response\" style=\"display:none\"\u003e\u003c/div\u003e\n \t\t\u003cdiv class=\"response\" id=\"mce-success-response\" style=\"display:none\"\u003e\u003c/div\u003e\n \t\u003c/div\u003e\n \t\u003c!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups--\u003e\n \t\u003cdiv style=\"position: absolute; left: -5000px;\" aria-hidden=\"true\"\u003e\u003cinput type=\"text\" name=\"b_8befe3303cf82796d2c1a1aff_5499ca154b\" tabindex=\"-1\" value=\"\"\u003e\u003c/div\u003e\n \u003c/div\u003e\n\u003c/form\u003e\n\u003c/div\u003e\n\u003c!-- /mailchimp --\u003e\n`\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:26Z\", nil, nil)\n}\n" + }, + { + "name": "page_license.gno", + "body": "package gnopages\n\nfunc init() {\n\tvar (\n\t\tpath = \"license\"\n\t\ttitle = \"Gno Network General Public License\"\n\t\tbody = `Copyright (C) 2024 NewTendermint, LLC\n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNO Network General Public License as published by\nNewTendermint, LLC, either version 4 of the License, or (at your option) any\nlater version published by NewTendermint, LLC.\n\nThis program is distributed in the hope that it will be useful, but is provided\nas-is and WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNO Network\nGeneral Public License for more details.\n\nYou should have received a copy of the GNO Network General Public License along\nwith this program. If not, see \u003chttps://gno.land/license\u003e.\n\nAttached below are the terms of the GNO Network General Public License, Version\n4 (a fork of the GNU Affero General Public License 3).\n\n## Additional Terms\n\n### Strong Attribution\n\nIf any of your user interfaces, such as websites and mobile applications, serve\nas the primary point of entry to a platform or blockchain that 1) offers users\nthe ability to upload their own smart contracts to the platform or blockchain,\nand 2) leverages any Covered Work (including the GNO virtual machine) to run\nthose smart contracts on the platform or blockchain (\"Applicable Work\"), then\nthe Applicable Work must prominently link to (1) gno.land or (2) any other URL\ndesignated by NewTendermint, LLC that has not been rejected by the governance of\nthe first chain known as gno.land, provided that the identity of the first chain\nis not ambiguous. In the event the identity of the first chain is ambiguous,\nthen NewTendermint, LLC's designation shall control. Such link must appear\nconspicuously in the header or footer of the Applicable Work, such that all\nusers may learn of gno.land or the URL designated by NewTendermint, LLC.\n\nThis additional attribution requirement shall remain in effect for (1) 7\nyears from the date of publication of the Applicable Work, or (2) 7 years from\nthe date of publication of the Covered Work (including republication of new\nversions), whichever is later, but no later than 12 years after the application\nof this strong attribution requirement to the publication of the Applicable\nWork. For purposes of this Strong Attribution requirement, Covered Work shall\nmean any work that is licensed under the GNO Network General Public License,\nVersion 4 or later, by NewTendermint, LLC.\n\n\n# GNO NETWORK GENERAL PUBLIC LICENSE\n\nVersion 4, 7 May 2024\n\nModified from the GNU AFFERO GENERAL PUBLIC LICENSE.\nGNU is not affiliated with GNO or NewTendermint, LLC.\nCopyright (C) 2022 NewTendermint, LLC.\n\n## Preamble\n\nThe GNO Network General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\nThe licenses for most software and other practical works are designed\nto take away your freedom to share and change the works. By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\nWhen we speak of free software, we are referring to freedom, not\nprice. Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\nDevelopers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\nA secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate. Many developers of free software are heartened and\nencouraged by the resulting cooperation. However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\nThe GNO Network General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community. It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server. Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\nThe precise terms and conditions for copying, distribution and\nmodification follow.\n\n## TERMS AND CONDITIONS\n\n### 0. Definitions.\n\n\"This License\" refers to version 4 of the GNO Network General Public License.\n\n\"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n\"The Program\" refers to any copyrightable work licensed under this\nLicense. Each licensee is addressed as \"you\". \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\nTo \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy. The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\nA \"covered work\" means either the unmodified Program or a work based\non the Program.\n\nTo \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy. Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\nTo \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies. Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\nAn interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License. If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n### 1. Source Code.\n\nThe \"source code\" for a work means the preferred form of the work\nfor making modifications to it. \"Object code\" means any non-source\nform of a work.\n\nA \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\nThe \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form. A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\nThe \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities. However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work. For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\nThe Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\nThe Corresponding Source for a work in source code form is that\nsame work.\n\n### 2. Basic Permissions.\n\nAll rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met. This License explicitly affirms your unlimited\npermission to run the unmodified Program. The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work. This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\nYou may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force. You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright. Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\nConveying under any other circumstances is permitted solely under\nthe conditions stated below. Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\nNo covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\nWhen you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n### 4. Conveying Verbatim Copies.\n\nYou may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\nYou may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n### 5. Conveying Modified Source Versions.\n\nYou may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n- a) The work must carry prominent notices stating that you modified\n it, and giving a relevant date.\n- b) The work must carry prominent notices stating that it is\n released under this License and any conditions added under section\n 7. This requirement modifies the requirement in section 4 to\n \"keep intact all notices\".\n- c) You must license the entire work, as a whole, under this\n License to anyone who comes into possession of a copy. This\n License will therefore apply, along with any applicable section 7\n additional terms, to the whole of the work, and all its parts,\n regardless of how they are packaged. This License gives no\n permission to license the work in any other way, but it does not\n invalidate such permission if you have separately received it.\n- d) If the work has interactive user interfaces, each must display\n Appropriate Legal Notices; however, if the Program has interactive\n interfaces that do not display Appropriate Legal Notices, your\n work need not make them do so.\n\nA compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit. Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n### 6. Conveying Non-Source Forms.\n\n You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n- a) Convey the object code in, or embodied in, a physical product\n (including a physical distribution medium), accompanied by the\n Corresponding Source fixed on a durable physical medium\n customarily used for software interchange.\n- b) Convey the object code in, or embodied in, a physical product\n (including a physical distribution medium), accompanied by a\n written offer, valid for at least three years and valid for as\n long as you offer spare parts or customer support for that product\n model, to give anyone who possesses the object code either (1) a\n copy of the Corresponding Source for all the software in the\n product that is covered by this License, on a durable physical\n medium customarily used for software interchange, for a price no\n more than your reasonable cost of physically performing this\n conveying of source, or (2) access to copy the\n Corresponding Source from a network server at no charge.\n- c) Convey individual copies of the object code with a copy of the\n written offer to provide the Corresponding Source. This\n alternative is allowed only occasionally and noncommercially, and\n only if you received the object code with such an offer, in accord\n with subsection 6b.\n- d) Convey the object code by offering access from a designated\n place (gratis or for a charge), and offer equivalent access to the\n Corresponding Source in the same way through the same place at no\n further charge. You need not require recipients to copy the\n Corresponding Source along with the object code. If the place to\n copy the object code is a network server, the Corresponding Source\n may be on a different server (operated by you or a third party)\n that supports equivalent copying facilities, provided you maintain\n clear directions next to the object code saying where to find the\n Corresponding Source. Regardless of what server hosts the\n Corresponding Source, you remain obligated to ensure that it is\n available for as long as needed to satisfy these requirements.\n- e) Convey the object code using peer-to-peer transmission, provided\n you inform other peers where the object code and Corresponding\n Source of the work are being offered to the general public at no\n charge under subsection 6d.\n\nA separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\nA \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling. In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage. For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product. A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n\"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source. The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\nIf you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information. But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\nThe requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed. Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\nCorresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n### 7. Additional Terms.\n\n\"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law. If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\nWhen you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit. (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.) You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\nNotwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n- a) Disclaiming warranty or limiting liability differently from the\n terms of sections 15 and 16 of this License; or\n- b) Requiring preservation of specified reasonable legal notices or\n author attributions in that material or in the Appropriate Legal\n Notices displayed by works containing it; or\n- c) Prohibiting misrepresentation of the origin of that material, or\n requiring that modified versions of such material be marked in\n reasonable ways as different from the original version; or\n- d) Limiting the use for publicity purposes of names of licensors or\n authors of the material; or\n- e) Declining to grant rights under trademark law for use of some\n trade names, trademarks, or service marks; or\n- f) Requiring indemnification of licensors and authors of that\n material by anyone who conveys the material (or modified versions of\n it) with contractual assumptions of liability to the recipient, for\n any liability that these contractual assumptions directly impose on\n those licensors and authors; or\n- g) Requiring strong attribution such as notices on any user interfaces\n that run or convey any covered work, such as a prominent link to a URL\n on the header of a website, such that all users of the covered work may\n become aware of the notice, for a period no longer than 20 years.\n\nAll other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10. If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term. If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\nIf you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\nAdditional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n### 8. Termination.\n\nYou may not propagate or modify a covered work except as expressly\nprovided under this License. Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\nHowever, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\nMoreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\nTermination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License. If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n### 9. Acceptance Not Required for Having Copies.\n\nYou are not required to accept this License in order to receive or\nrun a copy of the Program. Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance. However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work. These actions infringe copyright if you do\nnot accept this License. Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n### 10. Automatic Licensing of Downstream Recipients.\n\nEach time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License. You are not responsible\nfor enforcing compliance by third parties with this License.\n\nAn \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations. If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\nYou may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License. For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n### 11. Patents.\n\nA \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based. The\nwork thus licensed is called the contributor's \"contributor version\".\n\nA contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version. For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\nEach contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\nIn the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement). To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\nIf you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients. \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\nIf, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\nA patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License. You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\nNothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n### 12. No Surrender of Others' Freedom.\n\nIf conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License. If you cannot convey a\ncovered work so as to simultaneously satisfy your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all. For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n### 13. Remote Network Interaction; Use with the GNU General Public License.\n\nNotwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software. This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\nNotwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work. The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n### 14. Revised Versions of this License.\n\nNewTendermint LLC may publish revised and/or new versions of\nthe GNO Network General Public License from time to time. Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number. If the\nProgram specifies that a certain numbered version of the GNO Network General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Gno Software\nFoundation. If the Program does not specify a version number of the\nGNO Network General Public License, you may choose any version ever published\nby NewTendermint LLC.\n\nIf the Program specifies that a proxy can decide which future\nversions of the GNO Network General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\nLater license versions may give you additional or different\npermissions. However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n### 15. Disclaimer of Warranty.\n\nTHERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n### 16. Limitation of Liability.\n\nIN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n### 17. Interpretation of Sections 15 and 16.\n\nIf the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\nEND OF TERMS AND CONDITIONS\n\n## How to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n \u003cone line to give the program's name and a brief idea of what it does.\u003e\n Copyright (C) \u003cyear\u003e \u003cname of author\u003e\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNO Network General Public License as published by\n NewTendermint LLC, either version 4 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNO Network General Public License for more details.\n\n You should have received a copy of the GNO Network General Public License\n along with this program. If not, see \u003chttps://gno.land/license\u003e.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source. For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code. There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n`\n\t)\n\t_ = b.NewPost(\"\", path, title, body, \"2024-04-22T00:00:00Z\", nil, nil)\n}\n" + }, + { + "name": "page_partners.gno", + "body": "package gnopages\n\nfunc init() {\n\tpath := \"partners\"\n\ttitle := \"Partnerships\"\n\tbody := `### Fund and Grants Program\n\nAre you a builder, tinkerer, or researcher? If you’re looking to create awesome dApps, tooling, infrastructure, \nor smart contract libraries on gno.land, you can apply for a grant. The gno.land Ecosystem Fund and Grants program \nprovides financial contributions for individuals and teams to innovate on the platform.\n\nRead more about our Funds and Grants program [here](https://github.com/gnolang/ecosystem-fund-grants).\n`\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:27Z\", nil, nil)\n}\n" + }, + { + "name": "page_start.gno", + "body": "package gnopages\n\nfunc init() {\n\tpath := \"start\"\n\ttitle := \"Getting Started with Gno\"\n\t// XXX: description := \"\"\n\n\t// TODO: codegen to use README files here\n\n\t/* TODO: port previous message: This is a demo of Gno smart contract programming. This document was\n\tconstructed by Gno onto a smart contract hosted on the data Realm\n\tname [\"gno.land/r/demo/boards\"](https://gno.land/r/demo/boards/)\n\t([github](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/boards)).\n\t*/\n\tbody := `## Getting Started with Gno\n\n- [Install Gno Key](/r/demo/boards:testboard/5)\n- TODO: add more links\n`\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:28Z\", nil, nil)\n}\n" + }, + { + "name": "page_testnets.gno", + "body": "package gnopages\n\nfunc init() {\n\tpath := \"testnets\"\n\ttitle := \"Gno.land Testnet List\"\n\tbody := `\n- [Portal Loop](https://docs.gno.land/concepts/portal-loop) - a rolling testnet \n- [staging.gno.land](https://staging.gno.land) - wiped every commit to monorepo master\n- test4.gno.land (upcoming)\n- _[test3.gno.land](https://test3.gno.land) (latest)_\n- _[test2.gno.land](https://test2.gno.land) (archive)_\n- _[test1.gno.land](https://test1.gno.land) (archive)_\n\nFor a list of RPC endpoints, see the [reference documentation](https://docs.gno.land/reference/rpc-endpoints).\n\n## Local development\n\nSee the \"Getting started\" section in the [official documentation](https://docs.gno.land/getting-started/local-setup).\n`\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:29Z\", nil, nil)\n}\n" + }, + { + "name": "page_tokenomics.gno", + "body": "package gnopages\n\nfunc init() {\n\tvar (\n\t\tpath = \"tokenomics\"\n\t\ttitle = \"Gno.land Tokenomics\"\n\t\t// XXX: description = \"\"\"\n\t\tbody = `Lorem Ipsum`\n\t)\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:30Z\", nil, nil)\n}\n" + }, + { + "name": "pages.gno", + "body": "package gnopages\n\nimport (\n\t\"gno.land/p/demo/blog\"\n)\n\n// TODO: switch from p/blog to p/pages\n\nvar b = \u0026blog.Blog{\n\tTitle: \"Gnoland's Pages\",\n\tPrefix: \"/r/gnoland/pages:\",\n\tNoBreadcrumb: true,\n}\n\nfunc Render(path string) string {\n\treturn b.Render(path)\n}\n" + }, + { + "name": "pages_test.gno", + "body": "package gnopages\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestHome(t *testing.T) {\n\tprintedOnce := false\n\tgot := Render(\"\")\n\texpectedSubtrings := []string{\n\t\t\"/r/gnoland/pages:p/tokenomics\",\n\t\t\"/r/gnoland/pages:p/start\",\n\t\t\"/r/gnoland/pages:p/gor\",\n\t\t\"/r/gnoland/pages:p/about\",\n\t\t\"/r/gnoland/pages:p/gnolang\",\n\t}\n\tfor _, substring := range expectedSubtrings {\n\t\tif !strings.Contains(got, substring) {\n\t\t\tif !printedOnce {\n\t\t\t\tprintln(got)\n\t\t\t\tprintedOnce = true\n\t\t\t}\n\t\t\tt.Errorf(\"expected %q, but not found.\", substring)\n\t\t}\n\t}\n}\n\nfunc TestAbout(t *testing.T) {\n\tprintedOnce := false\n\tgot := Render(\"p/about\")\n\texpectedSubtrings := []string{\n\t\t\"Gno.land Is A Platform To Write Smart Contracts In Gno\",\n\t\t\"Gno.land is a next-generation smart contract platform using Gno, an interpreted version of the general-purpose Go\\nprogramming language.\",\n\t}\n\tfor _, substring := range expectedSubtrings {\n\t\tif !strings.Contains(got, substring) {\n\t\t\tif !printedOnce {\n\t\t\t\tprintln(got)\n\t\t\t\tprintedOnce = true\n\t\t\t}\n\t\t\tt.Errorf(\"expected %q, but not found.\", substring)\n\t\t}\n\t}\n}\n" + }, + { + "name": "util.gno", + "body": "package gnopages\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "govdao", + "path": "gno.land/r/gov/dao", + "files": [ + { + "name": "dao.gno", + "body": "package govdao\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/ufmt\"\n\tpproposal \"gno.land/p/gov/proposal\"\n)\n\nvar (\n\tproposals = make([]*proposal, 0)\n\tmembers = make([]std.Address, 0) // XXX: these should be pointers to avoid data duplication. Not possible due to VM bugs\n)\n\nconst (\n\tmsgMissingExecutor = \"missing proposal executor\"\n\tmsgPropExecuted = \"prop already executed\"\n\tmsgPropExpired = \"prop is expired\"\n\tmsgPropInactive = \"prop is not active anymore\"\n\tmsgPropActive = \"prop is still active\"\n\tmsgPropNotAccepted = \"prop is not accepted\"\n\n\tmsgCallerNotAMember = \"caller is not member of govdao\"\n\tmsgProposalNotFound = \"proposal not found\"\n)\n\nfunc init() {\n\t// Prepare the initial members\n\tset := []std.Address{\n\t\t/* GNO CORE */\n\t\t\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\", // Jae\n\t\t\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\", // Manfred\n\t\t\"g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2\", // Milos\n\t\t\"g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7\", // Nemanja\n\t\t\"g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d\", // Petar\n\t\t\"g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd\", // Marc\n\t\t\"g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl\", // Antonio\n\t\t\"g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x\", // Guilhem\n\t\t\"g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j\", // Ray\n\t\t\"g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq\", // Maxwell\n\t\t\"g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg\", // Dylan\n\t\t\"g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864\", // Morgan\n\t\t\"g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr\", // Sergio\n\t\t\"g125em6arxsnj49vx35f0n0z34putv5ty3376fg5\", // Leon\n\n\t\t/* GNO DEVX */\n\t\t\"g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu\", // Ilker\n\t\t\"g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun\", // Jerónimo\n\t\t\"g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd\", // Denis\n\t\t\"g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7\", // Danny\n\t\t\"g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5\", // Michelle\n\t\t\"g1mq7g0jszdmn4qdpc9tq94w0gyex37su892n80m\", // Alan\n\t\t\"g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh\", // Salvo\n\t\t\"g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq\", // Alexis\n\n\t\t/* ONBLOC */\n\t\t\"g17m8hlvm3k0agngz8vw29etpcjd2yvcel6pvt3k\", // Andrew\n\t\t\"g12vx7dn3dqq89mz550zwunvg4qw6epq73d9csay\", // Dongwon\n\t\t\"g1r04aw56fgvzy859fachr8hzzhqkulkaemltr76\", // Blake\n\t\t\"g17n4y745s08awwq4e0a38lagsgtntna0749tnxe\", // Jinwoo\n\t\t\"g1ckae7tc5sez8ul3ssne75sk4muwgttp6ks2ky9\", // ByeongJun\n\n\t\t/* TERITORI */\n\t\t\"g14u5eaheavy0ux4dmpykg2gvxpvqvexm9cyg58a\", // Norman\n\n\t\t/* BERTY */\n\t\t\"g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm\", // Rémi\n\n\t\t/* FLIPPANDO / ZENTASKTIC */\n\t\t\"g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3\", // Dragos\n\t}\n\n\t// Save the members\n\tmembers = append(members, set...)\n}\n\ntype proposal struct {\n\tauthor std.Address\n\tcomment string\n\texecutor pproposal.Executor\n\tvoter Voter\n\texecuted bool\n\tvoters []std.Address // XXX: these should be pointers to avoid data duplication. Not possible due to VM bugs.\n}\n\nfunc (p proposal) Status() Status {\n\tif p.executor.IsExpired() {\n\t\treturn Expired\n\t}\n\n\tif p.executor.IsDone() {\n\t\treturn Succeeded\n\t}\n\n\tif !p.voter.IsFinished(members) {\n\t\treturn Active\n\t}\n\n\tif p.voter.IsAccepted(members) {\n\t\treturn Accepted\n\t}\n\n\treturn NotAccepted\n}\n\n// Propose is designed to be called by another contract or with\n// `maketx run`, not by a `maketx call`.\nfunc Propose(comment string, executor pproposal.Executor) int {\n\t// XXX: require payment?\n\tif executor == nil {\n\t\tpanic(msgMissingExecutor)\n\t}\n\tcaller := std.GetOrigCaller() // XXX: CHANGE THIS WHEN MSGRUN PERSIST CODE ESCAPING THE main() SCOPE! IT IS UNSAFE!\n\tAssertIsMember(caller)\n\n\tprop := \u0026proposal{\n\t\tcomment: comment,\n\t\texecutor: executor,\n\t\tauthor: caller,\n\t\tvoter: NewPercentageVoter(66), // at least 2/3 must say yes\n\t}\n\n\tproposals = append(proposals, prop)\n\n\treturn len(proposals) - 1\n}\n\nfunc VoteOnProposal(idx int, option string) {\n\tassertProposalExists(idx)\n\tcaller := std.GetOrigCaller() // XXX: CHANGE THIS WHEN MSGRUN PERSIST CODE ESCAPING THE main() SCOPE! IT IS UNSAFE!\n\tAssertIsMember(caller)\n\n\tprop := getProposal(idx)\n\n\tif prop.executed {\n\t\tpanic(msgPropExecuted)\n\t}\n\n\tif prop.executor.IsExpired() {\n\t\tpanic(msgPropExpired)\n\t}\n\n\tif prop.voter.IsFinished(members) {\n\t\tpanic(msgPropInactive)\n\t}\n\n\tprop.voter.Vote(members, caller, option)\n}\n\nfunc ExecuteProposal(idx int) {\n\tassertProposalExists(idx)\n\tprop := getProposal(idx)\n\n\tif prop.executed {\n\t\tpanic(msgPropExecuted)\n\t}\n\n\tif prop.executor.IsExpired() {\n\t\tpanic(msgPropExpired)\n\t}\n\n\tif !prop.voter.IsFinished(members) {\n\t\tpanic(msgPropActive)\n\t}\n\n\tif !prop.voter.IsAccepted(members) {\n\t\tpanic(msgPropNotAccepted)\n\t}\n\n\tprop.executor.Execute()\n\tprop.voters = members\n\tprop.executed = true\n}\n\nfunc IsMember(addr std.Address) bool {\n\tif len(members) == 0 { // special case for initial execution\n\t\treturn true\n\t}\n\n\tfor _, v := range members {\n\t\tif v == addr {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc AssertIsMember(addr std.Address) {\n\tif !IsMember(addr) {\n\t\tpanic(msgCallerNotAMember)\n\t}\n}\n\nfunc Render(path string) string {\n\tif path == \"\" {\n\t\tif len(proposals) == 0 {\n\t\t\treturn \"No proposals found :(\" // corner case\n\t\t}\n\n\t\toutput := \"\"\n\t\tfor idx, prop := range proposals {\n\t\t\toutput += ufmt.Sprintf(\"- [%d](/r/gov/dao:%d) - %s (**%s**)(by %s)\\n\", idx, idx, prop.comment, string(prop.Status()), prop.author)\n\t\t}\n\n\t\treturn output\n\t}\n\n\t// else display the proposal\n\tidx, err := strconv.Atoi(path)\n\tif err != nil {\n\t\treturn \"404\"\n\t}\n\n\tif !proposalExists(idx) {\n\t\treturn \"404\"\n\t}\n\tprop := getProposal(idx)\n\n\tvs := members\n\tif prop.executed {\n\t\tvs = prop.voters\n\t}\n\n\toutput := \"\"\n\toutput += ufmt.Sprintf(\"# Prop #%d\", idx)\n\toutput += \"\\n\\n\"\n\toutput += prop.comment\n\toutput += \"\\n\\n\"\n\toutput += ufmt.Sprintf(\"Status: %s\", string(prop.Status()))\n\toutput += \"\\n\\n\"\n\toutput += ufmt.Sprintf(\"Voting status: %s\", prop.voter.Status(vs))\n\toutput += \"\\n\\n\"\n\toutput += ufmt.Sprintf(\"Author: %s\", string(prop.author))\n\toutput += \"\\n\\n\"\n\n\treturn output\n}\n\nfunc getProposal(idx int) *proposal {\n\tif idx \u003e len(proposals)-1 {\n\t\tpanic(msgProposalNotFound)\n\t}\n\n\treturn proposals[idx]\n}\n\nfunc proposalExists(idx int) bool {\n\treturn idx \u003e= 0 \u0026\u0026 idx \u003c= len(proposals)\n}\n\nfunc assertProposalExists(idx int) {\n\tif !proposalExists(idx) {\n\t\tpanic(\"invalid proposal id\")\n\t}\n}\n" + }, + { + "name": "dao_test.gno", + "body": "package govdao\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/urequire\"\n\tpproposal \"gno.land/p/gov/proposal\"\n)\n\nfunc TestPackage(t *testing.T) {\n\tu1 := testutils.TestAddress(\"u1\")\n\tu2 := testutils.TestAddress(\"u2\")\n\tu3 := testutils.TestAddress(\"u3\")\n\n\tmembers = append(members, u1)\n\tmembers = append(members, u2)\n\tmembers = append(members, u3)\n\n\tnu1 := testutils.TestAddress(\"random1\")\n\n\tout := Render(\"\")\n\n\texpected := \"No proposals found :(\"\n\turequire.Equal(t, expected, out)\n\n\tvar called bool\n\tex := pproposal.NewExecutor(func() error {\n\t\tcalled = true\n\t\treturn nil\n\t})\n\n\tstd.TestSetOrigCaller(u1)\n\tpid := Propose(\"dummy proposal\", ex)\n\n\t// try to vote not being a member\n\tstd.TestSetOrigCaller(nu1)\n\n\turequire.PanicsWithMessage(t, msgCallerNotAMember, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\n\t// try to vote several times\n\tstd.TestSetOrigCaller(u1)\n\turequire.NotPanics(t, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\turequire.PanicsWithMessage(t, msgAlreadyVoted, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: active\n\nVoting status: YES: 1, NO: 0, percent: 33, members: 3\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\tstd.TestSetOrigCaller(u2)\n\turequire.PanicsWithMessage(t, msgWrongVotingValue, func() {\n\t\tVoteOnProposal(pid, \"INCORRECT\")\n\t})\n\turequire.NotPanics(t, func() {\n\t\tVoteOnProposal(pid, \"NO\")\n\t})\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: active\n\nVoting status: YES: 1, NO: 1, percent: 33, members: 3\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\tstd.TestSetOrigCaller(u3)\n\turequire.NotPanics(t, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: accepted\n\nVoting status: YES: 2, NO: 1, percent: 66, members: 3\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\t// Add a new member, so non-executed proposals will change the voting status\n\tu4 := testutils.TestAddress(\"u4\")\n\tmembers = append(members, u4)\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: active\n\nVoting status: YES: 2, NO: 1, percent: 50, members: 4\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\tstd.TestSetOrigCaller(u4)\n\turequire.NotPanics(t, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: accepted\n\nVoting status: YES: 3, NO: 1, percent: 75, members: 4\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\tExecuteProposal(pid)\n\turequire.True(t, called)\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: succeeded\n\nVoting status: YES: 3, NO: 1, percent: 75, members: 4\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\t// Add a new member and try to vote an already executed proposal\n\tu5 := testutils.TestAddress(\"u5\")\n\tmembers = append(members, u5)\n\tstd.TestSetOrigCaller(u5)\n\turequire.PanicsWithMessage(t, msgPropExecuted, func() {\n\t\tExecuteProposal(pid)\n\t})\n\n\t// even if we added a new member the executed proposal is showing correctly the members that voted on it\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: succeeded\n\nVoting status: YES: 3, NO: 1, percent: 75, members: 4\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/gov/dao\n\nrequire (\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n\tgno.land/p/gov/proposal v0.0.0-latest\n)\n" + }, + { + "name": "memberset.gno", + "body": "package govdao\n\nimport (\n\t\"std\"\n\n\tpproposal \"gno.land/p/gov/proposal\"\n)\n\nconst daoPkgPath = \"gno.land/r/gov/dao\"\n\nconst (\n\terrNoChangesProposed = \"no set changes proposed\"\n\terrNotGovDAO = \"caller not govdao executor\"\n)\n\nfunc NewPropExecutor(changesFn func() []std.Address) pproposal.Executor {\n\tif changesFn == nil {\n\t\tpanic(errNoChangesProposed)\n\t}\n\n\tcallback := func() error {\n\t\t// Make sure the GovDAO executor runs the valset changes\n\t\tassertGovDAOCaller()\n\n\t\tfor _, addr := range changesFn() {\n\t\t\tmembers = append(members, addr)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\treturn pproposal.NewExecutor(callback)\n}\n\n// assertGovDAOCaller verifies the caller is the GovDAO executor\nfunc assertGovDAOCaller() {\n\tif std.CurrentRealm().PkgPath() != daoPkgPath {\n\t\tpanic(errNotGovDAO)\n\t}\n}\n" + }, + { + "name": "prop1_filetest.gno", + "body": "// Please note that this package is intended for demonstration purposes only.\n// You could execute this code (the init part) by running a `maketx run` command\n// or by uploading a similar package to a personal namespace.\n//\n// For the specific case of validators, a `r/gnoland/valopers` will be used to\n// organize the lifecycle of validators (register, etc), and this more complex\n// contract will be responsible to generate proposals.\npackage main\n\nimport (\n\t\"std\"\n\n\tpVals \"gno.land/p/sys/validators\"\n\tgovdao \"gno.land/r/gov/dao\"\n\t\"gno.land/r/sys/validators\"\n)\n\nconst daoPkgPath = \"gno.land/r/gov/dao\"\n\nfunc init() {\n\tmembersFn := func() []std.Address {\n\t\treturn []std.Address{\n\t\t\tstd.Address(\"g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\"),\n\t\t}\n\t}\n\n\tmExec := govdao.NewPropExecutor(membersFn)\n\n\tcomment := \"adding someone to vote\"\n\tid := govdao.Propose(comment, mExec)\n\tgovdao.ExecuteProposal(id)\n\n\tchangesFn := func() []pVals.Validator {\n\t\treturn []pVals.Validator{\n\t\t\t{\n\t\t\t\tAddress: std.Address(\"g12345678\"),\n\t\t\t\tPubKey: \"pubkey\",\n\t\t\t\tVotingPower: 10, // add a new validator\n\t\t\t},\n\t\t\t{\n\t\t\t\tAddress: std.Address(\"g000000000\"),\n\t\t\t\tPubKey: \"pubkey\",\n\t\t\t\tVotingPower: 10, // add a new validator\n\t\t\t},\n\t\t\t{\n\t\t\t\tAddress: std.Address(\"g000000000\"),\n\t\t\t\tPubKey: \"pubkey\",\n\t\t\t\tVotingPower: 0, // remove an existing validator\n\t\t\t},\n\t\t}\n\t}\n\n\t// Wraps changesFn to emit a certified event only if executed from a\n\t// complete governance proposal process.\n\texecutor := validators.NewPropExecutor(changesFn)\n\n\t// Create a proposal.\n\t// XXX: payment\n\tcomment = \"manual valset changes proposal example\"\n\tgovdao.Propose(comment, executor)\n}\n\nfunc main() {\n\tprintln(\"--\")\n\tprintln(govdao.Render(\"\"))\n\tprintln(\"--\")\n\tprintln(govdao.Render(\"1\"))\n\tprintln(\"--\")\n\tgovdao.VoteOnProposal(1, \"YES\")\n\tprintln(\"--\")\n\tprintln(govdao.Render(\"1\"))\n\tprintln(\"--\")\n\tprintln(validators.Render(\"\"))\n\tprintln(\"--\")\n\tgovdao.ExecuteProposal(1)\n\tprintln(\"--\")\n\tprintln(govdao.Render(\"1\"))\n\tprintln(\"--\")\n\tprintln(validators.Render(\"\"))\n}\n\n// Output:\n// --\n// - [0](/r/gov/dao:0) - adding someone to vote (**succeeded**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)\n// - [1](/r/gov/dao:1) - manual valset changes proposal example (**active**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)\n//\n// --\n// # Prop #1\n//\n// manual valset changes proposal example\n//\n// Status: active\n//\n// Voting status: YES: 0, NO: 0, percent: 0, members: 1\n//\n// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n//\n//\n// --\n// --\n// # Prop #1\n//\n// manual valset changes proposal example\n//\n// Status: accepted\n//\n// Voting status: YES: 1, NO: 0, percent: 100, members: 1\n//\n// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n//\n//\n// --\n// No valset changes to apply.\n// --\n// --\n// # Prop #1\n//\n// manual valset changes proposal example\n//\n// Status: succeeded\n//\n// Voting status: YES: 1, NO: 0, percent: 100, members: 1\n//\n// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n//\n//\n// --\n// Valset changes:\n// - #123: g12345678 (10)\n// - #123: g000000000 (10)\n// - #123: g000000000 (0)\n" + }, + { + "name": "prop2_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\t\"time\"\n\n\t\"gno.land/p/demo/context\"\n\t\"gno.land/p/gov/proposal\"\n\tgnoblog \"gno.land/r/gnoland/blog\"\n\tgovdao \"gno.land/r/gov/dao\"\n)\n\nfunc init() {\n\tmembersFn := func() []std.Address {\n\t\treturn []std.Address{\n\t\t\tstd.Address(\"g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\"),\n\t\t}\n\t}\n\n\tmExec := govdao.NewPropExecutor(membersFn)\n\n\tcomment := \"adding someone to vote\"\n\n\tid := govdao.Propose(comment, mExec)\n\n\tgovdao.ExecuteProposal(id)\n\n\texecutor := proposal.NewCtxExecutor(func(ctx context.Context) error {\n\t\tgnoblog.DaoAddPost(\n\t\t\tctx,\n\t\t\t\"hello-from-govdao\", // slug\n\t\t\t\"Hello from GovDAO!\", // title\n\t\t\t\"This post was published by a GovDAO proposal.\", // body\n\t\t\ttime.Now().Format(time.RFC3339), // publidation date\n\t\t\t\"moul\", // authors\n\t\t\t\"govdao,example\", // tags\n\t\t)\n\t\treturn nil\n\t})\n\n\t// Create a proposal.\n\t// XXX: payment\n\tcomment = \"post a new blogpost about govdao\"\n\tgovdao.Propose(comment, executor)\n}\n\nfunc main() {\n\tprintln(\"--\")\n\tprintln(govdao.Render(\"\"))\n\tprintln(\"--\")\n\tprintln(govdao.Render(\"1\"))\n\tprintln(\"--\")\n\tgovdao.VoteOnProposal(1, \"YES\")\n\tprintln(\"--\")\n\tprintln(govdao.Render(\"1\"))\n\tprintln(\"--\")\n\tprintln(gnoblog.Render(\"\"))\n\tprintln(\"--\")\n\tgovdao.ExecuteProposal(1)\n\tprintln(\"--\")\n\tprintln(govdao.Render(\"1\"))\n\tprintln(\"--\")\n\tprintln(gnoblog.Render(\"\"))\n}\n\n// Output:\n// --\n// - [0](/r/gov/dao:0) - adding someone to vote (**succeeded**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)\n// - [1](/r/gov/dao:1) - post a new blogpost about govdao (**active**)(by g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm)\n//\n// --\n// # Prop #1\n//\n// post a new blogpost about govdao\n//\n// Status: active\n//\n// Voting status: YES: 0, NO: 0, percent: 0, members: 1\n//\n// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n//\n//\n// --\n// --\n// # Prop #1\n//\n// post a new blogpost about govdao\n//\n// Status: accepted\n//\n// Voting status: YES: 1, NO: 0, percent: 100, members: 1\n//\n// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n//\n//\n// --\n// # Gnoland's Blog\n//\n// No posts.\n// --\n// --\n// # Prop #1\n//\n// post a new blogpost about govdao\n//\n// Status: succeeded\n//\n// Voting status: YES: 1, NO: 0, percent: 100, members: 1\n//\n// Author: g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm\n//\n//\n// --\n// # Gnoland's Blog\n//\n// \u003cdiv class='columns-3'\u003e\u003cdiv\u003e\n//\n// ### [Hello from GovDAO!](/r/gnoland/blog:p/hello-from-govdao)\n// 13 Feb 2009\n// \u003c/div\u003e\u003c/div\u003e\n" + }, + { + "name": "types.gno", + "body": "package govdao\n\nimport (\n\t\"std\"\n)\n\n// Status enum.\ntype Status string\n\nvar (\n\tAccepted Status = \"accepted\"\n\tActive Status = \"active\"\n\tNotAccepted Status = \"not accepted\"\n\tExpired Status = \"expired\"\n\tSucceeded Status = \"succeeded\"\n)\n\n// Voter defines the needed methods for a voting system\ntype Voter interface {\n\n\t// IsAccepted indicates if the voting process had been accepted\n\tIsAccepted(voters []std.Address) bool\n\n\t// IsFinished indicates if the voting process is finished\n\tIsFinished(voters []std.Address) bool\n\n\t// Vote adds a new vote to the voting system\n\tVote(voters []std.Address, caller std.Address, flag string)\n\n\t// Status returns a human friendly string describing how the voting process is going\n\tStatus(voters []std.Address) string\n}\n" + }, + { + "name": "voter.gno", + "body": "package govdao\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nconst (\n\tyay = \"YES\"\n\tnay = \"NO\"\n\n\tmsgNoMoreVotesAllowed = \"no more votes allowed\"\n\tmsgAlreadyVoted = \"caller already voted\"\n\tmsgWrongVotingValue = \"voting values must be YES or NO\"\n)\n\nfunc NewPercentageVoter(percent int) *PercentageVoter {\n\tif percent \u003c 0 || percent \u003e 100 {\n\t\tpanic(\"percent value must be between 0 and 100\")\n\t}\n\n\treturn \u0026PercentageVoter{\n\t\tpercentage: percent,\n\t}\n}\n\n// PercentageVoter is a system based on the amount of received votes.\n// When the specified treshold is reached, the voting process finishes.\ntype PercentageVoter struct {\n\tpercentage int\n\n\tvoters []std.Address\n\tyes int\n\tno int\n}\n\nfunc (pv *PercentageVoter) IsAccepted(voters []std.Address) bool {\n\tif len(voters) == 0 {\n\t\treturn true // special case\n\t}\n\n\treturn pv.percent(voters) \u003e= pv.percentage\n}\n\nfunc (pv *PercentageVoter) IsFinished(voters []std.Address) bool {\n\treturn pv.yes+pv.no \u003e= len(voters)\n}\n\nfunc (pv *PercentageVoter) Status(voters []std.Address) string {\n\treturn ufmt.Sprintf(\"YES: %d, NO: %d, percent: %d, members: %d\", pv.yes, pv.no, pv.percent(voters), len(voters))\n}\n\nfunc (pv *PercentageVoter) Vote(voters []std.Address, caller std.Address, flag string) {\n\tif pv.IsFinished(voters) {\n\t\tpanic(msgNoMoreVotesAllowed)\n\t}\n\n\tif pv.alreadyVoted(caller) {\n\t\tpanic(msgAlreadyVoted)\n\t}\n\n\tswitch flag {\n\tcase yay:\n\t\tpv.yes++\n\t\tpv.voters = append(pv.voters, caller)\n\tcase nay:\n\t\tpv.no++\n\t\tpv.voters = append(pv.voters, caller)\n\tdefault:\n\t\tpanic(msgWrongVotingValue)\n\t}\n}\n\nfunc (pv *PercentageVoter) percent(voters []std.Address) int {\n\tif len(voters) == 0 {\n\t\treturn 0\n\t}\n\n\treturn int((float32(pv.yes) / float32(len(voters))) * 100)\n}\n\nfunc (pv *PercentageVoter) alreadyVoted(addr std.Address) bool {\n\tfor _, v := range pv.voters {\n\t\tif v == addr {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "validators", + "path": "gno.land/r/sys/validators", + "files": [ + { + "name": "doc.gno", + "body": "// Package validators implements the on-chain validator set management through Proof of Contribution.\n// The Realm exposes only a public executor for govdao proposals, that can suggest validator set changes.\npackage validators\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/sys/validators\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/seqid v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/gov/proposal v0.0.0-latest\n\tgno.land/p/nt/poa v0.0.0-latest\n\tgno.land/p/sys/validators v0.0.0-latest\n)\n" + }, + { + "name": "gnosdk.gno", + "body": "package validators\n\nimport (\n\t\"gno.land/p/sys/validators\"\n)\n\n// GetChanges returns the validator changes stored on the realm, since the given block number.\n// This function is intended to be called by gno.land through the GnoSDK\nfunc GetChanges(from int64) []validators.Validator {\n\tvalsetChanges := make([]validators.Validator, 0)\n\n\t// Gather the changes from the specified block\n\tchanges.Iterate(getBlockID(from), \"\", func(_ string, value interface{}) bool {\n\t\tchs := value.([]change)\n\n\t\tfor _, ch := range chs {\n\t\t\tvalsetChanges = append(valsetChanges, ch.validator)\n\t\t}\n\n\t\treturn false\n\t})\n\n\treturn valsetChanges\n}\n" + }, + { + "name": "init.gno", + "body": "package validators\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/nt/poa\"\n\t\"gno.land/p/sys/validators\"\n)\n\nfunc init() {\n\t// Prepare the initial validator set\n\tset := []validators.Validator{\n\t\t// core-val-1\n\t\t{\n\t\t\tAddress: std.Address(\"g19v2h4pn6lrf8pwvhn8h0anek0cpt2tmhye4vkv\"),\n\t\t\tPubKey: \"gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zqm6x7rlcrp96dz60tn4ws5a6mt34aptmj4qrzxv59fcnlrrd59q52vvvrj\",\n\t\t\tVotingPower: 1,\n\t\t},\n\t\t// core-val-2\n\t\t{\n\t\t\tAddress: std.Address(\"g13us7swtc9hq550y9v4z6vcarak9vf8nqdvcqj4\"),\n\t\t\tPubKey: \"gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zp6z4cv32j5sjte5keucvfj6f44m9ctmaj4seqgg4vmekd4jdpzd6hzf0n7\",\n\t\t\tVotingPower: 1,\n\t\t},\n\t\t// core-val-3\n\t\t{\n\t\t\tAddress: std.Address(\"g1ectm6algkfw3qnjmjvx7hacmh358t36ggj5lqv\"),\n\t\t\tPubKey: \"gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zpq4pfx2yfnaqy4pf6xwz2mjx8fvw8d3rmfxxdega46g0z3ak2t47wj27kf\",\n\t\t\tVotingPower: 1,\n\t\t},\n\t\t// core-val-4\n\t\t{\n\t\t\tAddress: std.Address(\"g1tcxls3ylnrwrq95j33xpyuct4l370ra7jca4kj\"),\n\t\t\tPubKey: \"gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zp8lg7lfxj2lp68txvkh2mrpjkmgatpcgpsmlw53vssx9m2zgtfmnz5teuj\",\n\t\t\tVotingPower: 1,\n\t\t},\n\t\t// devx-val-1\n\t\t{\n\t\t\tAddress: std.Address(\"g1mxguhd5zacar64txhfm0v7hhtph5wur5hx86vs\"),\n\t\t\tPubKey: \"gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zqz6fwulsygvu9xypka3zqxhkxllm467e3adphmj6y44vn3yy8qq34vxnse\",\n\t\t\tVotingPower: 1,\n\t\t},\n\t\t// devx-val-2\n\t\t{\n\t\t\tAddress: std.Address(\"g1t9ctfa468hn6czff8kazw08crazehcxaqa2uaa\"),\n\t\t\tPubKey: \"gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zpsq650w975vqsf6ajj5x4wdzfnrh64kmw7sljqz7wts6k0p6l36d0huls3\",\n\t\t\tVotingPower: 1,\n\t\t},\n\t\t// onbloc-val-1\n\t\t{\n\t\t\tAddress: std.Address(\"g1gav33elude7prcdctpjekze7ft9l8qdjxqaj6d\"),\n\t\t\tPubKey: \"gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zp7pjfz35u8pm3jld0rsyw4ctphzql7n9jr59vrylq5l4f8qh35nw3pagcx\",\n\t\t\tVotingPower: 1,\n\t\t},\n\t}\n\n\t// The default valset protocol is PoA\n\tvp = poa.NewPoA(poa.WithInitialSet(set))\n\n\t// No changes to apply initially\n\tchanges = avl.NewTree()\n}\n" + }, + { + "name": "poc.gno", + "body": "package validators\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/gov/proposal\"\n\t\"gno.land/p/sys/validators\"\n)\n\nconst daoPkgPath = \"gno.land/r/gov/dao\"\n\nconst (\n\terrNoChangesProposed = \"no set changes proposed\"\n\terrNotGovDAO = \"caller not govdao executor\"\n)\n\n// NewPropExecutor creates a new executor that wraps a changes closure\n// proposal. This wrapper is required to ensure the GovDAO Realm actually\n// executed the callback.\n//\n// Concept adapted from:\n// https://github.com/gnolang/gno/pull/1945\nfunc NewPropExecutor(changesFn func() []validators.Validator) proposal.Executor {\n\tif changesFn == nil {\n\t\tpanic(errNoChangesProposed)\n\t}\n\n\tcallback := func() error {\n\t\t// Make sure the GovDAO executor runs the valset changes\n\t\tassertGovDAOCaller()\n\n\t\tfor _, change := range changesFn() {\n\t\t\tif change.VotingPower == 0 {\n\t\t\t\t// This change request is to remove the validator\n\t\t\t\tremoveValidator(change.Address)\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// This change request is to add the validator\n\t\t\taddValidator(change)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\treturn proposal.NewExecutor(callback)\n}\n\n// assertGovDAOCaller verifies the caller is the GovDAO executor\nfunc assertGovDAOCaller() {\n\tif std.PrevRealm().PkgPath() != daoPkgPath {\n\t\tpanic(errNotGovDAO)\n\t}\n}\n\n// IsValidator returns a flag indicating if the given bech32 address\n// is part of the validator set\nfunc IsValidator(addr std.Address) bool {\n\treturn vp.IsValidator(addr)\n}\n\n// GetValidators returns the typed validator set\nfunc GetValidators() []validators.Validator {\n\treturn vp.GetValidators()\n}\n" + }, + { + "name": "validators.gno", + "body": "package validators\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/seqid\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/sys/validators\"\n)\n\nvar (\n\tvp validators.ValsetProtocol // p is the underlying validator set protocol\n\tchanges *avl.Tree // changes holds any valset changes; seqid(block number) -\u003e []change\n)\n\n// change represents a single valset change, tied to a specific block number\ntype change struct {\n\tblockNum int64 // the block number associated with the valset change\n\tvalidator validators.Validator // the validator update\n}\n\n// addValidator adds a new validator to the validator set.\n// If the validator is already present, the method errors out\nfunc addValidator(validator validators.Validator) {\n\tval, err := vp.AddValidator(validator.Address, validator.PubKey, validator.VotingPower)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Validator added, note the change\n\tch := change{\n\t\tblockNum: std.GetHeight(),\n\t\tvalidator: val,\n\t}\n\n\tsaveChange(ch)\n\n\t// Emit the validator set change\n\tstd.Emit(validators.ValidatorAddedEvent)\n}\n\n// removeValidator removes the given validator from the set.\n// If the validator is not present in the set, the method errors out\nfunc removeValidator(address std.Address) {\n\tval, err := vp.RemoveValidator(address)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Validator removed, note the change\n\tch := change{\n\t\tblockNum: std.GetHeight(),\n\t\tvalidator: validators.Validator{\n\t\t\tAddress: val.Address,\n\t\t\tPubKey: val.PubKey,\n\t\t\tVotingPower: 0, // nullified the voting power indicates removal\n\t\t},\n\t}\n\n\tsaveChange(ch)\n\n\t// Emit the validator set change\n\tstd.Emit(validators.ValidatorRemovedEvent)\n}\n\n// saveChange saves the valset change\nfunc saveChange(ch change) {\n\tid := getBlockID(ch.blockNum)\n\n\tsetRaw, exists := changes.Get(id)\n\tif !exists {\n\t\tchanges.Set(id, []change{ch})\n\n\t\treturn\n\t}\n\n\t// Save the change\n\tset := setRaw.([]change)\n\tset = append(set, ch)\n\n\tchanges.Set(id, set)\n}\n\n// getBlockID converts the block number to a sequential ID\nfunc getBlockID(blockNum int64) string {\n\treturn seqid.ID(uint64(blockNum)).String()\n}\n\nfunc Render(_ string) string {\n\tvar (\n\t\tsize = changes.Size()\n\t\tmaxDisplay = 10\n\t)\n\n\tif size == 0 {\n\t\treturn \"No valset changes to apply.\"\n\t}\n\n\toutput := \"Valset changes:\\n\"\n\tchanges.ReverseIterateByOffset(size-maxDisplay, maxDisplay, func(_ string, value interface{}) bool {\n\t\tchs := value.([]change)\n\n\t\tfor _, ch := range chs {\n\t\t\toutput += ufmt.Sprintf(\n\t\t\t\t\"- #%d: %s (%d)\\n\",\n\t\t\t\tch.blockNum,\n\t\t\t\tch.validator.Address.String(),\n\t\t\t\tch.validator.VotingPower,\n\t\t\t)\n\t\t}\n\n\t\treturn false\n\t})\n\n\treturn output\n}\n" + }, + { + "name": "validators_test.gno", + "body": "package validators\n\nimport (\n\t\"testing\"\n\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/uassert\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/sys/validators\"\n)\n\n// generateTestValidators generates a dummy validator set\nfunc generateTestValidators(count int) []validators.Validator {\n\tvals := make([]validators.Validator, 0, count)\n\n\tfor i := 0; i \u003c count; i++ {\n\t\tval := validators.Validator{\n\t\t\tAddress: testutils.TestAddress(ufmt.Sprintf(\"%d\", i)),\n\t\t\tPubKey: \"public-key\",\n\t\t\tVotingPower: 10,\n\t\t}\n\n\t\tvals = append(vals, val)\n\t}\n\n\treturn vals\n}\n\nfunc TestValidators_AddRemove(t *testing.T) {\n\t// Clear any changes\n\tchanges = avl.NewTree()\n\n\tvar (\n\t\tvals = generateTestValidators(100)\n\t\tinitialHeight = int64(123)\n\t)\n\n\t// Add in the validators\n\tfor _, val := range vals {\n\t\taddValidator(val)\n\n\t\t// Make sure the validator is added\n\t\tuassert.True(t, vp.IsValidator(val.Address))\n\n\t\tstd.TestSkipHeights(1)\n\t}\n\n\tfor i := initialHeight; i \u003c initialHeight+int64(len(vals)); i++ {\n\t\t// Make sure the changes are saved\n\t\tchs := GetChanges(i)\n\n\t\t// We use the funky index calculation to make sure\n\t\t// changes are properly handled for each block span\n\t\tuassert.Equal(t, initialHeight+int64(len(vals))-i, int64(len(chs)))\n\n\t\tfor index, val := range vals[i-initialHeight:] {\n\t\t\t// Make sure the changes are equal to the additions\n\t\t\tch := chs[index]\n\n\t\t\tuassert.Equal(t, val.Address, ch.Address)\n\t\t\tuassert.Equal(t, val.PubKey, ch.PubKey)\n\t\t\tuassert.Equal(t, val.VotingPower, ch.VotingPower)\n\t\t}\n\t}\n\n\t// Save the beginning height for the removal\n\tinitialRemoveHeight := std.GetHeight()\n\n\t// Clear any changes\n\tchanges = avl.NewTree()\n\n\t// Remove the validators\n\tfor _, val := range vals {\n\t\tremoveValidator(val.Address)\n\n\t\t// Make sure the validator is removed\n\t\tuassert.False(t, vp.IsValidator(val.Address))\n\n\t\tstd.TestSkipHeights(1)\n\t}\n\n\tfor i := initialRemoveHeight; i \u003c initialRemoveHeight+int64(len(vals)); i++ {\n\t\t// Make sure the changes are saved\n\t\tchs := GetChanges(i)\n\n\t\t// We use the funky index calculation to make sure\n\t\t// changes are properly handled for each block span\n\t\tuassert.Equal(t, initialRemoveHeight+int64(len(vals))-i, int64(len(chs)))\n\n\t\tfor index, val := range vals[i-initialRemoveHeight:] {\n\t\t\t// Make sure the changes are equal to the additions\n\t\t\tch := chs[index]\n\n\t\t\tuassert.Equal(t, val.Address, ch.Address)\n\t\t\tuassert.Equal(t, val.PubKey, ch.PubKey)\n\t\t\tuassert.Equal(t, uint64(0), ch.VotingPower)\n\t\t}\n\t}\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "valopers", + "path": "gno.land/r/gnoland/valopers", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/gnoland/valopers\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/sys/validators v0.0.0-latest\n\tgno.land/r/gov/dao v0.0.0-latest\n\tgno.land/r/sys/validators v0.0.0-latest\n)\n" + }, + { + "name": "init.gno", + "body": "package valopers\n\nimport \"gno.land/p/demo/avl\"\n\nfunc init() {\n\tvalopers = avl.NewTree()\n}\n" + }, + { + "name": "valopers.gno", + "body": "// Package valopers is designed around the permissionless lifecycle of valoper profiles.\n// It also includes parts designed for govdao to propose valset changes based on registered valopers.\npackage valopers\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n\tpVals \"gno.land/p/sys/validators\"\n\tgovdao \"gno.land/r/gov/dao\"\n\t\"gno.land/r/sys/validators\"\n)\n\nconst (\n\terrValoperExists = \"valoper already exists\"\n\terrValoperMissing = \"valoper does not exist\"\n\terrInvalidAddressUpdate = \"valoper updated address exists\"\n\terrValoperNotCaller = \"valoper is not the caller\"\n)\n\n// valopers keeps track of all the active validator operators\nvar valopers *avl.Tree // Address -\u003e Valoper\n\n// Valoper represents a validator operator profile\ntype Valoper struct {\n\tName string // the display name of the valoper\n\tDescription string // the description of the valoper\n\n\tAddress std.Address // The bech32 gno address of the validator\n\tPubKey string // the bech32 public key of the validator\n\tP2PAddresses []string // the publicly reachable P2P addresses of the validator\n\tActive bool // flag indicating if the valoper is active\n}\n\n// Register registers a new valoper\nfunc Register(v Valoper) {\n\t// Check if the valoper is already registered\n\tif isValoper(v.Address) {\n\t\tpanic(errValoperExists)\n\t}\n\n\t// TODO add address derivation from public key\n\t// (when the laws of gno make it possible)\n\n\t// Save the valoper to the set\n\tvalopers.Set(v.Address.String(), v)\n}\n\n// Update updates an existing valoper\nfunc Update(address std.Address, v Valoper) {\n\t// Check if the valoper is present\n\tif !isValoper(address) {\n\t\tpanic(errValoperMissing)\n\t}\n\n\t// Check that the valoper wouldn't be\n\t// overwriting an existing one\n\tisAddressUpdate := address != v.Address\n\tif isAddressUpdate \u0026\u0026 isValoper(v.Address) {\n\t\tpanic(errInvalidAddressUpdate)\n\t}\n\n\t// Remove the old valoper info\n\t// in case the address changed\n\tif address != v.Address {\n\t\tvalopers.Remove(address.String())\n\t}\n\n\t// Save the new valoper info\n\tvalopers.Set(v.Address.String(), v)\n}\n\n// GetByAddr fetches the valoper using the address, if present\nfunc GetByAddr(address std.Address) Valoper {\n\tvaloperRaw, exists := valopers.Get(address.String())\n\tif !exists {\n\t\tpanic(errValoperMissing)\n\t}\n\n\treturn valoperRaw.(Valoper)\n}\n\n// Render renders the current valoper set\nfunc Render(_ string) string {\n\tif valopers.Size() == 0 {\n\t\treturn \"No valopers to display.\"\n\t}\n\n\toutput := \"Valset changes to apply:\\n\"\n\tvalopers.Iterate(\"\", \"\", func(_ string, value interface{}) bool {\n\t\tvaloper := value.(Valoper)\n\n\t\toutput += valoper.Render()\n\n\t\treturn false\n\t})\n\n\treturn output\n}\n\n// Render renders a single valoper with their information\nfunc (v Valoper) Render() string {\n\toutput := ufmt.Sprintf(\"## %s\\n\", v.Name)\n\toutput += ufmt.Sprintf(\"%s\\n\\n\", v.Description)\n\toutput += ufmt.Sprintf(\"- Address: %s\\n\", v.Address.String())\n\toutput += ufmt.Sprintf(\"- PubKey: %s\\n\", v.PubKey)\n\toutput += \"- P2P Addresses: [\\n\"\n\n\tif len(v.P2PAddresses) == 0 {\n\t\toutput += \"]\\n\"\n\n\t\treturn output\n\t}\n\n\tfor index, addr := range v.P2PAddresses {\n\t\toutput += addr\n\n\t\tif index == len(v.P2PAddresses)-1 {\n\t\t\toutput += \"]\\n\"\n\n\t\t\tcontinue\n\t\t}\n\n\t\toutput += \",\\n\"\n\t}\n\n\treturn output\n}\n\n// isValoper checks if the valoper exists\nfunc isValoper(address std.Address) bool {\n\t_, exists := valopers.Get(address.String())\n\n\treturn exists\n}\n\n// GovDAOProposal creates a proposal to the GovDAO\n// for adding the given valoper to the validator set.\n// This function is meant to serve as a helper\n// for generating the govdao proposal\nfunc GovDAOProposal(address std.Address) {\n\tvar (\n\t\tvaloper = GetByAddr(address)\n\t\tvotingPower = uint64(1)\n\t)\n\n\t// Make sure the valoper is the caller\n\tif std.GetOrigCaller() != address {\n\t\tpanic(errValoperNotCaller)\n\t}\n\n\t// Determine the voting power\n\tif !valoper.Active {\n\t\tvotingPower = uint64(0)\n\t}\n\n\tchangesFn := func() []pVals.Validator {\n\t\treturn []pVals.Validator{\n\t\t\t{\n\t\t\t\tAddress: valoper.Address,\n\t\t\t\tPubKey: valoper.PubKey,\n\t\t\t\tVotingPower: votingPower,\n\t\t\t},\n\t\t}\n\t}\n\n\t// Create the executor\n\texecutor := validators.NewPropExecutor(changesFn)\n\n\t// Craft the proposal comment\n\tcomment := ufmt.Sprintf(\n\t\t\"Proposal to add valoper %s (Address: %s; PubKey: %s) to the valset\",\n\t\tvaloper.Name,\n\t\tvaloper.Address.String(),\n\t\tvaloper.PubKey,\n\t)\n\n\t// Create the govdao proposal\n\tgovdao.Propose(comment, executor)\n}\n" + }, + { + "name": "valopers_test.gno", + "body": "package valopers\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/uassert\"\n)\n\nfunc TestValopers_Register(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"already a valoper\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Clear the set for the test\n\t\tvalopers = avl.NewTree()\n\n\t\tv := Valoper{\n\t\t\tAddress: testutils.TestAddress(\"valoper\"),\n\t\t}\n\n\t\t// Add the valoper\n\t\tvalopers.Set(v.Address.String(), v)\n\n\t\tuassert.PanicsWithMessage(t, errValoperExists, func() {\n\t\t\tRegister(v)\n\t\t})\n\t})\n\n\tt.Run(\"successful registration\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Clear the set for the test\n\t\tvalopers = avl.NewTree()\n\n\t\tv := Valoper{\n\t\t\tAddress: testutils.TestAddress(\"valoper\"),\n\t\t\tName: \"new valoper\",\n\t\t\tPubKey: \"pub key\",\n\t\t}\n\n\t\tuassert.NotPanics(t, func() {\n\t\t\tRegister(v)\n\t\t})\n\n\t\tuassert.NotPanics(t, func() {\n\t\t\tvaloper := GetByAddr(v.Address)\n\n\t\t\tuassert.Equal(t, v.Address, valoper.Address)\n\t\t\tuassert.Equal(t, v.Name, valoper.Name)\n\t\t\tuassert.Equal(t, v.PubKey, valoper.PubKey)\n\t\t})\n\t})\n}\n\nfunc TestValopers_Update(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"non-existing valoper\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Clear the set for the test\n\t\tvalopers = avl.NewTree()\n\n\t\tv := Valoper{}\n\n\t\t// Update the valoper\n\t\tuassert.PanicsWithMessage(t, errValoperMissing, func() {\n\t\t\tUpdate(v.Address, v)\n\t\t})\n\t})\n\n\tt.Run(\"overwrite valoper\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Clear the set for the test\n\t\tvalopers = avl.NewTree()\n\n\t\tone := Valoper{\n\t\t\tAddress: testutils.TestAddress(\"valoper 1\"),\n\t\t}\n\n\t\t// Add the valoper\n\t\tuassert.NotPanics(t, func() {\n\t\t\tRegister(one)\n\t\t})\n\n\t\tinitialAddress := testutils.TestAddress(\"valoper 2\")\n\t\ttwo := Valoper{\n\t\t\tAddress: initialAddress,\n\t\t}\n\n\t\t// Add the valoper\n\t\tuassert.NotPanics(t, func() {\n\t\t\tRegister(two)\n\t\t})\n\n\t\t// Update the valoper address\n\t\t// so it overlaps\n\t\ttwo = Valoper{\n\t\t\tAddress: one.Address,\n\t\t}\n\n\t\t// Update the valoper\n\t\tuassert.PanicsWithMessage(t, errInvalidAddressUpdate, func() {\n\t\t\tUpdate(initialAddress, two)\n\t\t})\n\t})\n\n\tt.Run(\"successful update\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Clear the set for the test\n\t\tvalopers = avl.NewTree()\n\n\t\tvar (\n\t\t\tname = \"new valoper\"\n\t\t\tv = Valoper{\n\t\t\t\tAddress: testutils.TestAddress(\"valoper\"),\n\t\t\t\tName: name,\n\t\t\t\tPubKey: \"pub key\",\n\t\t\t}\n\t\t)\n\n\t\t// Add the valoper\n\t\tuassert.NotPanics(t, func() {\n\t\t\tRegister(v)\n\t\t})\n\n\t\t// Update the valoper name\n\t\tv.Name = \"new name\"\n\t\tv.Active = false\n\n\t\t// Update the valoper\n\t\tuassert.NotPanics(t, func() {\n\t\t\tUpdate(v.Address, v)\n\t\t})\n\n\t\t// Make sure the valoper is updated\n\t\tuassert.NotPanics(t, func() {\n\t\t\tvaloper := GetByAddr(v.Address)\n\n\t\t\tuassert.Equal(t, v.Name, valoper.Name)\n\t\t\tuassert.Equal(t, v.Active, valoper.Active)\n\t\t})\n\t})\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "config", + "path": "gno.land/r/manfred/config", + "files": [ + { + "name": "config.gno", + "body": "package config\n\nimport \"std\"\n\nvar addr = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc Addr() std.Address {\n\treturn addr\n}\n\nfunc UpdateAddr(newAddr std.Address) {\n\tAssertIsAdmin()\n\taddr = newAddr\n}\n\nfunc AssertIsAdmin() {\n\tif std.GetOrigCaller() != addr {\n\t\tpanic(\"restricted area\")\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/manfred/config\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "home", + "path": "gno.land/r/manfred/home", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/manfred/home\n\nrequire gno.land/r/manfred/config v0.0.0-latest\n" + }, + { + "name": "home.gno", + "body": "package home\n\nimport \"gno.land/r/manfred/config\"\n\nvar (\n\ttodos []string\n\tstatus string\n\tmemeImgURL string\n)\n\nfunc init() {\n\ttodos = append(todos, \"fill this todo list...\")\n\tstatus = \"Online\" // Initial status set to \"Online\"\n\tmemeImgURL = \"https://i.imgflip.com/7ze8dc.jpg\"\n}\n\nfunc Render(path string) string {\n\tcontent := \"# Manfred's (gn)home Dashboard\\n\\n\"\n\n\tcontent += \"## Meme\\n\"\n\tcontent += \"![](\" + memeImgURL + \")\\n\\n\"\n\n\tcontent += \"## Status\\n\"\n\tcontent += status + \"\\n\\n\"\n\n\tcontent += \"## Personal ToDo List\\n\"\n\tfor _, todo := range todos {\n\t\tcontent += \"- [ ] \" + todo + \"\\n\"\n\t}\n\tcontent += \"\\n\"\n\n\t// TODO: Implement a feature to list replies on r/boards on my posts\n\t// TODO: Maybe integrate a calendar feature for upcoming events?\n\n\treturn content\n}\n\nfunc AddNewTodo(todo string) {\n\tconfig.AssertIsAdmin()\n\ttodos = append(todos, todo)\n}\n\nfunc DeleteTodo(todoIndex int) {\n\tconfig.AssertIsAdmin()\n\tif todoIndex \u003e= 0 \u0026\u0026 todoIndex \u003c len(todos) {\n\t\t// Remove the todo from the list by merging slices from before and after the todo\n\t\ttodos = append(todos[:todoIndex], todos[todoIndex+1:]...)\n\t} else {\n\t\tpanic(\"Invalid todo index\")\n\t}\n}\n\nfunc UpdateStatus(newStatus string) {\n\tconfig.AssertIsAdmin()\n\tstatus = newStatus\n}\n" + }, + { + "name": "z1_filetest.gno", + "body": "package main\n\nimport \"gno.land/r/manfred/home\"\n\nfunc main() {\n\tprintln(home.Render(\"\"))\n}\n\n// Output:\n// # Manfred's (gn)home Dashboard\n//\n// ## Meme\n// ![](https://i.imgflip.com/7ze8dc.jpg)\n//\n// ## Status\n// Online\n//\n// ## Personal ToDo List\n// - [ ] fill this todo list...\n" + }, + { + "name": "z2_filetest.gno", + "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/manfred/home\"\n)\n\nfunc main() {\n\tstd.TestSetOrigCaller(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\thome.AddNewTodo(\"aaa\")\n\thome.AddNewTodo(\"bbb\")\n\thome.AddNewTodo(\"ccc\")\n\thome.AddNewTodo(\"ddd\")\n\thome.AddNewTodo(\"eee\")\n\thome.UpdateStatus(\"Lorem Ipsum\")\n\thome.DeleteTodo(3)\n\tprintln(home.Render(\"\"))\n}\n\n// Output:\n// # Manfred's (gn)home Dashboard\n//\n// ## Meme\n// ![](https://i.imgflip.com/7ze8dc.jpg)\n//\n// ## Status\n// Lorem Ipsum\n//\n// ## Personal ToDo List\n// - [ ] fill this todo list...\n// - [ ] aaa\n// - [ ] bbb\n// - [ ] ddd\n// - [ ] eee\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "present", + "path": "gno.land/r/manfred/present", + "files": [ + { + "name": "admin.gno", + "body": "package present\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\nvar (\n\tadminAddr std.Address\n\tmoderatorList avl.Tree\n\tinPause bool\n)\n\nfunc init() {\n\t// adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis.\n\tadminAddr = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\"\n}\n\nfunc AdminSetAdminAddr(addr std.Address) {\n\tassertIsAdmin()\n\tadminAddr = addr\n}\n\nfunc AdminSetInPause(state bool) {\n\tassertIsAdmin()\n\tinPause = state\n}\n\nfunc AdminAddModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), true)\n}\n\nfunc AdminRemoveModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), false) // XXX: delete instead?\n}\n\nfunc ModAddPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\tcaller := std.GetOrigCaller()\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.NewPost(caller, slug, title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc ModEditPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.GetPost(slug).Update(title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc isAdmin(addr std.Address) bool {\n\treturn addr == adminAddr\n}\n\nfunc isModerator(addr std.Address) bool {\n\t_, found := moderatorList.Get(addr.String())\n\treturn found\n}\n\nfunc assertIsAdmin() {\n\tcaller := std.GetOrigCaller()\n\tif !isAdmin(caller) {\n\t\tpanic(\"access restricted.\")\n\t}\n}\n\nfunc assertIsModerator() {\n\tcaller := std.GetOrigCaller()\n\tif isAdmin(caller) || isModerator(caller) {\n\t\treturn\n\t}\n\tpanic(\"access restricted\")\n}\n\nfunc assertNotInPause() {\n\tif inPause {\n\t\tpanic(\"access restricted (pause)\")\n\t}\n}\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" + }, + { + "name": "gno.mod", + "body": "module gno.land/r/manfred/present\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/blog v0.0.0-latest\n)\n" + }, + { + "name": "present_miami23.gno", + "body": "package present\n\nfunc init() {\n\tpath := \"miami23\"\n\ttitle := \"Portal Loop Demo (Miami 2023)\"\n\tbody := `\nRendered by Gno.\n\n[Source (WIP)](https://github.com/gnolang/gno/pull/1176)\n\n## Portal Loop\n\n- DONE: Dynamic homepage, key pages, aliases, and redirects.\n- TODO: Deploy with history, complete worxdao v0.\n- Will replace the static gno.land site.\n- Enhances local development.\n\n[GitHub Issue](https://github.com/gnolang/gno/issues/1108)\n\n## Roadmap\n\n- Crafting the roadmap this week, open to collaboration.\n- Combining onchain (portal loop) and offchain (GitHub).\n- Next week: Unveiling the official v0 roadmap.\n\n## Teams, DAOs, Projects\n\n- Developing worxDAO contracts for directories of projects and teams.\n- GitHub teams and projects align with this structure.\n- CODEOWNER file updates coming.\n- Initial teams announced next week.\n\n## Tech Team Retreat Plan\n\n- Continue Portal Loop.\n- Consider dApp development.\n- Explore new topics [here](https://github.com/orgs/gnolang/projects/15/).\n- Engage in workshops.\n- Connect and have fun with colleagues.\n`\n\t_ = b.NewPost(adminAddr, path, title, body, \"2023-10-15T13:17:24Z\", []string{\"moul\"}, []string{\"demo\", \"portal-loop\", \"miami\"})\n}\n" + }, + { + "name": "present_miami23_filetest.gno", + "body": "package main\n\nimport (\n\t\"gno.land/r/manfred/present\"\n)\n\nfunc main() {\n\tprintln(present.Render(\"\"))\n\tprintln(\"------------------------------------\")\n\tprintln(present.Render(\"p/miami23\"))\n}\n" + }, + { + "name": "presentations.gno", + "body": "package present\n\nimport (\n\t\"gno.land/p/demo/blog\"\n)\n\n// TODO: switch from p/blog to p/present\n\nvar b = \u0026blog.Blog{\n\tTitle: \"Manfred's Presentations\",\n\tPrefix: \"/r/manfred/present:\",\n\tNoBreadcrumb: true,\n}\n\nfunc Render(path string) string {\n\treturn b.Render(path)\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "rewards", + "path": "gno.land/r/sys/rewards", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/sys/rewards\n" + }, + { + "name": "rewards.gno", + "body": "// This package will be used to manage proof-of-contributions on the exposed smart-contract side.\npackage rewards\n\n// TODO: write specs.\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + }, + { + "msg": [ + { + "@type": "/vm.m_addpkg", + "creator": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", + "package": { + "name": "users", + "path": "gno.land/r/sys/users", + "files": [ + { + "name": "gno.mod", + "body": "module gno.land/r/sys/users\n\nrequire (\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" + }, + { + "name": "verify.gno", + "body": "package users\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\" // @moul\n\ntype VerifyNameFunc func(enabled bool, address std.Address, name string) bool\n\nvar (\n\towner = ownable.NewWithAddress(admin) // Package owner\n\tcheckFunc = VerifyNameByUser // Checking namespace callback\n\tenabled = true // For now this package is disabled by default\n)\n\nfunc IsEnabled() bool { return enabled }\n\n// This method ensures that the given address has ownership of the given name.\nfunc IsAuthorizedAddressForName(address std.Address, name string) bool {\n\treturn checkFunc(enabled, address, name)\n}\n\n// VerifyNameByUser checks from the `users` package that the user has correctly\n// registered the given name.\n// This function considers as valid an `address` that matches the `name`.\nfunc VerifyNameByUser(enable bool, address std.Address, name string) bool {\n\tif !enable {\n\t\treturn true\n\t}\n\n\t// Allow user with their own address as name\n\tif address.String() == name {\n\t\treturn true\n\t}\n\n\tif user := users.GetUserByName(name); user != nil {\n\t\treturn user.Address == address\n\t}\n\n\treturn false\n}\n\n// Admin calls\n\n// Enable this package.\nfunc AdminEnable() {\n\tif err := owner.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tenabled = true\n}\n\n// Disable this package.\nfunc AdminDisable() {\n\tif err := owner.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tenabled = false\n}\n\n// AdminUpdateVerifyCall updates the method that verifies the namespace.\nfunc AdminUpdateVerifyCall(check VerifyNameFunc) {\n\tif err := owner.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tcheckFunc = check\n}\n\n// AdminTransferOwnership transfers the ownership to a new owner.\nfunc AdminTransferOwnership(newOwner std.Address) error {\n\tif err := owner.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn owner.TransferOwnership(newOwner)\n}\n" + } + ] + }, + "deposit": "" + } + ], + "fee": { + "gas_wanted": "50000", + "gas_fee": "1000000ugnot" + }, + "signatures": [ + { + "pub_key": null, + "signature": null + } + ], + "memo": "" + } + ] } } \ No newline at end of file From 6b8eb74b03c60c8550612b370938b1d4edc5bc3a Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Tue, 9 Jul 2024 20:43:11 +0200 Subject: [PATCH 19/22] Predeploy test1 --- misc/deployments/test4.gno.land/genesis.json | 59 ++++++++++--------- .../test4.gno.land/genesis_balances.txt | 4 ++ 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index 7876f112ca3..fc08f3734d1 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -84,45 +84,46 @@ "app_state": { "@type": "/gno.GenesisState", "balances": [ - "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", + "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", + "g1gadp0yq5djelxwv7h8g7wpdf7x5w3vuzwmqrne=9000000000000000000ugnot", + "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", + "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=103000000ugnot", + "g1cm7u2fqd7drwfcqtn7zsgrfcyvlkkukhhtrxxj=9000000000000000000ugnot", "g1knpjkw7d5ft2830n8rnatllzq9gu85dmn20nrp=9000000000000000000ugnot", - "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", - "g1xja7p9s3ly3tvkgks9x0n6f6yau2hnzl4x8x3d=9000000000000000000ugnot", - "g1563d3cw0gdv68le6azw2s63xm0jx9xvgpmfatq=9000000000000000000ugnot", - "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot", - "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", - "g16f5chytu99dmjqtekxf8qzg04vcv7dck6qny6d=9000000000000000000ugnot", + "g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot", + "g1a5kdc6ykfykq9p6088sclnr7e63hj8y4nnr5gn=9000000000000000000ugnot", "g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot", - "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", - "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", + "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", - "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", - "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", + "g1s6ujq0r200a0tfqgnjfflz7ddetsnrwvzxxx74=9000000000000000000ugnot", + "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", "g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3=9000000000000000000ugnot", + "g1563d3cw0gdv68le6azw2s63xm0jx9xvgpmfatq=9000000000000000000ugnot", + "g13csus64lj8twrn266837l9e77dusk2zwa794qz=9000000000000000000ugnot", + "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", + "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", + "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", + "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", + "g1xja7p9s3ly3tvkgks9x0n6f6yau2hnzl4x8x3d=9000000000000000000ugnot", + "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", + "g1njagaeg7e398hze39ygfgvc4gwsh6lkz7dwnuz=9000000000000000000ugnot", + "g1d7set9rsdw7ggt9elvzerep4jz80a4hu4crmzg=9000000000000000000ugnot", + "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", + "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", + "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", - "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", - "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", - "g1s6ujq0r200a0tfqgnjfflz7ddetsnrwvzxxx74=9000000000000000000ugnot", "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", + "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", + "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", + "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", - "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", - "g1a5kdc6ykfykq9p6088sclnr7e63hj8y4nnr5gn=9000000000000000000ugnot", - "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", - "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", - "g13csus64lj8twrn266837l9e77dusk2zwa794qz=9000000000000000000ugnot", - "g1gadp0yq5djelxwv7h8g7wpdf7x5w3vuzwmqrne=9000000000000000000ugnot", - "g1cm7u2fqd7drwfcqtn7zsgrfcyvlkkukhhtrxxj=9000000000000000000ugnot", - "g1njagaeg7e398hze39ygfgvc4gwsh6lkz7dwnuz=9000000000000000000ugnot", + "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", + "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot", "g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot", - "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", - "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", - "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", - "g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot", - "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", - "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", - "g1d7set9rsdw7ggt9elvzerep4jz80a4hu4crmzg=9000000000000000000ugnot" + "g16f5chytu99dmjqtekxf8qzg04vcv7dck6qny6d=9000000000000000000ugnot", + "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot" ], "txs": [ { diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index 5b7c31ca725..f425f22f4e5 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -1,3 +1,7 @@ +# Predeploy Accounts + +g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=103000000ugnot # Test1 (just enough for predeployment) + # Faucet Accounts (Core) g1njagaeg7e398hze39ygfgvc4gwsh6lkz7dwnuz=9000000000000000000ugnot # Faucet #0 From 5169299909e9cbdfb9eedf8c77c943d7a531bc16 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Tue, 9 Jul 2024 20:53:03 +0200 Subject: [PATCH 20/22] Remove leftover TODO --- misc/deployments/test4.gno.land/genesis_balances.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index f425f22f4e5..3e80f31e925 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -34,7 +34,6 @@ g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot # Ray g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot # Maxwell g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot # Dylan g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot # Morgan -# XXX=9000000000000000000ugnot # Kristov # TODO add g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot # Sergio # DevRel @@ -47,7 +46,6 @@ g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot # Leon g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot # Ilker g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot # Jerónimo g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot # Denis -# XXX=9000000000000000000ugnot # Lucio # TODO add g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot # Danny # XXX=9000000000000000000ugnot # Alan g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot # Salvo From 1f3115c84920c2088d53e0c7f866e315cab17b71 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Tue, 9 Jul 2024 22:49:15 +0200 Subject: [PATCH 21/22] Drop gno.mods from genesis txs --- misc/deployments/test4.gno.land/genesis.json | 412 ------------------- 1 file changed, 412 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index fc08f3734d1..1523fd3b7c6 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -135,10 +135,6 @@ "name": "bank", "path": "gno.land/p/demo/bank", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/bank\n" - }, { "name": "types.gno", "body": "// TODO: this is an example, and needs to be fixed up and tested.\n\npackage bank\n\n// NOTE: unexposed struct for security.\ntype order struct {\n\tfrom Address\n\tto Address\n\tamount Coins\n\tprocessed bool\n}\n\n// NOTE: unexposed methods for security.\nfunc (ch *order) string() string {\n\treturn \"TODO\"\n}\n\n// Wraps the internal *order for external use.\ntype Order struct {\n\t*order\n}\n\n// XXX only exposed for demonstration. TODO unexpose, make full demo.\nfunc NewOrder(from Address, to Address, amount Coins) Order {\n\treturn Order{\n\t\torder: \u0026order{\n\t\t\tfrom: from,\n\t\t\tto: to,\n\t\t\tamount: amount,\n\t\t},\n\t}\n}\n\n// Panics if error, or already processed.\nfunc (o Order) Execute() {\n\tif o.order.processed {\n\t\tpanic(\"order already processed\")\n\t}\n\to.order.processed = true\n\t// TODO implemement.\n}\n\nfunc (o Order) IsZero() bool {\n\treturn o.order == nil\n}\n\nfunc (o Order) From() Address {\n\treturn o.order.from\n}\n\nfunc (o Order) To() Address {\n\treturn o.order.to\n}\n\nfunc (o Order) Amount() Coins {\n\treturn o.order.amount\n}\n\nfunc (o Order) Processed() bool {\n\treturn o.order.processed\n}\n\n//----------------------------------------\n// Escrow\n\ntype EscrowTerms struct {\n\tPartyA Address\n\tPartyB Address\n\tAmountA Coins\n\tAmountB Coins\n}\n\ntype EscrowContract struct {\n\tEscrowTerms\n\tOrderA Order\n\tOrderB Order\n}\n\nfunc CreateEscrow(terms EscrowTerms) *EscrowContract {\n\treturn \u0026EscrowContract{\n\t\tEscrowTerms: terms,\n\t}\n}\n\nfunc (esc *EscrowContract) SetOrderA(order Order) {\n\tif !esc.OrderA.IsZero() {\n\t\tpanic(\"order-a already set\")\n\t}\n\tif esc.EscrowTerms.PartyA != order.From() {\n\t\tpanic(\"invalid order-a:from mismatch\")\n\t}\n\tif esc.EscrowTerms.PartyB != order.To() {\n\t\tpanic(\"invalid order-a:to mismatch\")\n\t}\n\tif !esc.EscrowTerms.AmountA.Equal(order.Amount()) {\n\t\tpanic(\"invalid order-a amount\")\n\t}\n\tesc.OrderA = order\n}\n\nfunc (esc *EscrowContract) SetOrderB(order Order) {\n\tif !esc.OrderB.IsZero() {\n\t\tpanic(\"order-b already set\")\n\t}\n\tif esc.EscrowTerms.PartyB != order.From() {\n\t\tpanic(\"invalid order-b:from mismatch\")\n\t}\n\tif esc.EscrowTerms.PartyA != order.To() {\n\t\tpanic(\"invalid order-b:to mismatch\")\n\t}\n\tif !esc.EscrowTerms.AmountB.Equal(order.Amount()) {\n\t\tpanic(\"invalid order-b amount\")\n\t}\n\tesc.OrderA = order\n}\n\nfunc (esc *EscrowContract) Execute() {\n\tif esc.OrderA.IsZero() {\n\t\tpanic(\"order-a not yet set\")\n\t}\n\tif esc.OrderB.IsZero() {\n\t\tpanic(\"order-b not yet set\")\n\t}\n\t// NOTE: succeeds atomically.\n\tesc.OrderA.Execute()\n\tesc.OrderB.Execute()\n}\n\n//----------------------------------------\n// TODO: actually implement these in std package.\n\ntype (\n\tAddress string\n\tCoins []Coin\n\tCoin struct {\n\t\tDenom bool\n\t\tAmount int64\n\t}\n)\n\nfunc (a Coins) Equal(b Coins) bool {\n\tif len(a) != len(b) {\n\t\treturn false\n\t}\n\tfor i, v := range a {\n\t\tif v != b[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n" @@ -169,10 +165,6 @@ "name": "avl", "path": "gno.land/p/demo/avl", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/avl\n" - }, { "name": "node.gno", "body": "package avl\n\n//----------------------------------------\n// Node\n\n// Node represents a node in an AVL tree.\ntype Node struct {\n\tkey string // key is the unique identifier for the node.\n\tvalue interface{} // value is the data stored in the node.\n\theight int8 // height is the height of the node in the tree.\n\tsize int // size is the number of nodes in the subtree rooted at this node.\n\tleftNode *Node // leftNode is the left child of the node.\n\trightNode *Node // rightNode is the right child of the node.\n}\n\n// NewNode creates a new node with the given key and value.\nfunc NewNode(key string, value interface{}) *Node {\n\treturn \u0026Node{\n\t\tkey: key,\n\t\tvalue: value,\n\t\theight: 0,\n\t\tsize: 1,\n\t}\n}\n\n// Size returns the size of the subtree rooted at the node.\nfunc (node *Node) Size() int {\n\tif node == nil {\n\t\treturn 0\n\t}\n\treturn node.size\n}\n\n// IsLeaf checks if the node is a leaf node (has no children).\nfunc (node *Node) IsLeaf() bool {\n\treturn node.height == 0\n}\n\n// Key returns the key of the node.\nfunc (node *Node) Key() string {\n\treturn node.key\n}\n\n// Value returns the value of the node.\nfunc (node *Node) Value() interface{} {\n\treturn node.value\n}\n\n// _copy creates a copy of the node (excluding value).\nfunc (node *Node) _copy() *Node {\n\tif node.height == 0 {\n\t\tpanic(\"Why are you copying a value node?\")\n\t}\n\treturn \u0026Node{\n\t\tkey: node.key,\n\t\theight: node.height,\n\t\tsize: node.size,\n\t\tleftNode: node.leftNode,\n\t\trightNode: node.rightNode,\n\t}\n}\n\n// Has checks if a node with the given key exists in the subtree rooted at the node.\nfunc (node *Node) Has(key string) (has bool) {\n\tif node == nil {\n\t\treturn false\n\t}\n\tif node.key == key {\n\t\treturn true\n\t}\n\tif node.height == 0 {\n\t\treturn false\n\t}\n\tif key \u003c node.key {\n\t\treturn node.getLeftNode().Has(key)\n\t}\n\treturn node.getRightNode().Has(key)\n}\n\n// Get searches for a node with the given key in the subtree rooted at the node\n// and returns its index, value, and whether it exists.\nfunc (node *Node) Get(key string) (index int, value interface{}, exists bool) {\n\tif node == nil {\n\t\treturn 0, nil, false\n\t}\n\n\tif node.height == 0 {\n\t\tif node.key == key {\n\t\t\treturn 0, node.value, true\n\t\t}\n\t\tif node.key \u003c key {\n\t\t\treturn 1, nil, false\n\t\t}\n\t\treturn 0, nil, false\n\t}\n\n\tif key \u003c node.key {\n\t\treturn node.getLeftNode().Get(key)\n\t}\n\n\trightNode := node.getRightNode()\n\tindex, value, exists = rightNode.Get(key)\n\tindex += node.size - rightNode.size\n\treturn index, value, exists\n}\n\n// GetByIndex retrieves the key-value pair of the node at the given index\n// in the subtree rooted at the node.\nfunc (node *Node) GetByIndex(index int) (key string, value interface{}) {\n\tif node.height == 0 {\n\t\tif index == 0 {\n\t\t\treturn node.key, node.value\n\t\t}\n\t\tpanic(\"GetByIndex asked for invalid index\")\n\t}\n\t// TODO: could improve this by storing the sizes\n\tleftNode := node.getLeftNode()\n\tif index \u003c leftNode.size {\n\t\treturn leftNode.GetByIndex(index)\n\t}\n\treturn node.getRightNode().GetByIndex(index - leftNode.size)\n}\n\n// Set inserts a new node with the given key-value pair into the subtree rooted at the node,\n// and returns the new root of the subtree and whether an existing node was updated.\n//\n// XXX consider a better way to do this... perhaps split Node from Node.\nfunc (node *Node) Set(key string, value interface{}) (newSelf *Node, updated bool) {\n\tif node == nil {\n\t\treturn NewNode(key, value), false\n\t}\n\n\tif node.height == 0 {\n\t\treturn node.setLeaf(key, value)\n\t}\n\n\tnode = node._copy()\n\tif key \u003c node.key {\n\t\tnode.leftNode, updated = node.getLeftNode().Set(key, value)\n\t} else {\n\t\tnode.rightNode, updated = node.getRightNode().Set(key, value)\n\t}\n\n\tif updated {\n\t\treturn node, updated\n\t}\n\n\tnode.calcHeightAndSize()\n\treturn node.balance(), updated\n}\n\n// setLeaf inserts a new leaf node with the given key-value pair into the subtree rooted at the node,\n// and returns the new root of the subtree and whether an existing node was updated.\nfunc (node *Node) setLeaf(key string, value interface{}) (newSelf *Node, updated bool) {\n\tif key == node.key {\n\t\treturn NewNode(key, value), true\n\t}\n\n\tif key \u003c node.key {\n\t\treturn \u0026Node{\n\t\t\tkey: node.key,\n\t\t\theight: 1,\n\t\t\tsize: 2,\n\t\t\tleftNode: NewNode(key, value),\n\t\t\trightNode: node,\n\t\t}, false\n\t}\n\n\treturn \u0026Node{\n\t\tkey: key,\n\t\theight: 1,\n\t\tsize: 2,\n\t\tleftNode: node,\n\t\trightNode: NewNode(key, value),\n\t}, false\n}\n\n// Remove deletes the node with the given key from the subtree rooted at the node.\n// returns the new root of the subtree, the new leftmost leaf key (if changed),\n// the removed value and the removal was successful.\nfunc (node *Node) Remove(key string) (\n\tnewNode *Node, newKey string, value interface{}, removed bool,\n) {\n\tif node == nil {\n\t\treturn nil, \"\", nil, false\n\t}\n\tif node.height == 0 {\n\t\tif key == node.key {\n\t\t\treturn nil, \"\", node.value, true\n\t\t}\n\t\treturn node, \"\", nil, false\n\t}\n\tif key \u003c node.key {\n\t\tvar newLeftNode *Node\n\t\tnewLeftNode, newKey, value, removed = node.getLeftNode().Remove(key)\n\t\tif !removed {\n\t\t\treturn node, \"\", value, false\n\t\t}\n\t\tif newLeftNode == nil { // left node held value, was removed\n\t\t\treturn node.rightNode, node.key, value, true\n\t\t}\n\t\tnode = node._copy()\n\t\tnode.leftNode = newLeftNode\n\t\tnode.calcHeightAndSize()\n\t\tnode = node.balance()\n\t\treturn node, newKey, value, true\n\t}\n\n\tvar newRightNode *Node\n\tnewRightNode, newKey, value, removed = node.getRightNode().Remove(key)\n\tif !removed {\n\t\treturn node, \"\", value, false\n\t}\n\tif newRightNode == nil { // right node held value, was removed\n\t\treturn node.leftNode, \"\", value, true\n\t}\n\tnode = node._copy()\n\tnode.rightNode = newRightNode\n\tif newKey != \"\" {\n\t\tnode.key = newKey\n\t}\n\tnode.calcHeightAndSize()\n\tnode = node.balance()\n\treturn node, \"\", value, true\n}\n\n// getLeftNode returns the left child of the node.\nfunc (node *Node) getLeftNode() *Node {\n\treturn node.leftNode\n}\n\n// getRightNode returns the right child of the node.\nfunc (node *Node) getRightNode() *Node {\n\treturn node.rightNode\n}\n\n// rotateRight performs a right rotation on the node and returns the new root.\n// NOTE: overwrites node\n// TODO: optimize balance \u0026 rotate\nfunc (node *Node) rotateRight() *Node {\n\tnode = node._copy()\n\tl := node.getLeftNode()\n\t_l := l._copy()\n\n\t_lrCached := _l.rightNode\n\t_l.rightNode = node\n\tnode.leftNode = _lrCached\n\n\tnode.calcHeightAndSize()\n\t_l.calcHeightAndSize()\n\n\treturn _l\n}\n\n// rotateLeft performs a left rotation on the node and returns the new root.\n// NOTE: overwrites node\n// TODO: optimize balance \u0026 rotate\nfunc (node *Node) rotateLeft() *Node {\n\tnode = node._copy()\n\tr := node.getRightNode()\n\t_r := r._copy()\n\n\t_rlCached := _r.leftNode\n\t_r.leftNode = node\n\tnode.rightNode = _rlCached\n\n\tnode.calcHeightAndSize()\n\t_r.calcHeightAndSize()\n\n\treturn _r\n}\n\n// calcHeightAndSize updates the height and size of the node based on its children.\n// NOTE: mutates height and size\nfunc (node *Node) calcHeightAndSize() {\n\tnode.height = maxInt8(node.getLeftNode().height, node.getRightNode().height) + 1\n\tnode.size = node.getLeftNode().size + node.getRightNode().size\n}\n\n// calcBalance calculates the balance factor of the node.\nfunc (node *Node) calcBalance() int {\n\treturn int(node.getLeftNode().height) - int(node.getRightNode().height)\n}\n\n// balance balances the subtree rooted at the node and returns the new root.\n// NOTE: assumes that node can be modified\n// TODO: optimize balance \u0026 rotate\nfunc (node *Node) balance() (newSelf *Node) {\n\tbalance := node.calcBalance()\n\tif balance \u003e= -1 {\n\t\treturn node\n\t}\n\tif balance \u003e 1 {\n\t\tif node.getLeftNode().calcBalance() \u003e= 0 {\n\t\t\t// Left Left Case\n\t\t\treturn node.rotateRight()\n\t\t}\n\t\t// Left Right Case\n\t\tleft := node.getLeftNode()\n\t\tnode.leftNode = left.rotateLeft()\n\t\treturn node.rotateRight()\n\t}\n\n\tif node.getRightNode().calcBalance() \u003c= 0 {\n\t\t// Right Right Case\n\t\treturn node.rotateLeft()\n\t}\n\n\t// Right Left Case\n\tright := node.getRightNode()\n\tnode.rightNode = right.rotateRight()\n\treturn node.rotateLeft()\n}\n\n// Shortcut for TraverseInRange.\nfunc (node *Node) Iterate(start, end string, cb func(*Node) bool) bool {\n\treturn node.TraverseInRange(start, end, true, true, cb)\n}\n\n// Shortcut for TraverseInRange.\nfunc (node *Node) ReverseIterate(start, end string, cb func(*Node) bool) bool {\n\treturn node.TraverseInRange(start, end, false, true, cb)\n}\n\n// TraverseInRange traverses all nodes, including inner nodes.\n// Start is inclusive and end is exclusive when ascending,\n// Start and end are inclusive when descending.\n// Empty start and empty end denote no start and no end.\n// If leavesOnly is true, only visit leaf nodes.\n// NOTE: To simulate an exclusive reverse traversal,\n// just append 0x00 to start.\nfunc (node *Node) TraverseInRange(start, end string, ascending bool, leavesOnly bool, cb func(*Node) bool) bool {\n\tif node == nil {\n\t\treturn false\n\t}\n\tafterStart := (start == \"\" || start \u003c node.key)\n\tstartOrAfter := (start == \"\" || start \u003c= node.key)\n\tbeforeEnd := false\n\tif ascending {\n\t\tbeforeEnd = (end == \"\" || node.key \u003c end)\n\t} else {\n\t\tbeforeEnd = (end == \"\" || node.key \u003c= end)\n\t}\n\n\t// Run callback per inner/leaf node.\n\tstop := false\n\tif (!node.IsLeaf() \u0026\u0026 !leavesOnly) ||\n\t\t(node.IsLeaf() \u0026\u0026 startOrAfter \u0026\u0026 beforeEnd) {\n\t\tstop = cb(node)\n\t\tif stop {\n\t\t\treturn stop\n\t\t}\n\t}\n\tif node.IsLeaf() {\n\t\treturn stop\n\t}\n\n\tif ascending {\n\t\t// check lower nodes, then higher\n\t\tif afterStart {\n\t\t\tstop = node.getLeftNode().TraverseInRange(start, end, ascending, leavesOnly, cb)\n\t\t}\n\t\tif stop {\n\t\t\treturn stop\n\t\t}\n\t\tif beforeEnd {\n\t\t\tstop = node.getRightNode().TraverseInRange(start, end, ascending, leavesOnly, cb)\n\t\t}\n\t} else {\n\t\t// check the higher nodes first\n\t\tif beforeEnd {\n\t\t\tstop = node.getRightNode().TraverseInRange(start, end, ascending, leavesOnly, cb)\n\t\t}\n\t\tif stop {\n\t\t\treturn stop\n\t\t}\n\t\tif afterStart {\n\t\t\tstop = node.getLeftNode().TraverseInRange(start, end, ascending, leavesOnly, cb)\n\t\t}\n\t}\n\n\treturn stop\n}\n\n// TraverseByOffset traverses all nodes, including inner nodes.\n// A limit of math.MaxInt means no limit.\nfunc (node *Node) TraverseByOffset(offset, limit int, descending bool, leavesOnly bool, cb func(*Node) bool) bool {\n\tif node == nil {\n\t\treturn false\n\t}\n\n\t// fast paths. these happen only if TraverseByOffset is called directly on a leaf.\n\tif limit \u003c= 0 || offset \u003e= node.size {\n\t\treturn false\n\t}\n\tif node.IsLeaf() {\n\t\tif offset \u003e 0 {\n\t\t\treturn false\n\t\t}\n\t\treturn cb(node)\n\t}\n\n\t// go to the actual recursive function.\n\treturn node.traverseByOffset(offset, limit, descending, leavesOnly, cb)\n}\n\n// TraverseByOffset traverses the subtree rooted at the node by offset and limit,\n// in either ascending or descending order, and applies the callback function to each traversed node.\n// If leavesOnly is true, only leaf nodes are visited.\nfunc (node *Node) traverseByOffset(offset, limit int, descending bool, leavesOnly bool, cb func(*Node) bool) bool {\n\t// caller guarantees: offset \u003c node.size; limit \u003e 0.\n\tif !leavesOnly {\n\t\tif cb(node) {\n\t\t\treturn true\n\t\t}\n\t}\n\tfirst, second := node.getLeftNode(), node.getRightNode()\n\tif descending {\n\t\tfirst, second = second, first\n\t}\n\tif first.IsLeaf() {\n\t\t// either run or skip, based on offset\n\t\tif offset \u003e 0 {\n\t\t\toffset--\n\t\t} else {\n\t\t\tcb(first)\n\t\t\tlimit--\n\t\t\tif limit \u003c= 0 {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// possible cases:\n\t\t// 1 the offset given skips the first node entirely\n\t\t// 2 the offset skips none or part of the first node, but the limit requires some of the second node.\n\t\t// 3 the offset skips none or part of the first node, and the limit stops our search on the first node.\n\t\tif offset \u003e= first.size {\n\t\t\toffset -= first.size // 1\n\t\t} else {\n\t\t\tif first.traverseByOffset(offset, limit, descending, leavesOnly, cb) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// number of leaves which could actually be called from inside\n\t\t\tdelta := first.size - offset\n\t\t\toffset = 0\n\t\t\tif delta \u003e= limit {\n\t\t\t\treturn true // 3\n\t\t\t}\n\t\t\tlimit -= delta // 2\n\t\t}\n\t}\n\n\t// because of the caller guarantees and the way we handle the first node,\n\t// at this point we know that limit \u003e 0 and there must be some values in\n\t// this second node that we include.\n\n\t// =\u003e if the second node is a leaf, it has to be included.\n\tif second.IsLeaf() {\n\t\treturn cb(second)\n\t}\n\t// =\u003e if it is not a leaf, it will still be enough to recursively call this\n\t// function with the updated offset and limit\n\treturn second.traverseByOffset(offset, limit, descending, leavesOnly, cb)\n}\n\n// Only used in testing...\nfunc (node *Node) lmd() *Node {\n\tif node.height == 0 {\n\t\treturn node\n\t}\n\treturn node.getLeftNode().lmd()\n}\n\n// Only used in testing...\nfunc (node *Node) rmd() *Node {\n\tif node.height == 0 {\n\t\treturn node\n\t}\n\treturn node.getRightNode().rmd()\n}\n\nfunc maxInt8(a, b int8) int8 {\n\tif a \u003e b {\n\t\treturn a\n\t}\n\treturn b\n}\n" @@ -235,10 +227,6 @@ "name": "crypto.gno", "body": "package testutils\n\nimport \"std\"\n\nfunc TestAddress(name string) std.Address {\n\tif len(name) \u003e std.RawAddressSize {\n\t\tpanic(\"address name cannot be greater than std.AddressSize bytes\")\n\t}\n\taddr := std.RawAddress{}\n\t// TODO: use strings.RepeatString or similar.\n\t// NOTE: I miss python's \"\".Join().\n\tblanks := \"____________________\"\n\tcopy(addr[:], []byte(blanks))\n\tcopy(addr[:], []byte(name))\n\treturn std.Address(std.EncodeBech32(\"g\", addr))\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/testutils\n" - }, { "name": "misc.gno", "body": "package testutils\n\n// For testing std.GetCallerAt().\nfunc WrapCall(fn func()) {\n\tfn()\n}\n" @@ -281,10 +269,6 @@ "name": "const.gno", "body": "package acl\n\nconst Everyone string = \"everyone\"\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/acl\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n)\n" - }, { "name": "perm.gno", "body": "package acl\n\nimport \"regexp\"\n\ntype perm struct {\n\tverbs []string\n\tresources []string\n}\n\nfunc (perm perm) hasPerm(verb, resource string) bool {\n\t// check verb\n\tverbOK := false\n\tfor _, pattern := range perm.verbs {\n\t\tif match(pattern, verb) {\n\t\t\tverbOK = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !verbOK {\n\t\treturn false\n\t}\n\n\t// check resource\n\tfor _, pattern := range perm.resources {\n\t\tif match(pattern, resource) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc match(pattern, target string) bool {\n\tif pattern == \".*\" {\n\t\treturn true\n\t}\n\n\tif pattern == target {\n\t\treturn true\n\t}\n\n\t// regexp handling\n\tmatch, _ := regexp.MatchString(pattern, target)\n\treturn match\n}\n" @@ -331,10 +315,6 @@ "name": "doc.gno", "body": "// Package bf implements a minimalist Brainfuck virtual machine in Gno.\n//\n// Brainfuck is an esoteric programming language known for its simplicity and minimalistic design.\n// It operates on an array of memory cells, with a memory pointer that can move left or right.\n// The language consists of eight commands: \u003e \u003c + - . , [ ].\n//\n// Usage:\n// To execute Brainfuck code, use the Execute function and provide the code as a string.\n//\n//\tcode := \"++++++++++[\u003e+++++++\u003e++++++++++\u003e+++\u003e+\u003c\u003c\u003c\u003c-]\u003e++.\u003e+.+++++++..+++.\u003e++.\u003c\u003c+++++++++++++++.\u003e.+++.------.--------.\"\n//\toutput := bf.Execute(code)\n//\n// Note:\n// This implementation is a minimalist version and may not handle all edge cases or advanced features of the Brainfuck language.\n//\n// Reference:\n// For more information on Brainfuck, refer to the Wikipedia page: https://en.wikipedia.org/wiki/Brainfuck\npackage bf // import \"gno.land/p/demo/bf\"\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/bf\n" - }, { "name": "run.gno", "body": "package bf\n\n// for `gno run`\nfunc main() {\n\tcode := \"++++++++++[\u003e+++++++\u003e++++++++++\u003e+++\u003e+\u003c\u003c\u003c\u003c-]\u003e++.\u003e+.+++++++..+++.\u003e++.\u003c\u003c+++++++++++++++.\u003e.+++.------.--------.\"\n\t// TODO: code = os.Args...\n\tExecute(code)\n}\n" @@ -369,10 +349,6 @@ "name": "doc.gno", "body": "// Package mux provides a simple routing and rendering library for handling dynamic path-based requests in Gno contracts.\n//\n// The `mux` package aims to offer similar functionality to `http.ServeMux` in Go, but for Gno's Render() requests.\n// It allows you to define routes with dynamic parts and associate them with corresponding handler functions for rendering outputs.\n//\n// Usage:\n// 1. Create a new Router instance using `NewRouter()` to handle routing and rendering logic.\n// 2. Register routes and their associated handler functions using the `Handle(route, handler)` method.\n// 3. Implement the rendering logic within the handler functions, utilizing the `Request` and `ResponseWriter` types.\n// 4. Use the `Render(path)` method to process a given path and execute the corresponding handler function to obtain the rendered output.\n//\n// Route Patterns:\n// Routes can include dynamic parts enclosed in braces, such as \"users/{id}\" or \"hello/{name}\". The `Request` object's `GetVar(key)`\n// method allows you to extract the value of a specific variable from the path based on routing rules.\n//\n// Example:\n//\n//\trouter := mux.NewRouter()\n//\n//\t// Define a route with a variable and associated handler function\n//\trouter.HandleFunc(\"hello/{name}\", func(res *mux.ResponseWriter, req *mux.Request) {\n//\t\tname := req.GetVar(\"name\")\n//\t\tif name != \"\" {\n//\t\t\tres.Write(\"Hello, \" + name + \"!\")\n//\t\t} else {\n//\t\t\tres.Write(\"Hello, world!\")\n//\t\t}\n//\t})\n//\n//\t// Render the output for the \"/hello/Alice\" path\n//\toutput := router.Render(\"hello/Alice\")\n//\t// Output: \"Hello, Alice!\"\n//\n// Note: The `mux` package provides a basic routing and rendering mechanism for simple use cases. For more advanced routing features,\n// consider using more specialized libraries or frameworks.\npackage mux\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/mux\n" - }, { "name": "handler.gno", "body": "package mux\n\ntype Handler struct {\n\tPattern string\n\tFn HandlerFunc\n}\n\ntype HandlerFunc func(*ResponseWriter, *Request)\n\n// TODO: type ErrHandlerFunc func(*ResponseWriter, *Request) error\n// TODO: NotFoundHandler\n// TODO: AutomaticIndex\n" @@ -427,10 +403,6 @@ "name": "ufmt", "path": "gno.land/p/demo/ufmt", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/ufmt\n" - }, { "name": "ufmt.gno", "body": "// Package ufmt provides utility functions for formatting strings, similarly\n// to the Go package \"fmt\", of which only a subset is currently supported\n// (hence the name µfmt - micro fmt).\npackage ufmt\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Println formats using the default formats for its operands and writes to standard output.\n// Println writes the given arguments to standard output with spaces between arguments\n// and a newline at the end.\nfunc Println(args ...interface{}) {\n\tvar strs []string\n\tfor _, arg := range args {\n\t\tswitch v := arg.(type) {\n\t\tcase string:\n\t\t\tstrs = append(strs, v)\n\t\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:\n\t\t\tstrs = append(strs, Sprintf(\"%d\", v))\n\t\tcase bool:\n\t\t\tif v {\n\t\t\t\tstrs = append(strs, \"true\")\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tstrs = append(strs, \"false\")\n\t\tdefault:\n\t\t\tstrs = append(strs, \"(unhandled)\")\n\t\t}\n\t}\n\n\t// TODO: remove println after gno supports os.Stdout\n\tprintln(strings.Join(strs, \" \"))\n}\n\n// Sprintf offers similar functionality to Go's fmt.Sprintf, or the sprintf\n// equivalent available in many languages, including C/C++.\n// The number of args passed must exactly match the arguments consumed by the format.\n// A limited number of formatting verbs and features are currently supported,\n// hence the name ufmt (µfmt, micro-fmt).\n//\n// The currently formatted verbs are the following:\n//\n//\t%s: places a string value directly.\n//\t If the value implements the interface interface{ String() string },\n//\t the String() method is called to retrieve the value.\n//\t%d: formats an integer value using package \"strconv\".\n//\t Currently supports only uint, uint64, int, int64.\n//\t%t: formats a boolean value to \"true\" or \"false\".\n//\t%%: outputs a literal %. Does not consume an argument.\nfunc Sprintf(format string, args ...interface{}) string {\n\t// we use runes to handle multi-byte characters\n\tsTor := []rune(format)\n\tend := len(sTor)\n\targNum := 0\n\targLen := len(args)\n\tbuf := \"\"\n\n\tfor i := 0; i \u003c end; {\n\t\tisLast := i == end-1\n\t\tc := string(sTor[i])\n\n\t\tif isLast || c != \"%\" {\n\t\t\t// we don't check for invalid format like a one ending with \"%\"\n\t\t\tbuf += string(c)\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\n\t\tverb := string(sTor[i+1])\n\t\tif verb == \"%\" {\n\t\t\tbuf += \"%\"\n\t\t\ti += 2\n\t\t\tcontinue\n\t\t}\n\n\t\tif argNum \u003e argLen {\n\t\t\tpanic(\"invalid number of arguments to ufmt.Sprintf\")\n\t\t}\n\t\targ := args[argNum]\n\t\targNum++\n\n\t\tswitch verb {\n\t\tcase \"s\":\n\t\t\tswitch v := arg.(type) {\n\t\t\tcase interface{ String() string }:\n\t\t\t\tbuf += v.String()\n\t\t\tcase string:\n\t\t\t\tbuf += v\n\t\t\tdefault:\n\t\t\t\tbuf += \"(unhandled)\"\n\t\t\t}\n\t\tcase \"d\":\n\t\t\tswitch v := arg.(type) {\n\t\t\tcase int:\n\t\t\t\tbuf += strconv.Itoa(v)\n\t\t\tcase int8:\n\t\t\t\tbuf += strconv.Itoa(int(v))\n\t\t\tcase int16:\n\t\t\t\tbuf += strconv.Itoa(int(v))\n\t\t\tcase int32:\n\t\t\t\tbuf += strconv.Itoa(int(v))\n\t\t\tcase int64:\n\t\t\t\tbuf += strconv.Itoa(int(v))\n\t\t\tcase uint:\n\t\t\t\tbuf += strconv.FormatUint(uint64(v), 10)\n\t\t\tcase uint8:\n\t\t\t\tbuf += strconv.FormatUint(uint64(v), 10)\n\t\t\tcase uint16:\n\t\t\t\tbuf += strconv.FormatUint(uint64(v), 10)\n\t\t\tcase uint32:\n\t\t\t\tbuf += strconv.FormatUint(uint64(v), 10)\n\t\t\tcase uint64:\n\t\t\t\tbuf += strconv.FormatUint(v, 10)\n\t\t\tdefault:\n\t\t\t\tbuf += \"(unhandled)\"\n\t\t\t}\n\t\tcase \"t\":\n\t\t\tswitch v := arg.(type) {\n\t\t\tcase bool:\n\t\t\t\tif v {\n\t\t\t\t\tbuf += \"true\"\n\t\t\t\t} else {\n\t\t\t\t\tbuf += \"false\"\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbuf += \"(unhandled)\"\n\t\t\t}\n\t\t// % handled before, as it does not consume an argument\n\t\tdefault:\n\t\t\tbuf += \"(unhandled)\"\n\t\t}\n\n\t\ti += 2\n\t}\n\tif argNum \u003c argLen {\n\t\tpanic(\"too many arguments to ufmt.Sprintf\")\n\t}\n\treturn buf\n}\n\n// errMsg implements the error interface.\ntype errMsg struct {\n\tmsg string\n}\n\n// Error defines the requirements of the error interface.\n// It functions similarly to Go's errors.New()\nfunc (e *errMsg) Error() string {\n\treturn e.msg\n}\n\n// Errorf is a function that mirrors the functionality of fmt.Errorf.\n//\n// It takes a format string and arguments to create a formatted string,\n// then sets this string as the 'msg' field of an errMsg struct and returns a pointer to this struct.\n//\n// This function operates in a similar manner to Go's fmt.Errorf,\n// providing a way to create formatted error messages.\n//\n// The currently formatted verbs are the following:\n//\n//\t%s: places a string value directly.\n//\t If the value implements the interface interface{ String() string },\n//\t the String() method is called to retrieve the value.\n//\t%d: formats an integer value using package \"strconv\".\n//\t Currently supports only uint, uint64, int, int64.\n//\t%t: formats a boolean value to \"true\" or \"false\".\n//\t%%: outputs a literal %. Does not consume an argument.\nfunc Errorf(format string, args ...interface{}) error {\n\treturn \u0026errMsg{Sprintf(format, args...)}\n}\n" @@ -477,10 +449,6 @@ "name": "errors.gno", "body": "package blog\n\nimport \"errors\"\n\nvar (\n\tErrPostTitleMissing = errors.New(\"post title is missing\")\n\tErrPostSlugMissing = errors.New(\"post slug is missing\")\n\tErrPostBodyMissing = errors.New(\"post body is missing\")\n\tErrPostSlugExists = errors.New(\"post with specified slug already exists\")\n\tErrPostPubDateExists = errors.New(\"post with specified publication date exists\")\n\tErrPostTitleExists = errors.New(\"post with specified title already exists\")\n\tErrNoSuchPost = errors.New(\"no such post\")\n)\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/blog\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/mux v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "util.gno", "body": "package blog\n\nimport (\n\t\"strings\"\n\t\"time\"\n)\n\nfunc breadcrumb(parts []string) string {\n\treturn \"# \" + strings.Join(parts, \" / \") + \"\\n\\n\"\n}\n\nfunc getTitleKey(title string) string {\n\treturn strings.Replace(title, \" \", \"\", -1)\n}\n\nfunc getPublishedKey(t time.Time) string {\n\treturn t.Format(time.RFC3339)\n}\n" @@ -526,10 +494,6 @@ { "name": "cford32_test.gno", "body": "package cford32\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestCompactRoundtrip(t *testing.T) {\n\tbuf := make([]byte, 13)\n\tprev := make([]byte, 13)\n\tfor i := uint64(0); i \u003c (1 \u003c\u003c 12); i++ {\n\t\tres := AppendCompact(i, buf[:0])\n\t\tback, err := Uint64(res)\n\t\ttestEqual(t, \"Uint64(%q) = (%d, %v), want %v\", string(res), back, err, nil)\n\t\ttestEqual(t, \"Uint64(%q) = %d, want %v\", string(res), back, i)\n\n\t\ttestEqual(t, \"bytes.Compare(prev, res) = %d, want %d\", bytes.Compare(prev, res), -1)\n\t\tprev, buf = res, prev\n\t}\n\tfor i := uint64(1\u003c\u003c34 - 1024); i \u003c (1\u003c\u003c34 + 1024); i++ {\n\t\tres := AppendCompact(i, buf[:0])\n\t\tback, err := Uint64(res)\n\t\t// println(string(res))\n\t\ttestEqual(t, \"Uint64(%q) = (%d, %v), want %v\", string(res), back, err, nil)\n\t\ttestEqual(t, \"Uint64(%q) = %d, want %v\", string(res), back, i)\n\n\t\ttestEqual(t, \"bytes.Compare(prev, res) = %d, want %d\", bytes.Compare(prev, res), -1)\n\t\tprev, buf = res, prev\n\t}\n\tfor i := uint64(1\u003c\u003c64 - 5000); i != 0; i++ {\n\t\tres := AppendCompact(i, buf[:0])\n\t\tback, err := Uint64(res)\n\t\ttestEqual(t, \"Uint64(%q) = (%d, %v), want %v\", string(res), back, err, nil)\n\t\ttestEqual(t, \"Uint64(%q) = %d, want %v\", string(res), back, i)\n\n\t\ttestEqual(t, \"bytes.Compare(prev, res) = %d, want %d\", bytes.Compare(prev, res), -1)\n\t\tprev, buf = res, prev\n\t}\n}\n\nfunc BenchmarkCompact(b *testing.B) {\n\tbuf := make([]byte, 13)\n\tfor i := 0; i \u003c b.N; i++ {\n\t\t_ = AppendCompact(uint64(i), buf[:0])\n\t}\n}\n\ntype testpair struct {\n\tdecoded, encoded string\n}\n\nvar pairs = []testpair{\n\t{\"\", \"\"},\n\t{\"f\", \"CR\"},\n\t{\"fo\", \"CSQG\"},\n\t{\"foo\", \"CSQPY\"},\n\t{\"foob\", \"CSQPYRG\"},\n\t{\"fooba\", \"CSQPYRK1\"},\n\t{\"foobar\", \"CSQPYRK1E8\"},\n\n\t{\"sure.\", \"EDTQ4S9E\"},\n\t{\"sure\", \"EDTQ4S8\"},\n\t{\"sur\", \"EDTQ4\"},\n\t{\"su\", \"EDTG\"},\n\t{\"leasure.\", \"DHJP2WVNE9JJW\"},\n\t{\"easure.\", \"CNGQ6XBJCMQ0\"},\n\t{\"asure.\", \"C5SQAWK55R\"},\n}\n\nvar bigtest = testpair{\n\t\"Twas brillig, and the slithy toves\",\n\t\"AHVP2WS0C9S6JV3CD5KJR831DSJ20X38CMG76V39EHM7J83MDXV6AWR\",\n}\n\nfunc testEqual(t *testing.T, msg string, args ...interface{}) bool {\n\tt.Helper()\n\tif args[len(args)-2] != args[len(args)-1] {\n\t\tt.Errorf(msg, args...)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc TestEncode(t *testing.T) {\n\tfor _, p := range pairs {\n\t\tgot := EncodeToString([]byte(p.decoded))\n\t\ttestEqual(t, \"Encode(%q) = %q, want %q\", p.decoded, got, p.encoded)\n\t\tdst := AppendEncode([]byte(\"lead\"), []byte(p.decoded))\n\t\ttestEqual(t, `AppendEncode(\"lead\", %q) = %q, want %q`, p.decoded, string(dst), \"lead\"+p.encoded)\n\t}\n}\n\nfunc TestEncoder(t *testing.T) {\n\tfor _, p := range pairs {\n\t\tbb := \u0026strings.Builder{}\n\t\tencoder := NewEncoder(bb)\n\t\tencoder.Write([]byte(p.decoded))\n\t\tencoder.Close()\n\t\ttestEqual(t, \"Encode(%q) = %q, want %q\", p.decoded, bb.String(), p.encoded)\n\t}\n}\n\nfunc TestEncoderBuffering(t *testing.T) {\n\tinput := []byte(bigtest.decoded)\n\tfor bs := 1; bs \u003c= 12; bs++ {\n\t\tbb := \u0026strings.Builder{}\n\t\tencoder := NewEncoder(bb)\n\t\tfor pos := 0; pos \u003c len(input); pos += bs {\n\t\t\tend := pos + bs\n\t\t\tif end \u003e len(input) {\n\t\t\t\tend = len(input)\n\t\t\t}\n\t\t\tn, err := encoder.Write(input[pos:end])\n\t\t\ttestEqual(t, \"Write(%q) gave error %v, want %v\", input[pos:end], err, error(nil))\n\t\t\ttestEqual(t, \"Write(%q) gave length %v, want %v\", input[pos:end], n, end-pos)\n\t\t}\n\t\terr := encoder.Close()\n\t\ttestEqual(t, \"Close gave error %v, want %v\", err, error(nil))\n\t\ttestEqual(t, \"Encoding/%d of %q = %q, want %q\", bs, bigtest.decoded, bb.String(), bigtest.encoded)\n\t}\n}\n\nfunc TestDecode(t *testing.T) {\n\tfor _, p := range pairs {\n\t\tdbuf := make([]byte, DecodedLen(len(p.encoded)))\n\t\tcount, err := decode(dbuf, []byte(p.encoded))\n\t\ttestEqual(t, \"Decode(%q) = error %v, want %v\", p.encoded, err, error(nil))\n\t\ttestEqual(t, \"Decode(%q) = length %v, want %v\", p.encoded, count, len(p.decoded))\n\t\ttestEqual(t, \"Decode(%q) = %q, want %q\", p.encoded, string(dbuf[0:count]), p.decoded)\n\n\t\tdbuf, err = DecodeString(p.encoded)\n\t\ttestEqual(t, \"DecodeString(%q) = error %v, want %v\", p.encoded, err, error(nil))\n\t\ttestEqual(t, \"DecodeString(%q) = %q, want %q\", p.encoded, string(dbuf), p.decoded)\n\n\t\t// XXX: https://github.com/gnolang/gno/issues/1570\n\t\tdst, err := AppendDecode(append([]byte(nil), []byte(\"lead\")...), []byte(p.encoded))\n\t\ttestEqual(t, \"AppendDecode(%q) = error %v, want %v\", p.encoded, err, error(nil))\n\t\ttestEqual(t, `AppendDecode(\"lead\", %q) = %q, want %q`, p.encoded, string(dst), \"lead\"+p.decoded)\n\n\t\tdst2, err := AppendDecode(dst[:0:len(p.decoded)], []byte(p.encoded))\n\t\ttestEqual(t, \"AppendDecode(%q) = error %v, want %v\", p.encoded, err, error(nil))\n\t\ttestEqual(t, `AppendDecode(\"\", %q) = %q, want %q`, p.encoded, string(dst2), p.decoded)\n\t\t// XXX: https://github.com/gnolang/gno/issues/1569\n\t\t// old used \u0026dst2[0] != \u0026dst[0] as a check.\n\t\tif len(dst) \u003e 0 \u0026\u0026 len(dst2) \u003e 0 \u0026\u0026 cap(dst2) != len(p.decoded) {\n\t\t\tt.Errorf(\"unexpected capacity growth: got %d, want %d\", cap(dst2), len(p.decoded))\n\t\t}\n\t}\n}\n\n// A minimal variation on strings.Reader.\n// Here, we return a io.EOF immediately on Read if the read has reached the end\n// of the reader. It's used to simplify TestDecoder.\ntype stringReader struct {\n\ts string\n\ti int64\n}\n\nfunc (r *stringReader) Read(b []byte) (n int, err error) {\n\tif r.i \u003e= int64(len(r.s)) {\n\t\treturn 0, io.EOF\n\t}\n\tn = copy(b, r.s[r.i:])\n\tr.i += int64(n)\n\tif r.i \u003e= int64(len(r.s)) {\n\t\treturn n, io.EOF\n\t}\n\treturn\n}\n\nfunc TestDecoder(t *testing.T) {\n\tfor _, p := range pairs {\n\t\tdecoder := NewDecoder(\u0026stringReader{p.encoded, 0})\n\t\tdbuf := make([]byte, DecodedLen(len(p.encoded)))\n\t\tcount, err := decoder.Read(dbuf)\n\t\tif err != nil \u0026\u0026 err != io.EOF {\n\t\t\tt.Fatal(\"Read failed\", err)\n\t\t}\n\t\ttestEqual(t, \"Read from %q = length %v, want %v\", p.encoded, count, len(p.decoded))\n\t\ttestEqual(t, \"Decoding of %q = %q, want %q\", p.encoded, string(dbuf[0:count]), p.decoded)\n\t\tif err != io.EOF {\n\t\t\t_, err = decoder.Read(dbuf)\n\t\t}\n\t\ttestEqual(t, \"Read from %q = %v, want %v\", p.encoded, err, io.EOF)\n\t}\n}\n\ntype badReader struct {\n\tdata []byte\n\terrs []error\n\tcalled int\n\tlimit int\n}\n\n// Populates p with data, returns a count of the bytes written and an\n// error. The error returned is taken from badReader.errs, with each\n// invocation of Read returning the next error in this slice, or io.EOF,\n// if all errors from the slice have already been returned. The\n// number of bytes returned is determined by the size of the input buffer\n// the test passes to decoder.Read and will be a multiple of 8, unless\n// badReader.limit is non zero.\nfunc (b *badReader) Read(p []byte) (int, error) {\n\tlim := len(p)\n\tif b.limit != 0 \u0026\u0026 b.limit \u003c lim {\n\t\tlim = b.limit\n\t}\n\tif len(b.data) \u003c lim {\n\t\tlim = len(b.data)\n\t}\n\tfor i := range p[:lim] {\n\t\tp[i] = b.data[i]\n\t}\n\tb.data = b.data[lim:]\n\terr := io.EOF\n\tif b.called \u003c len(b.errs) {\n\t\terr = b.errs[b.called]\n\t}\n\tb.called++\n\treturn lim, err\n}\n\n// TestIssue20044 tests that decoder.Read behaves correctly when the caller\n// supplied reader returns an error.\nfunc TestIssue20044(t *testing.T) {\n\tbadErr := errors.New(\"bad reader error\")\n\ttestCases := []struct {\n\t\tr badReader\n\t\tres string\n\t\terr error\n\t\tdbuflen int\n\t}{\n\t\t// Check valid input data accompanied by an error is processed and the error is propagated.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"d1jprv3fexqq4v34\"), errs: []error{badErr}},\n\t\t\tres: \"helloworld\", err: badErr,\n\t\t},\n\t\t// Check a read error accompanied by input data consisting of newlines only is propagated.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"\\n\\n\\n\\n\\n\\n\\n\\n\"), errs: []error{badErr, nil}},\n\t\t\tres: \"\", err: badErr,\n\t\t},\n\t\t// Reader will be called twice. The first time it will return 8 newline characters. The\n\t\t// second time valid base32 encoded data and an error. The data should be decoded\n\t\t// correctly and the error should be propagated.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"\\n\\n\\n\\n\\n\\n\\n\\nd1jprv3fexqq4v34\"), errs: []error{nil, badErr}},\n\t\t\tres: \"helloworld\", err: badErr, dbuflen: 8,\n\t\t},\n\t\t// Reader returns invalid input data (too short) and an error. Verify the reader\n\t\t// error is returned.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"c\"), errs: []error{badErr}},\n\t\t\tres: \"\", err: badErr,\n\t\t},\n\t\t// Reader returns invalid input data (too short) but no error. Verify io.ErrUnexpectedEOF\n\t\t// is returned.\n\t\t// NOTE(thehowl): I don't think this should applyto us?\n\t\t/* {\n\t\t\tr: badReader{data: []byte(\"c\"), errs: []error{nil}},\n\t\t\tres: \"\", err: io.ErrUnexpectedEOF,\n\t\t},*/\n\t\t// Reader returns invalid input data and an error. Verify the reader and not the\n\t\t// decoder error is returned.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"cu\"), errs: []error{badErr}},\n\t\t\tres: \"\", err: badErr,\n\t\t},\n\t\t// Reader returns valid data and io.EOF. Check data is decoded and io.EOF is propagated.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"csqpyrk1\"), errs: []error{io.EOF}},\n\t\t\tres: \"fooba\", err: io.EOF,\n\t\t},\n\t\t// Check errors are properly reported when decoder.Read is called multiple times.\n\t\t// decoder.Read will be called 8 times, badReader.Read will be called twice, returning\n\t\t// valid data both times but an error on the second call.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjwc9g\"), errs: []error{nil, badErr}},\n\t\t\tres: \"leasure.10\", err: badErr, dbuflen: 1,\n\t\t},\n\t\t// Check io.EOF is properly reported when decoder.Read is called multiple times.\n\t\t// decoder.Read will be called 8 times, badReader.Read will be called twice, returning\n\t\t// valid data both times but io.EOF on the second call.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjw\"), errs: []error{nil, io.EOF}},\n\t\t\tres: \"leasure.\", err: io.EOF, dbuflen: 1,\n\t\t},\n\t\t// The following two test cases check that errors are propagated correctly when more than\n\t\t// 8 bytes are read at a time.\n\t\t{\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjw\"), errs: []error{io.EOF}},\n\t\t\tres: \"leasure.\", err: io.EOF, dbuflen: 11,\n\t\t},\n\t\t{\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjwc9g\"), errs: []error{badErr}},\n\t\t\tres: \"leasure.10\", err: badErr, dbuflen: 11,\n\t\t},\n\t\t// Check that errors are correctly propagated when the reader returns valid bytes in\n\t\t// groups that are not divisible by 8. The first read will return 11 bytes and no\n\t\t// error. The second will return 7 and an error. The data should be decoded correctly\n\t\t// and the error should be propagated.\n\t\t// NOTE(thehowl): again, this is on the assumption that this is padded, and it's not.\n\t\t/* {\n\t\t\tr: badReader{data: []byte(\"dhjp2wvne9jjw\"), errs: []error{nil, badErr}, limit: 11},\n\t\t\tres: \"leasure.\", err: badErr,\n\t\t}, */\n\t}\n\n\tfor idx, tc := range testCases {\n\t\tt.Run(fmt.Sprintf(\"%d-%s\", idx, string(tc.res)), func(t *testing.T) {\n\t\t\tinput := tc.r.data\n\t\t\tdecoder := NewDecoder(\u0026tc.r)\n\t\t\tvar dbuflen int\n\t\t\tif tc.dbuflen \u003e 0 {\n\t\t\t\tdbuflen = tc.dbuflen\n\t\t\t} else {\n\t\t\t\tdbuflen = DecodedLen(len(input))\n\t\t\t}\n\t\t\tdbuf := make([]byte, dbuflen)\n\t\t\tvar err error\n\t\t\tvar res []byte\n\t\t\tfor err == nil {\n\t\t\t\tvar n int\n\t\t\t\tn, err = decoder.Read(dbuf)\n\t\t\t\tif n \u003e 0 {\n\t\t\t\t\tres = append(res, dbuf[:n]...)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttestEqual(t, \"Decoding of %q = %q, want %q\", string(input), string(res), tc.res)\n\t\t\ttestEqual(t, \"Decoding of %q err = %v, expected %v\", string(input), err, tc.err)\n\t\t})\n\t}\n}\n\n// TestDecoderError verifies decode errors are propagated when there are no read\n// errors.\nfunc TestDecoderError(t *testing.T) {\n\tfor _, readErr := range []error{io.EOF, nil} {\n\t\tinput := \"ucsqpyrk1u\"\n\t\tdbuf := make([]byte, DecodedLen(len(input)))\n\t\tbr := badReader{data: []byte(input), errs: []error{readErr}}\n\t\tdecoder := NewDecoder(\u0026br)\n\t\tn, err := decoder.Read(dbuf)\n\t\ttestEqual(t, \"Read after EOF, n = %d, expected %d\", n, 0)\n\t\tif _, ok := err.(CorruptInputError); !ok {\n\t\t\tt.Errorf(\"Corrupt input error expected. Found %T\", err)\n\t\t}\n\t}\n}\n\n// TestReaderEOF ensures decoder.Read behaves correctly when input data is\n// exhausted.\nfunc TestReaderEOF(t *testing.T) {\n\tfor _, readErr := range []error{io.EOF, nil} {\n\t\tinput := \"MZXW6YTB\"\n\t\tbr := badReader{data: []byte(input), errs: []error{nil, readErr}}\n\t\tdecoder := NewDecoder(\u0026br)\n\t\tdbuf := make([]byte, DecodedLen(len(input)))\n\t\tn, err := decoder.Read(dbuf)\n\t\ttestEqual(t, \"Decoding of %q err = %v, expected %v\", input, err, error(nil))\n\t\tn, err = decoder.Read(dbuf)\n\t\ttestEqual(t, \"Read after EOF, n = %d, expected %d\", n, 0)\n\t\ttestEqual(t, \"Read after EOF, err = %v, expected %v\", err, io.EOF)\n\t\tn, err = decoder.Read(dbuf)\n\t\ttestEqual(t, \"Read after EOF, n = %d, expected %d\", n, 0)\n\t\ttestEqual(t, \"Read after EOF, err = %v, expected %v\", err, io.EOF)\n\t}\n}\n\nfunc TestDecoderBuffering(t *testing.T) {\n\tfor bs := 1; bs \u003c= 12; bs++ {\n\t\tdecoder := NewDecoder(strings.NewReader(bigtest.encoded))\n\t\tbuf := make([]byte, len(bigtest.decoded)+12)\n\t\tvar total int\n\t\tvar n int\n\t\tvar err error\n\t\tfor total = 0; total \u003c len(bigtest.decoded) \u0026\u0026 err == nil; {\n\t\t\tn, err = decoder.Read(buf[total : total+bs])\n\t\t\ttotal += n\n\t\t}\n\t\tif err != nil \u0026\u0026 err != io.EOF {\n\t\t\tt.Errorf(\"Read from %q at pos %d = %d, unexpected error %v\", bigtest.encoded, total, n, err)\n\t\t}\n\t\ttestEqual(t, \"Decoding/%d of %q = %q, want %q\", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded)\n\t}\n}\n\nfunc TestDecodeCorrupt(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput string\n\t\toffset int // -1 means no corruption.\n\t}{\n\t\t{\"\", -1},\n\t\t{\"iIoOlL\", -1},\n\t\t{\"!!!!\", 0},\n\t\t{\"uxp10\", 0},\n\t\t{\"x===\", 1},\n\t\t{\"AA=A====\", 2},\n\t\t{\"AAA=AAAA\", 3},\n\t\t// Much fewer cases compared to Go as there are much fewer cases where input\n\t\t// can be \"corrupted\".\n\t}\n\tfor _, tc := range testCases {\n\t\tdbuf := make([]byte, DecodedLen(len(tc.input)))\n\t\t_, err := Decode(dbuf, []byte(tc.input))\n\t\tif tc.offset == -1 {\n\t\t\tif err != nil {\n\t\t\t\tt.Error(\"Decoder wrongly detected corruption in\", tc.input)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tswitch err := err.(type) {\n\t\tcase CorruptInputError:\n\t\t\ttestEqual(t, \"Corruption in %q at offset %v, want %v\", tc.input, int(err), tc.offset)\n\t\tdefault:\n\t\t\tt.Error(\"Decoder failed to detect corruption in\", tc)\n\t\t}\n\t}\n}\n\nfunc TestBig(t *testing.T) {\n\tn := 3*1000 + 1\n\traw := make([]byte, n)\n\tconst alpha = \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\tfor i := 0; i \u003c n; i++ {\n\t\traw[i] = alpha[i%len(alpha)]\n\t}\n\tencoded := new(bytes.Buffer)\n\tw := NewEncoder(encoded)\n\tnn, err := w.Write(raw)\n\tif nn != n || err != nil {\n\t\tt.Fatalf(\"Encoder.Write(raw) = %d, %v want %d, nil\", nn, err, n)\n\t}\n\terr = w.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"Encoder.Close() = %v want nil\", err)\n\t}\n\tdecoded, err := io.ReadAll(NewDecoder(encoded))\n\tif err != nil {\n\t\tt.Fatalf(\"io.ReadAll(NewDecoder(...)): %v\", err)\n\t}\n\n\tif !bytes.Equal(raw, decoded) {\n\t\tvar i int\n\t\tfor i = 0; i \u003c len(decoded) \u0026\u0026 i \u003c len(raw); i++ {\n\t\t\tif decoded[i] != raw[i] {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tt.Errorf(\"Decode(Encode(%d-byte string)) failed at offset %d\", n, i)\n\t}\n}\n\nfunc testStringEncoding(t *testing.T, expected string, examples []string) {\n\tfor _, e := range examples {\n\t\tbuf, err := DecodeString(e)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Decode(%q) failed: %v\", e, err)\n\t\t\tcontinue\n\t\t}\n\t\tif s := string(buf); s != expected {\n\t\t\tt.Errorf(\"Decode(%q) = %q, want %q\", e, s, expected)\n\t\t}\n\t}\n}\n\nfunc TestNewLineCharacters(t *testing.T) {\n\t// Each of these should decode to the string \"sure\", without errors.\n\texamples := []string{\n\t\t\"EDTQ4S8\",\n\t\t\"EDTQ4S8\\r\",\n\t\t\"EDTQ4S8\\n\",\n\t\t\"EDTQ4S8\\r\\n\",\n\t\t\"EDTQ4S\\r\\n8\",\n\t\t\"EDT\\rQ4S\\n8\",\n\t\t\"edt\\nq4s\\r8\",\n\t\t\"edt\\nq4s8\",\n\t\t\"EDTQ4S\\n8\",\n\t}\n\ttestStringEncoding(t, \"sure\", examples)\n}\n\nfunc BenchmarkEncode(b *testing.B) {\n\tdata := make([]byte, 8192)\n\tbuf := make([]byte, EncodedLen(len(data)))\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tEncode(buf, data)\n\t}\n}\n\nfunc BenchmarkEncodeToString(b *testing.B) {\n\tdata := make([]byte, 8192)\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tEncodeToString(data)\n\t}\n}\n\nfunc BenchmarkDecode(b *testing.B) {\n\tdata := make([]byte, EncodedLen(8192))\n\tEncode(data, make([]byte, 8192))\n\tbuf := make([]byte, 8192)\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tDecode(buf, data)\n\t}\n}\n\nfunc BenchmarkDecodeString(b *testing.B) {\n\tdata := EncodeToString(make([]byte, 8192))\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tDecodeString(data)\n\t}\n}\n\n/* TODO: rewrite without using goroutines\nfunc TestBufferedDecodingSameError(t *testing.T) {\n\ttestcases := []struct {\n\t\tprefix string\n\t\tchunkCombinations [][]string\n\t\texpected error\n\t}{\n\t\t// Normal case, this is valid input\n\t\t{\"helloworld\", [][]string{\n\t\t\t{\"D1JP\", \"RV3F\", \"EXQQ\", \"4V34\"},\n\t\t\t{\"D1JPRV3FEXQQ4V34\"},\n\t\t\t{\"D1J\", \"PRV\", \"3FE\", \"XQQ\", \"4V3\", \"4\"},\n\t\t\t{\"D1JPRV3FEXQQ4V\", \"34\"},\n\t\t}, nil},\n\n\t\t// Normal case, this is valid input\n\t\t{\"fooba\", [][]string{\n\t\t\t{\"CSQPYRK1\"},\n\t\t\t{\"CSQPYRK\", \"1\"},\n\t\t\t{\"CSQPYR\", \"K1\"},\n\t\t\t{\"CSQPY\", \"RK1\"},\n\t\t\t{\"CSQPY\", \"RK\", \"1\"},\n\t\t\t{\"CSQPY\", \"RK1\"},\n\t\t\t{\"CSQP\", \"YR\", \"K1\"},\n\t\t}, nil},\n\n\t\t// NOTE: many test cases have been removed as we don't return ErrUnexpectedEOF.\n\t}\n\n\tfor _, testcase := range testcases {\n\t\tfor _, chunks := range testcase.chunkCombinations {\n\t\t\tpr, pw := io.Pipe()\n\n\t\t\t// Write the encoded chunks into the pipe\n\t\t\tgo func() {\n\t\t\t\tfor _, chunk := range chunks {\n\t\t\t\t\tpw.Write([]byte(chunk))\n\t\t\t\t}\n\t\t\t\tpw.Close()\n\t\t\t}()\n\n\t\t\tdecoder := NewDecoder(pr)\n\t\t\tback, err := io.ReadAll(decoder)\n\n\t\t\tif err != testcase.expected {\n\t\t\t\tt.Errorf(\"Expected %v, got %v; case %s %+v\", testcase.expected, err, testcase.prefix, chunks)\n\t\t\t}\n\t\t\tif testcase.expected == nil {\n\t\t\t\ttestEqual(t, \"Decode from NewDecoder(chunkReader(%v)) = %q, want %q\", chunks, string(back), testcase.prefix)\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n\nfunc TestEncodedLen(t *testing.T) {\n\ttype test struct {\n\t\tn int\n\t\twant int64\n\t}\n\ttests := []test{\n\t\t{0, 0},\n\t\t{1, 2},\n\t\t{2, 4},\n\t\t{3, 5},\n\t\t{4, 7},\n\t\t{5, 8},\n\t\t{6, 10},\n\t\t{7, 12},\n\t\t{10, 16},\n\t\t{11, 18},\n\t}\n\t// check overflow\n\ttests = append(tests, test{(math.MaxInt-4)/8 + 1, 1844674407370955162})\n\ttests = append(tests, test{math.MaxInt/8*5 + 4, math.MaxInt})\n\tfor _, tt := range tests {\n\t\tif got := EncodedLen(tt.n); int64(got) != tt.want {\n\t\t\tt.Errorf(\"EncodedLen(%d): got %d, want %d\", tt.n, got, tt.want)\n\t\t}\n\t}\n}\n\nfunc TestDecodedLen(t *testing.T) {\n\ttype test struct {\n\t\tn int\n\t\twant int64\n\t}\n\ttests := []test{\n\t\t{0, 0},\n\t\t{2, 1},\n\t\t{4, 2},\n\t\t{5, 3},\n\t\t{7, 4},\n\t\t{8, 5},\n\t\t{10, 6},\n\t\t{12, 7},\n\t\t{16, 10},\n\t\t{18, 11},\n\t}\n\t// check overflow\n\ttests = append(tests, test{math.MaxInt/5 + 1, 1152921504606846976})\n\ttests = append(tests, test{math.MaxInt, 5764607523034234879})\n\tfor _, tt := range tests {\n\t\tif got := DecodedLen(tt.n); int64(got) != tt.want {\n\t\t\tt.Errorf(\"DecodedLen(%d): got %d, want %d\", tt.n, got, tt.want)\n\t\t}\n\t}\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/cford32\n" } ] }, @@ -564,10 +528,6 @@ { "name": "context_test.gno", "body": "package context\n\nimport \"testing\"\n\nfunc TestContextExample(t *testing.T) {\n\ttype favContextKey string\n\n\tk := favContextKey(\"language\")\n\tctx := WithValue(Empty(), k, \"Gno\")\n\n\tif v := ctx.Value(k); v != nil {\n\t\tif string(v) != \"Gno\" {\n\t\t\tt.Errorf(\"language value should be Gno, but is %s\", v)\n\t\t}\n\t} else {\n\t\tt.Errorf(\"language key value was not found\")\n\t}\n\n\tif v := ctx.Value(favContextKey(\"color\")); v != nil {\n\t\tt.Errorf(\"color key was found\")\n\t}\n}\n\n// otherContext is a Context that's not one of the types defined in context.go.\n// This lets us test code paths that differ based on the underlying type of the\n// Context.\ntype otherContext struct {\n\tContext\n}\n\ntype (\n\tkey1 int\n\tkey2 int\n)\n\n// func (k key2) String() string { return fmt.Sprintf(\"%[1]T(%[1]d)\", k) }\n\nvar (\n\tk1 = key1(1)\n\tk2 = key2(1) // same int as k1, different type\n\tk3 = key2(3) // same type as k2, different int\n)\n\nfunc TestValues(t *testing.T) {\n\tcheck := func(c Context, nm, v1, v2, v3 string) {\n\t\tif v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {\n\t\t\tt.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)\n\t\t}\n\t\tif v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {\n\t\t\tt.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)\n\t\t}\n\t\tif v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {\n\t\t\tt.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)\n\t\t}\n\t}\n\n\tc0 := Empty()\n\tcheck(c0, \"c0\", \"\", \"\", \"\")\n\n\tt.Skip() // XXX: depends on https://github.com/gnolang/gno/issues/2386\n\n\tc1 := WithValue(Empty(), k1, \"c1k1\")\n\tcheck(c1, \"c1\", \"c1k1\", \"\", \"\")\n\n\t/*if got, want := c1.String(), `context.Empty.WithValue(context_test.key1, c1k1)`; got != want {\n\t\tt.Errorf(\"c.String() = %q want %q\", got, want)\n\t}*/\n\n\tc2 := WithValue(c1, k2, \"c2k2\")\n\tcheck(c2, \"c2\", \"c1k1\", \"c2k2\", \"\")\n\n\t/*if got, want := fmt.Sprint(c2), `context.Empty.WithValue(context_test.key1, c1k1).WithValue(context_test.key2(1), c2k2)`; got != want {\n\t\tt.Errorf(\"c.String() = %q want %q\", got, want)\n\t}*/\n\n\tc3 := WithValue(c2, k3, \"c3k3\")\n\tcheck(c3, \"c2\", \"c1k1\", \"c2k2\", \"c3k3\")\n\n\tc4 := WithValue(c3, k1, nil)\n\tcheck(c4, \"c4\", \"\", \"c2k2\", \"c3k3\")\n\n\to0 := otherContext{Empty()}\n\tcheck(o0, \"o0\", \"\", \"\", \"\")\n\n\to1 := otherContext{WithValue(Empty(), k1, \"c1k1\")}\n\tcheck(o1, \"o1\", \"c1k1\", \"\", \"\")\n\n\to2 := WithValue(o1, k2, \"o2k2\")\n\tcheck(o2, \"o2\", \"c1k1\", \"o2k2\", \"\")\n\n\to3 := otherContext{c4}\n\tcheck(o3, \"o3\", \"\", \"c2k2\", \"c3k3\")\n\n\to4 := WithValue(o3, k3, nil)\n\tcheck(o4, \"o4\", \"\", \"c2k2\", \"\")\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/context\n" } ] }, @@ -598,10 +558,6 @@ { "name": "dom.gno", "body": "// XXX This is only used for testing in ./tests.\n// Otherwise this package is deprecated.\n// TODO: replace with a package that is supported, and delete this.\n\npackage dom\n\nimport (\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\ntype Plot struct {\n\tName string\n\tPosts avl.Tree // postsCtr -\u003e *Post\n\tPostsCtr int\n}\n\nfunc (plot *Plot) AddPost(title string, body string) {\n\tctr := plot.PostsCtr\n\tplot.PostsCtr++\n\tkey := strconv.Itoa(ctr)\n\tpost := \u0026Post{\n\t\tTitle: title,\n\t\tBody: body,\n\t}\n\tplot.Posts.Set(key, post)\n}\n\nfunc (plot *Plot) String() string {\n\tstr := \"# [plot] \" + plot.Name + \"\\n\"\n\tif plot.Posts.Size() \u003e 0 {\n\t\tplot.Posts.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tstr += \"\\n\"\n\t\t\tstr += value.(*Post).String()\n\t\t\treturn false\n\t\t})\n\t}\n\treturn str\n}\n\ntype Post struct {\n\tTitle string\n\tBody string\n\tComments avl.Tree\n}\n\nfunc (post *Post) String() string {\n\tstr := \"## \" + post.Title + \"\\n\"\n\tstr += \"\"\n\tstr += post.Body\n\tif post.Comments.Size() \u003e 0 {\n\t\tpost.Comments.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tstr += \"\\n\"\n\t\t\tstr += value.(*Comment).String()\n\t\t\treturn false\n\t\t})\n\t}\n\treturn str\n}\n\ntype Comment struct {\n\tCreator string\n\tBody string\n}\n\nfunc (cmm Comment) String() string {\n\treturn cmm.Body + \" - @\" + cmm.Creator + \"\\n\"\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/dom\n\nrequire gno.land/p/demo/avl v0.0.0-latest\n" } ] }, @@ -641,10 +597,6 @@ "name": "flow.gno", "body": "//\n// Written by Maxim Khitrov (November 2012)\n//\n// XXX modified to disable blocking, time.Sleep().\n\n// Package flow provides the tools for monitoring and limiting the flow rate\n// of an arbitrary data stream.\npackage flow\n\nimport (\n\t\"math\"\n\t// \"sync\"\n\t\"time\"\n)\n\n// Monitor monitors and limits the transfer rate of a data stream.\ntype Monitor struct {\n\t// mu sync.Mutex // Mutex guarding access to all internal fields\n\tactive bool // Flag indicating an active transfer\n\tstart time.Duration // Transfer start time (clock() value)\n\tbytes int64 // Total number of bytes transferred\n\tsamples int64 // Total number of samples taken\n\n\trSample float64 // Most recent transfer rate sample (bytes per second)\n\trEMA float64 // Exponential moving average of rSample\n\trPeak float64 // Peak transfer rate (max of all rSamples)\n\trWindow float64 // rEMA window (seconds)\n\n\tsBytes int64 // Number of bytes transferred since sLast\n\tsLast time.Duration // Most recent sample time (stop time when inactive)\n\tsRate time.Duration // Sampling rate\n\n\ttBytes int64 // Number of bytes expected in the current transfer\n\ttLast time.Duration // Time of the most recent transfer of at least 1 byte\n}\n\n// New creates a new flow control monitor. Instantaneous transfer rate is\n// measured and updated for each sampleRate interval. windowSize determines the\n// weight of each sample in the exponential moving average (EMA) calculation.\n// The exact formulas are:\n//\n//\tsampleTime = currentTime - prevSampleTime\n//\tsampleRate = byteCount / sampleTime\n//\tweight = 1 - exp(-sampleTime/windowSize)\n//\tnewRate = weight*sampleRate + (1-weight)*oldRate\n//\n// The default values for sampleRate and windowSize (if \u003c= 0) are 100ms and 1s,\n// respectively.\nfunc New(sampleRate, windowSize time.Duration) *Monitor {\n\tif sampleRate = clockRound(sampleRate); sampleRate \u003c= 0 {\n\t\tsampleRate = 5 * clockRate\n\t}\n\tif windowSize \u003c= 0 {\n\t\twindowSize = 1 * time.Second\n\t}\n\tnow := clock()\n\treturn \u0026Monitor{\n\t\tactive: true,\n\t\tstart: now,\n\t\trWindow: windowSize.Seconds(),\n\t\tsLast: now,\n\t\tsRate: sampleRate,\n\t\ttLast: now,\n\t}\n}\n\n// Update records the transfer of n bytes and returns n. It should be called\n// after each Read/Write operation, even if n is 0.\nfunc (m *Monitor) Update(n int) int {\n\t// m.mu.Lock()\n\tm.update(n)\n\t// m.mu.Unlock()\n\treturn n\n}\n\n// Hack to set the current rEMA.\nfunc (m *Monitor) SetREMA(rEMA float64) {\n\t// m.mu.Lock()\n\tm.rEMA = rEMA\n\tm.samples++\n\t// m.mu.Unlock()\n}\n\n// IO is a convenience method intended to wrap io.Reader and io.Writer method\n// execution. It calls m.Update(n) and then returns (n, err) unmodified.\nfunc (m *Monitor) IO(n int, err error) (int, error) {\n\treturn m.Update(n), err\n}\n\n// Done marks the transfer as finished and prevents any further updates or\n// limiting. Instantaneous and current transfer rates drop to 0. Update, IO, and\n// Limit methods become NOOPs. It returns the total number of bytes transferred.\nfunc (m *Monitor) Done() int64 {\n\t// m.mu.Lock()\n\tif now := m.update(0); m.sBytes \u003e 0 {\n\t\tm.reset(now)\n\t}\n\tm.active = false\n\tm.tLast = 0\n\tn := m.bytes\n\t// m.mu.Unlock()\n\treturn n\n}\n\n// timeRemLimit is the maximum Status.TimeRem value.\nconst timeRemLimit = 999*time.Hour + 59*time.Minute + 59*time.Second\n\n// Status represents the current Monitor status. All transfer rates are in bytes\n// per second rounded to the nearest byte.\ntype Status struct {\n\tActive bool // Flag indicating an active transfer\n\tStart time.Time // Transfer start time\n\tDuration time.Duration // Time period covered by the statistics\n\tIdle time.Duration // Time since the last transfer of at least 1 byte\n\tBytes int64 // Total number of bytes transferred\n\tSamples int64 // Total number of samples taken\n\tInstRate int64 // Instantaneous transfer rate\n\tCurRate int64 // Current transfer rate (EMA of InstRate)\n\tAvgRate int64 // Average transfer rate (Bytes / Duration)\n\tPeakRate int64 // Maximum instantaneous transfer rate\n\tBytesRem int64 // Number of bytes remaining in the transfer\n\tTimeRem time.Duration // Estimated time to completion\n\tProgress Percent // Overall transfer progress\n}\n\nfunc (s Status) String() string {\n\treturn \"STATUS{}\"\n}\n\n// Status returns current transfer status information. The returned value\n// becomes static after a call to Done.\nfunc (m *Monitor) Status() Status {\n\t// m.mu.Lock()\n\tnow := m.update(0)\n\ts := Status{\n\t\tActive: m.active,\n\t\tStart: clockToTime(m.start),\n\t\tDuration: m.sLast - m.start,\n\t\tIdle: now - m.tLast,\n\t\tBytes: m.bytes,\n\t\tSamples: m.samples,\n\t\tPeakRate: round(m.rPeak),\n\t\tBytesRem: m.tBytes - m.bytes,\n\t\tProgress: percentOf(float64(m.bytes), float64(m.tBytes)),\n\t}\n\tif s.BytesRem \u003c 0 {\n\t\ts.BytesRem = 0\n\t}\n\tif s.Duration \u003e 0 {\n\t\trAvg := float64(s.Bytes) / s.Duration.Seconds()\n\t\ts.AvgRate = round(rAvg)\n\t\tif s.Active {\n\t\t\ts.InstRate = round(m.rSample)\n\t\t\ts.CurRate = round(m.rEMA)\n\t\t\tif s.BytesRem \u003e 0 {\n\t\t\t\tif tRate := 0.8*m.rEMA + 0.2*rAvg; tRate \u003e 0 {\n\t\t\t\t\tns := float64(s.BytesRem) / tRate * 1e9\n\t\t\t\t\tif ns \u003e float64(timeRemLimit) {\n\t\t\t\t\t\tns = float64(timeRemLimit)\n\t\t\t\t\t}\n\t\t\t\t\ts.TimeRem = clockRound(time.Duration(ns))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// m.mu.Unlock()\n\treturn s\n}\n\n// Limit restricts the instantaneous (per-sample) data flow to rate bytes per\n// second. It returns the maximum number of bytes (0 \u003c= n \u003c= want) that may be\n// transferred immediately without exceeding the limit. If block == true, the\n// call blocks until n \u003e 0. want is returned unmodified if want \u003c 1, rate \u003c 1,\n// or the transfer is inactive (after a call to Done).\n//\n// At least one byte is always allowed to be transferred in any given sampling\n// period. Thus, if the sampling rate is 100ms, the lowest achievable flow rate\n// is 10 bytes per second.\n//\n// For usage examples, see the implementation of Reader and Writer in io.go.\nfunc (m *Monitor) Limit(want int, rate int64, block bool) (n int) {\n\tif block {\n\t\tpanic(\"blocking not yet supported\")\n\t}\n\tif want \u003c 1 || rate \u003c 1 {\n\t\treturn want\n\t}\n\t// m.mu.Lock()\n\n\t// Determine the maximum number of bytes that can be sent in one sample\n\tlimit := round(float64(rate) * m.sRate.Seconds())\n\tif limit \u003c= 0 {\n\t\tlimit = 1\n\t}\n\n\t_ = m.update(0)\n\t/* XXX\n\t// If block == true, wait until m.sBytes \u003c limit\n\tif now := m.update(0); block {\n\t\tfor m.sBytes \u003e= limit \u0026\u0026 m.active {\n\t\t\tnow = m.waitNextSample(now)\n\t\t}\n\t}\n\t*/\n\n\t// Make limit \u003c= want (unlimited if the transfer is no longer active)\n\tif limit -= m.sBytes; limit \u003e int64(want) || !m.active {\n\t\tlimit = int64(want)\n\t}\n\t// m.mu.Unlock()\n\n\tif limit \u003c 0 {\n\t\tlimit = 0\n\t}\n\treturn int(limit)\n}\n\n// SetTransferSize specifies the total size of the data transfer, which allows\n// the Monitor to calculate the overall progress and time to completion.\nfunc (m *Monitor) SetTransferSize(bytes int64) {\n\tif bytes \u003c 0 {\n\t\tbytes = 0\n\t}\n\t// m.mu.Lock()\n\tm.tBytes = bytes\n\t// m.mu.Unlock()\n}\n\n// update accumulates the transferred byte count for the current sample until\n// clock() - m.sLast \u003e= m.sRate. The monitor status is updated once the current\n// sample is done.\nfunc (m *Monitor) update(n int) (now time.Duration) {\n\tif !m.active {\n\t\treturn\n\t}\n\tif now = clock(); n \u003e 0 {\n\t\tm.tLast = now\n\t}\n\tm.sBytes += int64(n)\n\tif sTime := now - m.sLast; sTime \u003e= m.sRate {\n\t\tt := sTime.Seconds()\n\t\tif m.rSample = float64(m.sBytes) / t; m.rSample \u003e m.rPeak {\n\t\t\tm.rPeak = m.rSample\n\t\t}\n\n\t\t// Exponential moving average using a method similar to *nix load\n\t\t// average calculation. Longer sampling periods carry greater weight.\n\t\tif m.samples \u003e 0 {\n\t\t\tw := math.Exp(-t / m.rWindow)\n\t\t\tm.rEMA = m.rSample + w*(m.rEMA-m.rSample)\n\t\t} else {\n\t\t\tm.rEMA = m.rSample\n\t\t}\n\t\tm.reset(now)\n\t}\n\treturn\n}\n\n// reset clears the current sample state in preparation for the next sample.\nfunc (m *Monitor) reset(sampleTime time.Duration) {\n\tm.bytes += m.sBytes\n\tm.samples++\n\tm.sBytes = 0\n\tm.sLast = sampleTime\n}\n\n/*\n// waitNextSample sleeps for the remainder of the current sample. The lock is\n// released and reacquired during the actual sleep period, so it's possible for\n// the transfer to be inactive when this method returns.\nfunc (m *Monitor) waitNextSample(now time.Duration) time.Duration {\n\tconst minWait = 5 * time.Millisecond\n\tcurrent := m.sLast\n\n\t// sleep until the last sample time changes (ideally, just one iteration)\n\tfor m.sLast == current \u0026\u0026 m.active {\n\t\td := current + m.sRate - now\n\t\t// m.mu.Unlock()\n\t\tif d \u003c minWait {\n\t\t\td = minWait\n\t\t}\n\t\ttime.Sleep(d)\n\t\t// m.mu.Lock()\n\t\tnow = m.update(0)\n\t}\n\treturn now\n}\n*/\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/flow\n" - }, { "name": "io.gno", "body": "//\n// Written by Maxim Khitrov (November 2012)\n//\n\npackage flow\n\nimport (\n\t\"errors\"\n\t\"io\"\n)\n\n// ErrLimit is returned by the Writer when a non-blocking write is short due to\n// the transfer rate limit.\nvar ErrLimit = errors.New(\"flowrate: flow rate limit exceeded\")\n\n// Limiter is implemented by the Reader and Writer to provide a consistent\n// interface for monitoring and controlling data transfer.\ntype Limiter interface {\n\tDone() int64\n\tStatus() Status\n\tSetTransferSize(bytes int64)\n\tSetLimit(new int64) (old int64)\n\tSetBlocking(new bool) (old bool)\n}\n\n// Reader implements io.ReadCloser with a restriction on the rate of data\n// transfer.\ntype Reader struct {\n\tio.Reader // Data source\n\t*Monitor // Flow control monitor\n\n\tlimit int64 // Rate limit in bytes per second (unlimited when \u003c= 0)\n\tblock bool // What to do when no new bytes can be read due to the limit\n}\n\n// NewReader restricts all Read operations on r to limit bytes per second.\nfunc NewReader(r io.Reader, limit int64) *Reader {\n\treturn \u0026Reader{r, New(0, 0), limit, false} // XXX default false\n}\n\n// Read reads up to len(p) bytes into p without exceeding the current transfer\n// rate limit. It returns (0, nil) immediately if r is non-blocking and no new\n// bytes can be read at this time.\nfunc (r *Reader) Read(p []byte) (n int, err error) {\n\tp = p[:r.Limit(len(p), r.limit, r.block)]\n\tif len(p) \u003e 0 {\n\t\tn, err = r.IO(r.Reader.Read(p))\n\t}\n\treturn\n}\n\n// SetLimit changes the transfer rate limit to new bytes per second and returns\n// the previous setting.\nfunc (r *Reader) SetLimit(new int64) (old int64) {\n\told, r.limit = r.limit, new\n\treturn\n}\n\n// SetBlocking changes the blocking behavior and returns the previous setting. A\n// Read call on a non-blocking reader returns immediately if no additional bytes\n// may be read at this time due to the rate limit.\nfunc (r *Reader) SetBlocking(new bool) (old bool) {\n\tif new == true {\n\t\tpanic(\"blocking not yet supported\")\n\t}\n\told, r.block = r.block, new\n\treturn\n}\n\n// Close closes the underlying reader if it implements the io.Closer interface.\nfunc (r *Reader) Close() error {\n\tdefer r.Done()\n\tif c, ok := r.Reader.(io.Closer); ok {\n\t\treturn c.Close()\n\t}\n\treturn nil\n}\n\n// Writer implements io.WriteCloser with a restriction on the rate of data\n// transfer.\ntype Writer struct {\n\tio.Writer // Data destination\n\t*Monitor // Flow control monitor\n\n\tlimit int64 // Rate limit in bytes per second (unlimited when \u003c= 0)\n\tblock bool // What to do when no new bytes can be written due to the limit\n}\n\n// NewWriter restricts all Write operations on w to limit bytes per second. The\n// transfer rate and the default blocking behavior (true) can be changed\n// directly on the returned *Writer.\nfunc NewWriter(w io.Writer, limit int64) *Writer {\n\treturn \u0026Writer{w, New(0, 0), limit, false} // XXX default false\n}\n\n// Write writes len(p) bytes from p to the underlying data stream without\n// exceeding the current transfer rate limit. It returns (n, ErrLimit) if w is\n// non-blocking and no additional bytes can be written at this time.\nfunc (w *Writer) Write(p []byte) (n int, err error) {\n\tvar c int\n\tfor len(p) \u003e 0 \u0026\u0026 err == nil {\n\t\ts := p[:w.Limit(len(p), w.limit, w.block)]\n\t\tif len(s) \u003e 0 {\n\t\t\tc, err = w.IO(w.Writer.Write(s))\n\t\t} else {\n\t\t\treturn n, ErrLimit\n\t\t}\n\t\tp = p[c:]\n\t\tn += c\n\t}\n\treturn\n}\n\n// SetLimit changes the transfer rate limit to new bytes per second and returns\n// the previous setting.\nfunc (w *Writer) SetLimit(new int64) (old int64) {\n\told, w.limit = w.limit, new\n\treturn\n}\n\n// SetBlocking changes the blocking behavior and returns the previous setting. A\n// Write call on a non-blocking writer returns as soon as no additional bytes\n// may be written at this time due to the rate limit.\nfunc (w *Writer) SetBlocking(new bool) (old bool) {\n\told, w.block = w.block, new\n\treturn\n}\n\n// Close closes the underlying writer if it implements the io.Closer interface.\nfunc (w *Writer) Close() error {\n\tdefer w.Done()\n\tif c, ok := w.Writer.(io.Closer); ok {\n\t\treturn c.Close()\n\t}\n\treturn nil\n}\n" @@ -683,10 +635,6 @@ "name": "gnode", "path": "gno.land/p/demo/gnode", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnode\n" - }, { "name": "gnode.gno", "body": "package gnode\n\n// XXX what about Gnodes signing on behalf of others?\n// XXX like a multi-sig of Gnodes?\n\ntype Name string\n\ntype Gnode interface {\n\t//----------------------------------------\n\t// Basic properties\n\tGetName() Name\n\n\t//----------------------------------------\n\t// Affiliate Gnodes\n\tNumAffiliates() int\n\tGetAffiliates(Name) Affiliate\n\tAddAffiliate(Affiliate) error // must be affiliated\n\tRemAffiliate(Name) error // must have become unaffiliated\n\n\t//----------------------------------------\n\t// Signing\n\tNumSignedDocuments() int\n\tGetSignedDocument(idx int) Document\n\tSignDocument(doc Document) (int, error) // index relative to signer\n\n\t//----------------------------------------\n\t// Rendering\n\tRenderLines() []string\n}\n\ntype Affiliate struct {\n\tType string\n\tGnode Gnode\n\tTags []string\n}\n\ntype MyGnode struct {\n\tName\n\t// Owners // voting set, something that gives authority of action.\n\t// Treasury //\n\t// Affiliates //\n\t// Board // discussions\n\t// Data // XXX ?\n}\n\ntype Affiliates []*Affiliate\n\n// Documents are equal if they compare equal.\n// NOTE: requires all fields to be comparable.\ntype Document struct {\n\tAuthors string\n\t// Timestamp\n\t// Body\n\t// Attachments\n}\n\n// ACTIONS\n\n// * Lend tokens\n// * Pay tokens\n// * Administrate transferrable and non-transferrable tokens\n// * Sum tokens\n// * Passthrough dependencies\n// * Code\n// * ...\n" @@ -717,10 +665,6 @@ "name": "agent", "path": "gno.land/p/demo/gnorkle/agent", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/agent\n\nrequire gno.land/p/demo/avl v0.0.0-latest\n" - }, { "name": "whitelist.gno", "body": "package agent\n\nimport \"gno.land/p/demo/avl\"\n\n// Whitelist manages whitelisted agent addresses.\ntype Whitelist struct {\n\tstore *avl.Tree\n}\n\n// ClearAddresses removes all addresses from the whitelist and puts into a state\n// that indicates it is moot and has no whitelist defined.\nfunc (m *Whitelist) ClearAddresses() {\n\tm.store = nil\n}\n\n// AddAddresses adds the given addresses to the whitelist.\nfunc (m *Whitelist) AddAddresses(addresses []string) {\n\tif m.store == nil {\n\t\tm.store = avl.NewTree()\n\t}\n\n\tfor _, address := range addresses {\n\t\tm.store.Set(address, struct{}{})\n\t}\n}\n\n// RemoveAddress removes the given address from the whitelist if it exists.\nfunc (m *Whitelist) RemoveAddress(address string) {\n\tif m.store == nil {\n\t\treturn\n\t}\n\n\tm.store.Remove(address)\n}\n\n// HasDefinition returns true if the whitelist has a definition. It retuns false if\n// `ClearAddresses` has been called without any subsequent `AddAddresses` calls, or\n// if `AddAddresses` has never been called.\nfunc (m Whitelist) HasDefinition() bool {\n\treturn m.store != nil\n}\n\n// HasAddress returns true if the given address is in the whitelist.\nfunc (m Whitelist) HasAddress(address string) bool {\n\tif m.store == nil {\n\t\treturn false\n\t}\n\n\treturn m.store.Has(address)\n}\n" @@ -759,10 +703,6 @@ "name": "errors.gno", "body": "package feed\n\nimport \"errors\"\n\nvar ErrUndefined = errors.New(\"undefined feed\")\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/feed\n" - }, { "name": "task.gno", "body": "package feed\n\n// Task is a unit of work that can be part of a `Feed` definition. Tasks\n// are executed by agents.\ntype Task interface {\n\tMarshalJSON() ([]byte, error)\n}\n" @@ -805,10 +745,6 @@ "name": "errors.gno", "body": "package ingester\n\nimport \"errors\"\n\nvar ErrUndefined = errors.New(\"ingester undefined\")\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/ingester\n" - }, { "name": "type.gno", "body": "package ingester\n\n// Type indicates an ingester type.\ntype Type int\n\nconst (\n\t// TypeSingle indicates an ingester that can only ingest a single within a given period or no period.\n\tTypeSingle Type = iota\n\t// TypeMulti indicates an ingester that can ingest multiple within a given period or no period\n\tTypeMulti\n)\n" @@ -839,10 +775,6 @@ "name": "message", "path": "gno.land/p/demo/gnorkle/message", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/message\n" - }, { "name": "parse.gno", "body": "package message\n\nimport \"strings\"\n\n// ParseFunc parses a raw message and returns the message function\n// type extracted from the remainder of the message.\nfunc ParseFunc(rawMsg string) (FuncType, string) {\n\tfuncType, remainder := parseFirstToken(rawMsg)\n\treturn FuncType(funcType), remainder\n}\n\n// ParseID parses a raw message and returns the ID extracted from\n// the remainder of the message.\nfunc ParseID(rawMsg string) (string, string) {\n\treturn parseFirstToken(rawMsg)\n}\n\nfunc parseFirstToken(rawMsg string) (string, string) {\n\tmsgParts := strings.SplitN(rawMsg, \",\", 2)\n\tif len(msgParts) \u003c 2 {\n\t\treturn msgParts[0], \"\"\n\t}\n\n\treturn msgParts[0], msgParts[1]\n}\n" @@ -885,10 +817,6 @@ "name": "feed.gno", "body": "package gnorkle\n\nimport (\n\t\"gno.land/p/demo/gnorkle/feed\"\n\t\"gno.land/p/demo/gnorkle/message\"\n)\n\n// Feed is an abstraction used by a gnorkle `Instance` to ingest data from\n// agents and provide data feeds to consumers.\ntype Feed interface {\n\tID() string\n\tType() feed.Type\n\tValue() (value feed.Value, dataType string, consumable bool)\n\tIngest(funcType message.FuncType, rawMessage, providerAddress string) error\n\tMarshalJSON() ([]byte, error)\n\tTasks() []feed.Task\n\tIsActive() bool\n}\n\n// FeedWithWhitelist associates a `Whitelist` with a `Feed`.\ntype FeedWithWhitelist struct {\n\tFeed\n\tWhitelist\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/gnorkle\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/gnorkle/agent v0.0.0-latest\n\tgno.land/p/demo/gnorkle/feed v0.0.0-latest\n\tgno.land/p/demo/gnorkle/ingester v0.0.0-latest\n\tgno.land/p/demo/gnorkle/message v0.0.0-latest\n)\n" - }, { "name": "ingester.gno", "body": "package gnorkle\n\nimport \"gno.land/p/demo/gnorkle/ingester\"\n\n// Ingester is the abstraction that allows a `Feed` to ingest data from agents\n// and commit it to storage using zero or more intermediate aggregation steps.\ntype Ingester interface {\n\tType() ingester.Type\n\tIngest(value, providerAddress string) (canAutoCommit bool, err error)\n\tCommitValue(storage Storage, providerAddress string) error\n}\n" @@ -934,10 +862,6 @@ { "name": "errors.gno", "body": "package storage\n\nimport \"errors\"\n\nvar ErrUndefined = errors.New(\"undefined storage\")\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/storage\n" } ] }, @@ -965,10 +889,6 @@ "name": "simple", "path": "gno.land/p/demo/gnorkle/storage/simple", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/storage/simple\n\nrequire (\n\tgno.land/p/demo/gnorkle/feed v0.0.0-latest\n\tgno.land/p/demo/gnorkle/storage v0.0.0-latest\n)\n" - }, { "name": "storage.gno", "body": "package simple\n\nimport (\n\t\"time\"\n\n\t\"gno.land/p/demo/gnorkle/feed\"\n\t\"gno.land/p/demo/gnorkle/storage\"\n)\n\n// Storage is simple, bounded storage for published feed values.\ntype Storage struct {\n\tvalues []feed.Value\n\tmaxValues uint\n}\n\n// NewStorage creates a new Storage with the given maximum number of values.\n// If maxValues is 0, the storage is bounded to a size of one. If this is not desirable,\n// then don't provide a value of 0.\nfunc NewStorage(maxValues uint) *Storage {\n\tif maxValues == 0 {\n\t\tmaxValues = 1\n\t}\n\n\treturn \u0026Storage{\n\t\tmaxValues: maxValues,\n\t}\n}\n\n// Put adds a new value to the storage. If the storage is full, the oldest value\n// is removed. If maxValues is 0, the storage is bounded to a size of one.\nfunc (s *Storage) Put(value string) error {\n\tif s == nil {\n\t\treturn storage.ErrUndefined\n\t}\n\n\ts.values = append(s.values, feed.Value{String: value, Time: time.Now()})\n\tif uint(len(s.values)) \u003e s.maxValues {\n\t\ts.values = s.values[1:]\n\t}\n\n\treturn nil\n}\n\n// GetLatest returns the most recently added value, or an empty value if none exist.\nfunc (s Storage) GetLatest() feed.Value {\n\tif len(s.values) == 0 {\n\t\treturn feed.Value{}\n\t}\n\n\treturn s.values[len(s.values)-1]\n}\n\n// GetHistory returns all values in the storage, from oldest to newest.\nfunc (s Storage) GetHistory() []feed.Value {\n\treturn s.values\n}\n" @@ -1003,10 +923,6 @@ "name": "single", "path": "gno.land/p/demo/gnorkle/ingesters/single", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/ingesters/single\n\nrequire (\n\tgno.land/p/demo/gnorkle/gnorkle v0.0.0-latest\n\tgno.land/p/demo/gnorkle/ingester v0.0.0-latest\n\tgno.land/p/demo/gnorkle/storage/simple v0.0.0-latest\n)\n" - }, { "name": "ingester.gno", "body": "package single\n\nimport (\n\t\"gno.land/p/demo/gnorkle/gnorkle\"\n\t\"gno.land/p/demo/gnorkle/ingester\"\n)\n\n// ValueIngester is an ingester that ingests a single value.\ntype ValueIngester struct {\n\tvalue string\n}\n\n// Type returns the type of the ingester.\nfunc (i *ValueIngester) Type() ingester.Type {\n\treturn ingester.TypeSingle\n}\n\n// Ingest ingests a value provided by the given agent address.\nfunc (i *ValueIngester) Ingest(value, providerAddress string) (bool, error) {\n\tif i == nil {\n\t\treturn false, ingester.ErrUndefined\n\t}\n\n\ti.value = value\n\treturn true, nil\n}\n\n// CommitValue commits the ingested value to the given storage instance.\nfunc (i *ValueIngester) CommitValue(valueStorer gnorkle.Storage, providerAddress string) error {\n\tif i == nil {\n\t\treturn ingester.ErrUndefined\n\t}\n\n\treturn valueStorer.Put(i.value)\n}\n" @@ -1048,10 +964,6 @@ { "name": "feed_test.gno", "body": "package static_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/gnorkle/feed\"\n\t\"gno.land/p/demo/gnorkle/feeds/static\"\n\t\"gno.land/p/demo/gnorkle/gnorkle\"\n\t\"gno.land/p/demo/gnorkle/ingester\"\n\t\"gno.land/p/demo/gnorkle/message\"\n\t\"gno.land/p/demo/gnorkle/storage/simple\"\n)\n\ntype mockIngester struct {\n\tcanAutoCommit bool\n\tingestErr error\n\tcommitErr error\n\tvalue string\n\tproviderAddress string\n}\n\nfunc (i mockIngester) Type() ingester.Type {\n\treturn ingester.Type(0)\n}\n\nfunc (i *mockIngester) Ingest(value, providerAddress string) (bool, error) {\n\tif i.ingestErr != nil {\n\t\treturn false, i.ingestErr\n\t}\n\n\ti.value = value\n\ti.providerAddress = providerAddress\n\treturn i.canAutoCommit, nil\n}\n\nfunc (i *mockIngester) CommitValue(storage gnorkle.Storage, providerAddress string) error {\n\tif i.commitErr != nil {\n\t\treturn i.commitErr\n\t}\n\n\treturn storage.Put(i.value)\n}\n\nfunc TestNewSingleValueFeed(t *testing.T) {\n\tstaticFeed := static.NewSingleValueFeed(\"1\", \"\")\n\tif staticFeed.ID() != \"1\" {\n\t\tt.Errorf(\"expected ID to be 1, got %s\", staticFeed.ID())\n\t}\n\n\tif staticFeed.Type() != feed.TypeStatic {\n\t\tt.Errorf(\"expected static feed type, got %s\", staticFeed.Type())\n\t}\n}\n\nfunc TestFeed_Ingest(t *testing.T) {\n\tvar undefinedFeed *static.Feed\n\tif undefinedFeed.Ingest(\"\", \"\", \"\") != feed.ErrUndefined {\n\t\tt.Errorf(\"expected ErrUndefined, got nil\")\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\tingester *mockIngester\n\t\tverifyIsLocked bool\n\t\tdoCommit bool\n\t\tfuncType message.FuncType\n\t\tmsg string\n\t\tproviderAddress string\n\t\texpFeedValueString string\n\t\texpErrText string\n\t\texpIsActive bool\n\t}{\n\t\t{\n\t\t\tname: \"func invalid error\",\n\t\t\tingester: \u0026mockIngester{},\n\t\t\tfuncType: message.FuncType(\"derp\"),\n\t\t\texpErrText: \"invalid message function derp\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"func ingest ingest error\",\n\t\t\tingester: \u0026mockIngester{\n\t\t\t\tingestErr: errors.New(\"ingest error\"),\n\t\t\t},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\texpErrText: \"ingest error\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"func ingest commit error\",\n\t\t\tingester: \u0026mockIngester{\n\t\t\t\tcommitErr: errors.New(\"commit error\"),\n\t\t\t\tcanAutoCommit: true,\n\t\t\t},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\texpErrText: \"commit error\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"func commit commit error\",\n\t\t\tingester: \u0026mockIngester{\n\t\t\t\tcommitErr: errors.New(\"commit error\"),\n\t\t\t\tcanAutoCommit: true,\n\t\t\t},\n\t\t\tfuncType: message.FuncTypeCommit,\n\t\t\texpErrText: \"commit error\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"only ingest\",\n\t\t\tingester: \u0026mockIngester{},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\tmsg: \"still active feed\",\n\t\t\tproviderAddress: \"gno1234\",\n\t\t\texpIsActive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ingest autocommit\",\n\t\t\tingester: \u0026mockIngester{canAutoCommit: true},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\tmsg: \"still active feed\",\n\t\t\tproviderAddress: \"gno1234\",\n\t\t\texpFeedValueString: \"still active feed\",\n\t\t\tverifyIsLocked: true,\n\t\t},\n\t\t{\n\t\t\tname: \"commit no value\",\n\t\t\tingester: \u0026mockIngester{},\n\t\t\tfuncType: message.FuncTypeCommit,\n\t\t\tmsg: \"shouldn't be stored\",\n\t\t\tverifyIsLocked: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ingest then commmit\",\n\t\t\tingester: \u0026mockIngester{},\n\t\t\tfuncType: message.FuncTypeIngest,\n\t\t\tmsg: \"blahblah\",\n\t\t\tdoCommit: true,\n\t\t\texpFeedValueString: \"blahblah\",\n\t\t\tverifyIsLocked: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstaticFeed := static.NewFeed(\n\t\t\t\t\"1\",\n\t\t\t\t\"string\",\n\t\t\t\ttt.ingester,\n\t\t\t\tsimple.NewStorage(1),\n\t\t\t\tnil,\n\t\t\t)\n\n\t\t\tvar errText string\n\t\t\tif err := staticFeed.Ingest(tt.funcType, tt.msg, tt.providerAddress); err != nil {\n\t\t\t\terrText = err.Error()\n\t\t\t}\n\n\t\t\tif errText != tt.expErrText {\n\t\t\t\tt.Fatalf(\"expected error text %s, got %s\", tt.expErrText, errText)\n\t\t\t}\n\n\t\t\tif tt.doCommit {\n\t\t\t\terr := staticFeed.Ingest(message.FuncTypeCommit, \"\", \"\")\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"follow up commit failed: %s\", err.Error())\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif tt.verifyIsLocked {\n\t\t\t\terrText = \"\"\n\t\t\t\tif err := staticFeed.Ingest(tt.funcType, tt.msg, tt.providerAddress); err != nil {\n\t\t\t\t\terrText = err.Error()\n\t\t\t\t}\n\n\t\t\t\tif errText != \"feed locked\" {\n\t\t\t\t\tt.Fatalf(\"expected error text feed locked, got %s\", errText)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif tt.ingester.providerAddress != tt.providerAddress {\n\t\t\t\tt.Errorf(\"expected provider address %s, got %s\", tt.providerAddress, tt.ingester.providerAddress)\n\t\t\t}\n\n\t\t\tfeedValue, dataType, isLocked := staticFeed.Value()\n\t\t\tif feedValue.String != tt.expFeedValueString {\n\t\t\t\tt.Errorf(\"expected feed value string %s, got %s\", tt.expFeedValueString, feedValue.String)\n\t\t\t}\n\n\t\t\tif dataType != \"string\" {\n\t\t\t\tt.Errorf(\"expected data type string, got %s\", dataType)\n\t\t\t}\n\n\t\t\tif isLocked != tt.verifyIsLocked {\n\t\t\t\tt.Errorf(\"expected is locked %t, got %t\", tt.verifyIsLocked, isLocked)\n\t\t\t}\n\n\t\t\tif staticFeed.IsActive() != tt.expIsActive {\n\t\t\t\tt.Errorf(\"expected is active %t, got %t\", tt.expIsActive, staticFeed.IsActive())\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype mockTask struct {\n\terr error\n\tvalue string\n}\n\nfunc (t mockTask) MarshalJSON() ([]byte, error) {\n\tif t.err != nil {\n\t\treturn nil, t.err\n\t}\n\n\treturn []byte(`{\"value\":\"` + t.value + `\"}`), nil\n}\n\nfunc TestFeed_Tasks(t *testing.T) {\n\tid := \"99\"\n\tvalueDataType := \"int\"\n\n\ttests := []struct {\n\t\tname string\n\t\ttasks []feed.Task\n\t\texpErrText string\n\t\texpJSON string\n\t}{\n\t\t{\n\t\t\tname: \"no tasks\",\n\t\t\texpJSON: `{\"id\":\"99\",\"type\":\"0\",\"value_type\":\"int\",\"tasks\":[]}`,\n\t\t},\n\t\t{\n\t\t\tname: \"marshal error\",\n\t\t\ttasks: []feed.Task{\n\t\t\t\tmockTask{err: errors.New(\"marshal error\")},\n\t\t\t},\n\t\t\texpErrText: \"marshal error\",\n\t\t},\n\t\t{\n\t\t\tname: \"one task\",\n\t\t\ttasks: []feed.Task{\n\t\t\t\tmockTask{value: \"single\"},\n\t\t\t},\n\t\t\texpJSON: `{\"id\":\"99\",\"type\":\"0\",\"value_type\":\"int\",\"tasks\":[{\"value\":\"single\"}]}`,\n\t\t},\n\t\t{\n\t\t\tname: \"two tasks\",\n\t\t\ttasks: []feed.Task{\n\t\t\t\tmockTask{value: \"first\"},\n\t\t\t\tmockTask{value: \"second\"},\n\t\t\t},\n\t\t\texpJSON: `{\"id\":\"99\",\"type\":\"0\",\"value_type\":\"int\",\"tasks\":[{\"value\":\"first\"},{\"value\":\"second\"}]}`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tstaticFeed := static.NewSingleValueFeed(\n\t\t\t\tid,\n\t\t\t\tvalueDataType,\n\t\t\t\ttt.tasks...,\n\t\t\t)\n\n\t\t\tif len(staticFeed.Tasks()) != len(tt.tasks) {\n\t\t\t\tt.Fatalf(\"expected %d tasks, got %d\", len(tt.tasks), len(staticFeed.Tasks()))\n\t\t\t}\n\n\t\t\tvar errText string\n\t\t\tjson, err := staticFeed.MarshalJSON()\n\t\t\tif err != nil {\n\t\t\t\terrText = err.Error()\n\t\t\t}\n\n\t\t\tif errText != tt.expErrText {\n\t\t\t\tt.Fatalf(\"expected error text %s, got %s\", tt.expErrText, errText)\n\t\t\t}\n\n\t\t\tif string(json) != tt.expJSON {\n\t\t\t\tt.Errorf(\"expected json %s, got %s\", tt.expJSON, string(json))\n\t\t\t}\n\t\t})\n\t}\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/gnorkle/feeds/static\n\nrequire (\n\tgno.land/p/demo/gnorkle/feed v0.0.0-latest\n\tgno.land/p/demo/gnorkle/gnorkle v0.0.0-latest\n\tgno.land/p/demo/gnorkle/ingester v0.0.0-latest\n\tgno.land/p/demo/gnorkle/ingesters/single v0.0.0-latest\n\tgno.land/p/demo/gnorkle/message v0.0.0-latest\n\tgno.land/p/demo/gnorkle/storage/simple v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" } ] }, @@ -1079,10 +991,6 @@ "name": "exts", "path": "gno.land/p/demo/grc/exts", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/grc/exts\n" - }, { "name": "token_metadata.gno", "body": "package exts\n\ntype TokenMetadata interface {\n\t// Returns the name of the token.\n\tGetName() string\n\n\t// Returns the symbol of the token, usually a shorter version of the\n\t// name.\n\tGetSymbol() string\n\n\t// Returns the decimals places of the token.\n\tGetDecimals() uint\n}\n" @@ -1129,10 +1037,6 @@ "name": "errors.gno", "body": "package grc1155\n\nimport \"errors\"\n\nvar (\n\tErrInvalidAddress = errors.New(\"invalid address\")\n\tErrMismatchLength = errors.New(\"accounts and ids length mismatch\")\n\tErrCannotTransferToSelf = errors.New(\"cannot send transfer to self\")\n\tErrTransferToRejectedOrNonGRC1155Receiver = errors.New(\"transfer to rejected or non GRC1155Receiver implementer\")\n\tErrCallerIsNotOwnerOrApproved = errors.New(\"caller is not token owner or approved\")\n\tErrInsufficientBalance = errors.New(\"insufficient balance for transfer\")\n\tErrBurnAmountExceedsBalance = errors.New(\"burn amount exceeds balance\")\n)\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/grc/grc1155\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "igrc1155.gno", "body": "package grc1155\n\nimport \"std\"\n\ntype IGRC1155 interface {\n\tSafeTransferFrom(from, to std.Address, tid TokenID, amount uint64) error\n\tSafeBatchTransferFrom(from, to std.Address, batch []TokenID, amounts []uint64) error\n\tBalanceOf(owner std.Address, tid TokenID) (uint64, error)\n\tBalanceOfBatch(owners []std.Address, batch []TokenID) ([]uint64, error)\n\tSetApprovalForAll(operator std.Address, approved bool) error\n\tIsApprovedForAll(owner, operator std.Address) bool\n}\n\ntype TokenID string\n\ntype TransferSingleEvent struct {\n\tOperator std.Address\n\tFrom std.Address\n\tTo std.Address\n\tTokenID TokenID\n\tAmount uint64\n}\n\ntype TransferBatchEvent struct {\n\tOperator std.Address\n\tFrom std.Address\n\tTo std.Address\n\tBatch []TokenID\n\tAmounts []uint64\n}\n\ntype ApprovalForAllEvent struct {\n\tOwner std.Address\n\tOperator std.Address\n\tApproved bool\n}\n\ntype UpdateURIEvent struct {\n\tURI string\n}\n" @@ -1171,10 +1075,6 @@ "name": "doc.gno", "body": "package uassert // import \"gno.land/p/demo/uassert\"\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/uassert\n" - }, { "name": "helpers.gno", "body": "package uassert\n\nimport \"strings\"\n\nfunc fail(t TestingT, customMsgs []string, failureMessage string, args ...interface{}) bool {\n\tcustomMsg := \"\"\n\tif len(customMsgs) \u003e 0 {\n\t\tcustomMsg = strings.Join(customMsgs, \" \")\n\t}\n\tif customMsg != \"\" {\n\t\tfailureMessage += \" - \" + customMsg\n\t}\n\tt.Errorf(failureMessage, args...)\n\treturn false\n}\n\nfunc autofail(t TestingT, success bool, customMsgs []string, failureMessage string, args ...interface{}) bool {\n\tif success {\n\t\treturn true\n\t}\n\treturn fail(t, customMsgs, failureMessage, args...)\n}\n\nfunc checkDidPanic(f func()) (didPanic bool, message string) {\n\tdidPanic = true\n\tdefer func() {\n\t\tr := recover()\n\n\t\tif r == nil {\n\t\t\tmessage = \"nil\"\n\t\t\treturn\n\t\t}\n\n\t\terr, ok := r.(error)\n\t\tif ok {\n\t\t\tmessage = err.Error()\n\t\t\treturn\n\t\t}\n\n\t\terrStr, ok := r.(string)\n\t\tif ok {\n\t\t\tmessage = errStr\n\t\t\treturn\n\t\t}\n\n\t\tmessage = \"recover: unsupported type\"\n\t}()\n\tf()\n\tdidPanic = false\n\treturn\n}\n" @@ -1221,10 +1121,6 @@ "name": "urequire", "path": "gno.land/p/demo/urequire", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/urequire\n\nrequire gno.land/p/demo/uassert v0.0.0-latest\n" - }, { "name": "urequire.gno", "body": "// urequire is a sister package for uassert.\n// XXX: codegen the package.\npackage urequire\n\nimport \"gno.land/p/demo/uassert\"\n\n// type TestingT = uassert.TestingT // XXX: bug, should work\n\nfunc NoError(t uassert.TestingT, err error, msgs ...string) {\n\tt.Helper()\n\tif uassert.NoError(t, err, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc Error(t uassert.TestingT, err error, msgs ...string) {\n\tt.Helper()\n\tif uassert.Error(t, err, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc ErrorContains(t uassert.TestingT, err error, contains string, msgs ...string) {\n\tt.Helper()\n\tif uassert.ErrorContains(t, err, contains, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc True(t uassert.TestingT, value bool, msgs ...string) {\n\tt.Helper()\n\tif uassert.True(t, value, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc False(t uassert.TestingT, value bool, msgs ...string) {\n\tt.Helper()\n\tif uassert.False(t, value, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc ErrorIs(t uassert.TestingT, err, target error, msgs ...string) {\n\tt.Helper()\n\tif uassert.ErrorIs(t, err, target, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc PanicsWithMessage(t uassert.TestingT, msg string, f func(), msgs ...string) {\n\tt.Helper()\n\tif uassert.PanicsWithMessage(t, msg, f, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc NotPanics(t uassert.TestingT, f func(), msgs ...string) {\n\tt.Helper()\n\tif uassert.NotPanics(t, f, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc Equal(t uassert.TestingT, expected, actual interface{}, msgs ...string) {\n\tt.Helper()\n\tif uassert.Equal(t, expected, actual, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\nfunc Empty(t uassert.TestingT, obj interface{}, msgs ...string) {\n\tt.Helper()\n\tif uassert.Empty(t, obj, msgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n" @@ -1267,10 +1163,6 @@ "name": "banker_test.gno", "body": "package grc20\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/demo/urequire\"\n)\n\nfunc TestBankerImpl(t *testing.T) {\n\tdummy := NewBanker(\"Dummy\", \"DUMMY\", 4)\n\turequire.False(t, dummy == nil, \"dummy should not be nil\")\n}\n\nfunc TestAllowance(t *testing.T) {\n\tvar (\n\t\towner = testutils.TestAddress(\"owner\")\n\t\tspender = testutils.TestAddress(\"spender\")\n\t\tdest = testutils.TestAddress(\"dest\")\n\t)\n\n\tb := NewBanker(\"Dummy\", \"DUMMY\", 6)\n\turequire.NoError(t, b.Mint(owner, 100000000))\n\turequire.NoError(t, b.Approve(owner, spender, 5000000))\n\turequire.Error(t, b.TransferFrom(spender, owner, dest, 10000000), ErrInsufficientAllowance.Error(), \"should not be able to transfer more than approved\")\n\n\ttests := []struct {\n\t\tspend uint64\n\t\texp uint64\n\t}{\n\t\t{3, 4999997},\n\t\t{999997, 4000000},\n\t\t{4000000, 0},\n\t}\n\n\tfor _, tt := range tests {\n\t\tb0 := b.BalanceOf(dest)\n\t\turequire.NoError(t, b.TransferFrom(spender, owner, dest, tt.spend))\n\t\ta := b.Allowance(owner, spender)\n\t\turequire.Equal(t, a, tt.exp, ufmt.Sprintf(\"allowance exp: %d, got %d\", tt.exp, a))\n\t\tb := b.BalanceOf(dest)\n\t\texpB := b0 + tt.spend\n\t\turequire.Equal(t, b, expB, ufmt.Sprintf(\"balance exp: %d, got %d\", expB, b))\n\t}\n\n\turequire.Error(t, b.TransferFrom(spender, owner, dest, 1), \"no allowance\")\n\tkey := allowanceKey(owner, spender)\n\turequire.False(t, b.allowances.Has(key), \"allowance should be removed\")\n\turequire.Equal(t, b.Allowance(owner, spender), uint64(0), \"allowance should be 0\")\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/grc/grc20\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/grc/exts v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n)\n" - }, { "name": "token.gno", "body": "package grc20\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/grc/exts\"\n)\n\n// token implements the Token interface.\n//\n// It is generated with Banker.Token().\n// It can safely be explosed publicly.\ntype token struct {\n\tbanker *Banker\n}\n\n// var _ Token = (*token)(nil)\nfunc (t *token) GetName() string { return t.banker.name }\nfunc (t *token) GetSymbol() string { return t.banker.symbol }\nfunc (t *token) GetDecimals() uint { return t.banker.decimals }\nfunc (t *token) TotalSupply() uint64 { return t.banker.totalSupply }\n\nfunc (t *token) BalanceOf(owner std.Address) uint64 {\n\treturn t.banker.BalanceOf(owner)\n}\n\nfunc (t *token) Transfer(to std.Address, amount uint64) error {\n\tcaller := std.PrevRealm().Addr()\n\treturn t.banker.Transfer(caller, to, amount)\n}\n\nfunc (t *token) Allowance(owner, spender std.Address) uint64 {\n\treturn t.banker.Allowance(owner, spender)\n}\n\nfunc (t *token) Approve(spender std.Address, amount uint64) error {\n\tcaller := std.PrevRealm().Addr()\n\treturn t.banker.Approve(caller, spender, amount)\n}\n\nfunc (t *token) TransferFrom(from, to std.Address, amount uint64) error {\n\tspender := std.PrevRealm().Addr()\n\tif err := t.banker.SpendAllowance(from, spender, amount); err != nil {\n\t\treturn err\n\t}\n\treturn t.banker.Transfer(from, to, amount)\n}\n\ntype Token2 interface {\n\texts.TokenMetadata\n\n\t// Returns the amount of tokens in existence.\n\tTotalSupply() uint64\n\n\t// Returns the amount of tokens owned by `account`.\n\tBalanceOf(account std.Address) uint64\n\n\t// Moves `amount` tokens from the caller's account to `to`.\n\t//\n\t// Returns an error if the operation failed.\n\tTransfer(to std.Address, amount uint64) error\n\n\t// Returns the remaining number of tokens that `spender` will be\n\t// allowed to spend on behalf of `owner` through {transferFrom}. This is\n\t// zero by default.\n\t//\n\t// This value changes when {approve} or {transferFrom} are called.\n\tAllowance(owner, spender std.Address) uint64\n\n\t// Sets `amount` as the allowance of `spender` over the caller's tokens.\n\t//\n\t// Returns an error if the operation failed.\n\t//\n\t// IMPORTANT: Beware that changing an allowance with this method brings the risk\n\t// that someone may use both the old and the new allowance by unfortunate\n\t// transaction ordering. One possible solution to mitigate this race\n\t// condition is to first reduce the spender's allowance to 0 and set the\n\t// desired value afterwards:\n\t// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n\tApprove(spender std.Address, amount uint64) error\n\n\t// Moves `amount` tokens from `from` to `to` using the\n\t// allowance mechanism. `amount` is then deducted from the caller's\n\t// allowance.\n\t//\n\t// Returns an error if the operation failed.\n\tTransferFrom(from, to std.Address, amount uint64) error\n}\n" @@ -1321,10 +1213,6 @@ "name": "errors.gno", "body": "package grc721\n\nimport \"errors\"\n\nvar (\n\tErrInvalidTokenId = errors.New(\"invalid token id\")\n\tErrInvalidAddress = errors.New(\"invalid address\")\n\tErrTokenIdNotHasApproved = errors.New(\"token id not approved for anyone\")\n\tErrApprovalToCurrentOwner = errors.New(\"approval to current owner\")\n\tErrCallerIsNotOwner = errors.New(\"caller is not token owner\")\n\tErrCallerNotApprovedForAll = errors.New(\"caller is not approved for all\")\n\tErrCannotTransferToSelf = errors.New(\"cannot send transfer to self\")\n\tErrTransferFromIncorrectOwner = errors.New(\"transfer from incorrect owner\")\n\tErrTransferToNonGRC721Receiver = errors.New(\"transfer to non GRC721Receiver implementer\")\n\tErrCallerIsNotOwnerOrApproved = errors.New(\"caller is not token owner or approved\")\n\tErrTokenIdAlreadyExists = errors.New(\"token id already exists\")\n\n\t// ERC721Royalty\n\tErrInvalidRoyaltyPercentage = errors.New(\"invalid royalty percentage\")\n\tErrInvalidRoyaltyPaymentAddress = errors.New(\"invalid royalty paymentAddress\")\n\tErrCannotCalculateRoyaltyAmount = errors.New(\"cannot calculate royalty amount\")\n)\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/grc/grc721\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "grc721_metadata.gno", "body": "package grc721\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\n// metadataNFT represents an NFT with metadata extensions.\ntype metadataNFT struct {\n\t*basicNFT // Embedded basicNFT struct for basic NFT functionality\n\textensions *avl.Tree // AVL tree for storing metadata extensions\n}\n\n// Ensure that metadataNFT implements the IGRC721MetadataOnchain interface.\nvar _ IGRC721MetadataOnchain = (*metadataNFT)(nil)\n\n// NewNFTWithMetadata creates a new basic NFT with metadata extensions.\nfunc NewNFTWithMetadata(name string, symbol string) *metadataNFT {\n\t// Create a new basic NFT\n\tnft := NewBasicNFT(name, symbol)\n\n\t// Return a metadataNFT with basicNFT embedded and an empty AVL tree for extensions\n\treturn \u0026metadataNFT{\n\t\tbasicNFT: nft,\n\t\textensions: avl.NewTree(),\n\t}\n}\n\n// SetTokenMetadata sets metadata for a given token ID.\nfunc (s *metadataNFT) SetTokenMetadata(tid TokenID, metadata Metadata) error {\n\t// Check if the caller is the owner of the token\n\towner, err := s.basicNFT.OwnerOf(tid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcaller := std.PrevRealm().Addr()\n\tif caller != owner {\n\t\treturn ErrCallerIsNotOwner\n\t}\n\n\t// Set the metadata for the token ID in the extensions AVL tree\n\ts.extensions.Set(string(tid), metadata)\n\treturn nil\n}\n\n// TokenMetadata retrieves metadata for a given token ID.\nfunc (s *metadataNFT) TokenMetadata(tid TokenID) (Metadata, error) {\n\t// Retrieve metadata from the extensions AVL tree\n\tmetadata, found := s.extensions.Get(string(tid))\n\tif !found {\n\t\treturn Metadata{}, ErrInvalidTokenId\n\t}\n\n\treturn metadata.(Metadata), nil\n}\n\n// mint mints a new token and assigns it to the specified address.\nfunc (s *metadataNFT) mint(to std.Address, tid TokenID) error {\n\t// Check if the address is valid\n\tif err := isValidAddress(to); err != nil {\n\t\treturn err\n\t}\n\n\t// Check if the token ID already exists\n\tif s.basicNFT.exists(tid) {\n\t\treturn ErrTokenIdAlreadyExists\n\t}\n\n\ts.basicNFT.beforeTokenTransfer(zeroAddress, to, tid, 1)\n\n\t// Check if the token ID was minted by beforeTokenTransfer\n\tif s.basicNFT.exists(tid) {\n\t\treturn ErrTokenIdAlreadyExists\n\t}\n\n\t// Increment balance of the recipient address\n\ttoBalance, err := s.basicNFT.BalanceOf(to)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttoBalance += 1\n\ts.basicNFT.balances.Set(to.String(), toBalance)\n\n\t// Set owner of the token ID to the recipient address\n\ts.basicNFT.owners.Set(string(tid), to)\n\n\t// Emit transfer event\n\tevent := TransferEvent{zeroAddress, to, tid}\n\temit(\u0026event)\n\n\ts.basicNFT.afterTokenTransfer(zeroAddress, to, tid, 1)\n\n\treturn nil\n}\n" @@ -1387,10 +1275,6 @@ "name": "dummy_test.gno", "body": "package grc777\n\nimport (\n\t\"std\"\n\t\"testing\"\n)\n\ntype dummyImpl struct{}\n\n// FIXME: this should fail.\nvar _ IGRC777 = (*dummyImpl)(nil)\n\nfunc TestInterface(t *testing.T) {\n\tvar dummy IGRC777 = \u0026dummyImpl{}\n}\n\nfunc (impl *dummyImpl) GetName() string { panic(\"not implemented\") }\nfunc (impl *dummyImpl) GetSymbol() string { panic(\"not implemented\") }\nfunc (impl *dummyImpl) GetDecimals() uint { panic(\"not implemented\") }\nfunc (impl *dummyImpl) Granularity() (granularity uint64) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) TotalSupply() (supply uint64) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) BalanceOf(address std.Address) uint64 { panic(\"not implemented\") }\nfunc (impl *dummyImpl) Burn(amount uint64, data []byte) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) AuthorizeOperator(operator std.Address) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) RevokeOperator(operators std.Address) { panic(\"not implemented\") }\nfunc (impl *dummyImpl) DefaultOperators() []std.Address { panic(\"not implemented\") }\nfunc (impl *dummyImpl) Send(recipient std.Address, amount uint64, data []byte) {\n\tpanic(\"not implemented\")\n}\n\nfunc (impl *dummyImpl) IsOperatorFor(operator, tokenHolder std.Address) bool {\n\tpanic(\"not implemented\")\n}\n\nfunc (impl *dummyImpl) OperatorSend(sender, recipient std.Address, amount uint64, data, operatorData []byte) {\n\tpanic(\"not implemented\")\n}\n\nfunc (impl *dummyImpl) OperatorBurn(account std.Address, amount uint64, data, operatorData []byte) {\n\tpanic(\"not implemented\")\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/grc/grc777\n\nrequire gno.land/p/demo/grc/exts v0.0.0-latest\n" - }, { "name": "igrc777.gno", "body": "package grc777\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/grc/exts\"\n)\n\n// TODO: use big.Int or a custom uint64 instead of uint64\n\ntype IGRC777 interface {\n\texts.TokenMetadata\n\n\t// Returns the smallest part of the token that is not divisible. This\n\t// means all token operations (creation, movement and destruction) must\n\t// have amounts that are a multiple of this number.\n\t//\n\t// For most token contracts, this value will equal 1.\n\tGranularity() (granularity uint64)\n\n\t// Returns the amount of tokens in existence.\n\tTotalSupply() (supply uint64)\n\n\t// Returns the amount of tokens owned by an account (`owner`).\n\tBalanceOf(address std.Address) uint64\n\n\t// Moves `amount` tokens from the caller's account to `recipient`.\n\t//\n\t// If send or receive hooks are registered for the caller and `recipient`,\n\t// the corresponding functions will be called with `data` and empty\n\t// `operatorData`. See {IERC777Sender} and {IERC777Recipient}.\n\t//\n\t// Emits a {Sent} event.\n\t//\n\t// Requirements\n\t//\n\t// - the caller must have at least `amount` tokens.\n\t// - `recipient` cannot be the zero address.\n\t// - if `recipient` is a contract, it must implement the {IERC777Recipient}\n\t// interface.\n\tSend(recipient std.Address, amount uint64, data []byte)\n\n\t// Destroys `amount` tokens from the caller's account, reducing the\n\t// total supply.\n\t//\n\t// If a send hook is registered for the caller, the corresponding function\n\t// will be called with `data` and empty `operatorData`. See {IERC777Sender}.\n\t//\n\t// Emits a {Burned} event.\n\t//\n\t// Requirements\n\t//\n\t// - the caller must have at least `amount` tokens.\n\tBurn(amount uint64, data []byte)\n\n\t// Returns true if an account is an operator of `tokenHolder`.\n\t// Operators can send and burn tokens on behalf of their owners. All\n\t// accounts are their own operator.\n\t//\n\t// See {operatorSend} and {operatorBurn}.\n\tIsOperatorFor(operator, tokenHolder std.Address) bool\n\n\t// Make an account an operator of the caller.\n\t//\n\t// See {isOperatorFor}.\n\t//\n\t// Emits an {AuthorizedOperator} event.\n\t//\n\t// Requirements\n\t//\n\t// - `operator` cannot be calling address.\n\tAuthorizeOperator(operator std.Address)\n\n\t// Revoke an account's operator status for the caller.\n\t//\n\t// See {isOperatorFor} and {defaultOperators}.\n\t//\n\t// Emits a {RevokedOperator} event.\n\t//\n\t// Requirements\n\t//\n\t// - `operator` cannot be calling address.\n\tRevokeOperator(operators std.Address)\n\n\t// Returns the list of default operators. These accounts are operators\n\t// for all token holders, even if {authorizeOperator} was never called on\n\t// them.\n\t//\n\t// This list is immutable, but individual holders may revoke these via\n\t// {revokeOperator}, in which case {isOperatorFor} will return false.\n\tDefaultOperators() []std.Address\n\n\t// Moves `amount` tokens from `sender` to `recipient`. The caller must\n\t// be an operator of `sender`.\n\t//\n\t// If send or receive hooks are registered for `sender` and `recipient`,\n\t// the corresponding functions will be called with `data` and\n\t// `operatorData`. See {IERC777Sender} and {IERC777Recipient}.\n\t//\n\t// Emits a {Sent} event.\n\t//\n\t// Requirements\n\t//\n\t// - `sender` cannot be the zero address.\n\t// - `sender` must have at least `amount` tokens.\n\t// - the caller must be an operator for `sender`.\n\t// - `recipient` cannot be the zero address.\n\t// - if `recipient` is a contract, it must implement the {IERC777Recipient}\n\t// interface.\n\tOperatorSend(sender, recipient std.Address, amount uint64, data, operatorData []byte)\n\n\t// Destroys `amount` tokens from `account`, reducing the total supply.\n\t// The caller must be an operator of `account`.\n\t//\n\t// If a send hook is registered for `account`, the corresponding function\n\t// will be called with `data` and `operatorData`. See {IERC777Sender}.\n\t//\n\t// Emits a {Burned} event.\n\t//\n\t// Requirements\n\t//\n\t// - `account` cannot be the zero address.\n\t// - `account` must have at least `amount` tokens.\n\t// - the caller must be an operator for `account`.\n\tOperatorBurn(account std.Address, amount uint64, data, operatorData []byte)\n}\n\n// Emitted when `amount` tokens are created by `operator` and assigned to `to`.\n//\n// Note that some additional user `data` and `operatorData` can be logged in the event.\ntype MintedEvent struct {\n\tOperator std.Address\n\tTo std.Address\n\tAmount uint64\n\tData []byte\n\tOperatorData []byte\n}\n\n// Emitted when `operator` destroys `amount` tokens from `account`.\n//\n// Note that some additional user `data` and `operatorData` can be logged in the event.\ntype BurnedEvent struct {\n\tOperator std.Address\n\tFrom std.Address\n\tAmount uint64\n\tData []byte\n\tOperatorData []byte\n}\n\n// Emitted when `operator` is made operator for `tokenHolder`\ntype AuthorizedOperatorEvent struct {\n\tOperator std.Address\n\tTokenHolder std.Address\n}\n\n// Emitted when `operator` is revoked its operator status for `tokenHolder`.\ntype RevokedOperatorEvent struct {\n\tOperator std.Address\n\tTokenHolder std.Address\n}\n\ntype SentEvent struct {\n\tOperator std.Address\n\tFrom std.Address\n\tTo std.Address\n\tAmount uint64\n\tData []byte\n\tOperatorData []byte\n}\n" @@ -1421,10 +1305,6 @@ "name": "rat", "path": "gno.land/p/demo/rat", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/rat\n" - }, { "name": "maths.gno", "body": "package rat\n\nconst (\n\tintSize = 32 \u003c\u003c (^uint(0) \u003e\u003e 63) // 32 or 64\n\n\tMaxInt = 1\u003c\u003c(intSize-1) - 1\n\tMinInt = -1 \u003c\u003c (intSize - 1)\n\tMaxInt8 = 1\u003c\u003c7 - 1\n\tMinInt8 = -1 \u003c\u003c 7\n\tMaxInt16 = 1\u003c\u003c15 - 1\n\tMinInt16 = -1 \u003c\u003c 15\n\tMaxInt32 = 1\u003c\u003c31 - 1\n\tMinInt32 = -1 \u003c\u003c 31\n\tMaxInt64 = 1\u003c\u003c63 - 1\n\tMinInt64 = -1 \u003c\u003c 63\n\tMaxUint = 1\u003c\u003cintSize - 1\n\tMaxUint8 = 1\u003c\u003c8 - 1\n\tMaxUint16 = 1\u003c\u003c16 - 1\n\tMaxUint32 = 1\u003c\u003c32 - 1\n\tMaxUint64 = 1\u003c\u003c64 - 1\n)\n" @@ -1459,10 +1339,6 @@ "name": "users", "path": "gno.land/p/demo/users", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/users\n" - }, { "name": "types.gno", "body": "package users\n\ntype AddressOrName string\n\nfunc (aon AddressOrName) IsName() bool {\n\treturn aon != \"\" \u0026\u0026 aon[0] == '@'\n}\n\nfunc (aon AddressOrName) GetName() (string, bool) {\n\tif len(aon) \u003e= 2 \u0026\u0026 aon[0] == '@' {\n\t\treturn string(aon[1:]), true\n\t}\n\treturn \"\", false\n}\n" @@ -1501,10 +1377,6 @@ "name": "users", "path": "gno.land/r/demo/users", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/users\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n)\n" - }, { "name": "preregister.gno", "body": "package users\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/users\"\n)\n\n// pre-restricted names\nvar preRestrictedNames = []string{\n\t\"bitcoin\", \"cosmos\", \"newtendermint\", \"ethereum\",\n}\n\n// pre-registered users\nvar preRegisteredUsers = []struct {\n\tName string\n\tAddress std.Address\n}{\n\t// system name\n\t{\"archives\", \"g1xlnyjrnf03ju82v0f98ruhpgnquk28knmjfe5k\"}, // -\u003e @r_archives\n\t{\"demo\", \"g13ek2zz9qurzynzvssyc4sthwppnruhnp0gdz8n\"}, // -\u003e @r_demo\n\t{\"gno\", \"g19602kd9tfxrfd60sgreadt9zvdyyuudcyxsz8a\"}, // -\u003e @r_gno\n\t{\"gnoland\", \"g1g3lsfxhvaqgdv4ccemwpnms4fv6t3aq3p5z6u7\"}, // -\u003e @r_gnoland\n\t{\"gnolang\", \"g1yjlnm3z2630gg5mryjd79907e0zx658wxs9hnd\"}, // -\u003e @r_gnolang\n\t{\"gov\", \"g1g73v2anukg4ej7axwqpthsatzrxjsh0wk797da\"}, // -\u003e @r_gov\n\t{\"nt\", \"g15ge0ae9077eh40erwrn2eq0xw6wupwqthpv34l\"}, // -\u003e @r_nt\n\t{\"sys\", \"g1r929wt2qplfawe4lvqv9zuwfdcz4vxdun7qh8l\"}, // -\u003e @r_sys\n\t{\"x\", \"g164sdpew3c2t3rvxj3kmfv7c7ujlvcw2punzzuz\"}, // -\u003e @r_x\n\n\t// Onbloc\n\t{\"gnoswap\", \"g1lmvrrrr4er2us84h2732sru76c9zl2nvknha8c\"}, // -\u003e @r_gnoswap\n\t{\"onbloc\", \"g12vx7dn3dqq89mz550zwunvg4qw6epq73d9csay\"}, // -\u003e @r_onbloc\n}\n\nfunc init() {\n\t// add pre-registered users\n\tfor _, res := range preRegisteredUsers {\n\t\t// assert not already registered.\n\t\t_, ok := name2User.Get(res.Name)\n\t\tif ok {\n\t\t\tpanic(\"name already registered\")\n\t\t}\n\n\t\t_, ok = addr2User.Get(res.Address.String())\n\t\tif ok {\n\t\t\tpanic(\"address already registered\")\n\t\t}\n\n\t\tcounter++\n\t\tuser := \u0026users.User{\n\t\t\tAddress: res.Address,\n\t\t\tName: res.Name,\n\t\t\tProfile: \"\",\n\t\t\tNumber: counter,\n\t\t\tInvites: int(0),\n\t\t\tInviter: admin,\n\t\t}\n\t\tname2User.Set(res.Name, user)\n\t\taddr2User.Set(res.Address.String(), user)\n\t}\n\n\t// add pre-restricted names\n\tfor _, name := range preRestrictedNames {\n\t\tif _, ok := name2User.Get(name); ok {\n\t\t\tpanic(\"name already registered\")\n\t\t}\n\n\t\trestricted.Set(name, true)\n\t}\n}\n" @@ -1611,10 +1483,6 @@ "name": "boards.gno", "body": "package boards\n\nimport (\n\t\"regexp\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\n//----------------------------------------\n// Realm (package) state\n\nvar (\n\tgBoards avl.Tree // id -\u003e *Board\n\tgBoardsCtr int // increments Board.id\n\tgBoardsByName avl.Tree // name -\u003e *Board\n\tgDefaultAnonFee = 100000000 // minimum fee required if anonymous\n)\n\n//----------------------------------------\n// Constants\n\nvar reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{2,29}$`)\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/boards\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" - }, { "name": "misc.gno", "body": "package boards\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/r/demo/users\"\n)\n\n//----------------------------------------\n// private utility methods\n// XXX ensure these cannot be called from public.\n\nfunc getBoard(bid BoardID) *Board {\n\tbidkey := boardIDKey(bid)\n\tboard_, exists := gBoards.Get(bidkey)\n\tif !exists {\n\t\treturn nil\n\t}\n\tboard := board_.(*Board)\n\treturn board\n}\n\nfunc incGetBoardID() BoardID {\n\tgBoardsCtr++\n\treturn BoardID(gBoardsCtr)\n}\n\nfunc padLeft(str string, length int) string {\n\tif len(str) \u003e= length {\n\t\treturn str\n\t} else {\n\t\treturn strings.Repeat(\" \", length-len(str)) + str\n\t}\n}\n\nfunc padZero(u64 uint64, length int) string {\n\tstr := strconv.Itoa(int(u64))\n\tif len(str) \u003e= length {\n\t\treturn str\n\t} else {\n\t\treturn strings.Repeat(\"0\", length-len(str)) + str\n\t}\n}\n\nfunc boardIDKey(bid BoardID) string {\n\treturn padZero(uint64(bid), 10)\n}\n\nfunc postIDKey(pid PostID) string {\n\treturn padZero(uint64(pid), 10)\n}\n\nfunc indentBody(indent string, body string) string {\n\tlines := strings.Split(body, \"\\n\")\n\tres := \"\"\n\tfor i, line := range lines {\n\t\tif i \u003e 0 {\n\t\t\tres += \"\\n\"\n\t\t}\n\t\tres += indent + line\n\t}\n\treturn res\n}\n\n// NOTE: length must be greater than 3.\nfunc summaryOf(str string, length int) string {\n\tlines := strings.SplitN(str, \"\\n\", 2)\n\tline := lines[0]\n\tif len(line) \u003e length {\n\t\tline = line[:(length-3)] + \"...\"\n\t} else if len(lines) \u003e 1 {\n\t\t// len(line) \u003c= 80\n\t\tline = line + \"...\"\n\t}\n\treturn line\n}\n\nfunc displayAddressMD(addr std.Address) string {\n\tuser := users.GetUserByAddress(addr)\n\tif user == nil {\n\t\treturn \"[\" + addr.String() + \"](/r/demo/users:\" + addr.String() + \")\"\n\t} else {\n\t\treturn \"[@\" + user.Name + \"](/r/demo/users:\" + user.Name + \")\"\n\t}\n}\n\nfunc usernameOf(addr std.Address) string {\n\tuser := users.GetUserByAddress(addr)\n\tif user == nil {\n\t\treturn \"\"\n\t}\n\treturn user.Name\n}\n" @@ -1797,10 +1665,6 @@ "name": "groups", "path": "gno.land/p/demo/groups", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/groups\n\nrequire (\n\tgno.land/p/demo/rat v0.0.0-latest\n\tgno.land/r/demo/boards v0.0.0-latest\n)\n" - }, { "name": "groups.gno", "body": "package groups\n\nimport \"gno.land/r/demo/boards\"\n\n// TODO implement something and test.\ntype Group struct {\n\tBoard *boards.Board\n}\n" @@ -1887,10 +1751,6 @@ "name": "error.gno", "body": "package uint256\n\nimport (\n\t\"errors\"\n)\n\nvar (\n\tErrEmptyString = errors.New(\"empty hex string\")\n\tErrSyntax = errors.New(\"invalid hex string\")\n\tErrRange = errors.New(\"number out of range\")\n\tErrMissingPrefix = errors.New(\"hex string without 0x prefix\")\n\tErrEmptyNumber = errors.New(\"hex string \\\"0x\\\"\")\n\tErrLeadingZero = errors.New(\"hex number with leading zero digits\")\n\tErrBig256Range = errors.New(\"hex number \u003e 256 bits\")\n\tErrBadBufferLength = errors.New(\"bad ssz buffer length\")\n\tErrBadEncodedLength = errors.New(\"bad ssz encoded length\")\n\tErrInvalidBase = errors.New(\"invalid base\")\n\tErrInvalidBitSize = errors.New(\"invalid bit size\")\n)\n\ntype u256Error struct {\n\tfn string // function name\n\tinput string\n\terr error\n}\n\nfunc (e *u256Error) Error() string {\n\treturn e.fn + \": \" + e.input + \": \" + e.err.Error()\n}\n\nfunc (e *u256Error) Unwrap() error {\n\treturn e.err\n}\n\nfunc errEmptyString(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrEmptyString}\n}\n\nfunc errSyntax(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrSyntax}\n}\n\nfunc errMissingPrefix(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrMissingPrefix}\n}\n\nfunc errEmptyNumber(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrEmptyNumber}\n}\n\nfunc errLeadingZero(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrLeadingZero}\n}\n\nfunc errRange(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrRange}\n}\n\nfunc errBig256Range(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrBig256Range}\n}\n\nfunc errBadBufferLength(fn, input string) error {\n\treturn \u0026u256Error{fn: fn, input: input, err: ErrBadBufferLength}\n}\n\nfunc errInvalidBase(fn string, base int) error {\n\treturn \u0026u256Error{fn: fn, input: string(base), err: ErrInvalidBase}\n}\n\nfunc errInvalidBitSize(fn string, bitSize int) error {\n\treturn \u0026u256Error{fn: fn, input: string(bitSize), err: ErrInvalidBitSize}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/uint256\n" - }, { "name": "mod.gno", "body": "package uint256\n\nimport (\n\t\"math/bits\"\n)\n\n// Some utility functions\n\n// Reciprocal computes a 320-bit value representing 1/m\n//\n// Notes:\n// - specialized for m.arr[3] != 0, hence limited to 2^192 \u003c= m \u003c 2^256\n// - returns zero if m.arr[3] == 0\n// - starts with a 32-bit division, refines with newton-raphson iterations\nfunc Reciprocal(m *Uint) (mu [5]uint64) {\n\tif m.arr[3] == 0 {\n\t\treturn mu\n\t}\n\n\ts := bits.LeadingZeros64(m.arr[3]) // Replace with leadingZeros(m) for general case\n\tp := 255 - s // floor(log_2(m)), m\u003e0\n\n\t// 0 or a power of 2?\n\n\t// Check if at least one bit is set in m.arr[2], m.arr[1] or m.arr[0],\n\t// or at least two bits in m.arr[3]\n\n\tif m.arr[0]|m.arr[1]|m.arr[2]|(m.arr[3]\u0026(m.arr[3]-1)) == 0 {\n\n\t\tmu[4] = ^uint64(0) \u003e\u003e uint(p\u002663)\n\t\tmu[3] = ^uint64(0)\n\t\tmu[2] = ^uint64(0)\n\t\tmu[1] = ^uint64(0)\n\t\tmu[0] = ^uint64(0)\n\n\t\treturn mu\n\t}\n\n\t// Maximise division precision by left-aligning divisor\n\n\tvar (\n\t\ty Uint // left-aligned copy of m\n\t\tr0 uint32 // estimate of 2^31/y\n\t)\n\n\ty.Lsh(m, uint(s)) // 1/2 \u003c y \u003c 1\n\n\t// Extract most significant 32 bits\n\n\tyh := uint32(y.arr[3] \u003e\u003e 32)\n\n\tif yh == 0x80000000 { // Avoid overflow in division\n\t\tr0 = 0xffffffff\n\t} else {\n\t\tr0, _ = bits.Div32(0x80000000, 0, yh)\n\t}\n\n\t// First iteration: 32 -\u003e 64\n\n\tt1 := uint64(r0) // 2^31/y\n\tt1 *= t1 // 2^62/y^2\n\tt1, _ = bits.Mul64(t1, y.arr[3]) // 2^62/y^2 * 2^64/y / 2^64 = 2^62/y\n\n\tr1 := uint64(r0) \u003c\u003c 32 // 2^63/y\n\tr1 -= t1 // 2^63/y - 2^62/y = 2^62/y\n\tr1 *= 2 // 2^63/y\n\n\tif (r1 | (y.arr[3] \u003c\u003c 1)) == 0 {\n\t\tr1 = ^uint64(0)\n\t}\n\n\t// Second iteration: 64 -\u003e 128\n\n\t// square: 2^126/y^2\n\ta2h, a2l := bits.Mul64(r1, r1)\n\n\t// multiply by y: e2h:e2l:b2h = 2^126/y^2 * 2^128/y / 2^128 = 2^126/y\n\tb2h, _ := bits.Mul64(a2l, y.arr[2])\n\tc2h, c2l := bits.Mul64(a2l, y.arr[3])\n\td2h, d2l := bits.Mul64(a2h, y.arr[2])\n\te2h, e2l := bits.Mul64(a2h, y.arr[3])\n\n\tb2h, c := bits.Add64(b2h, c2l, 0)\n\te2l, c = bits.Add64(e2l, c2h, c)\n\te2h, _ = bits.Add64(e2h, 0, c)\n\n\t_, c = bits.Add64(b2h, d2l, 0)\n\te2l, c = bits.Add64(e2l, d2h, c)\n\te2h, _ = bits.Add64(e2h, 0, c)\n\n\t// subtract: t2h:t2l = 2^127/y - 2^126/y = 2^126/y\n\tt2l, b := bits.Sub64(0, e2l, 0)\n\tt2h, _ := bits.Sub64(r1, e2h, b)\n\n\t// double: r2h:r2l = 2^127/y\n\tr2l, c := bits.Add64(t2l, t2l, 0)\n\tr2h, _ := bits.Add64(t2h, t2h, c)\n\n\tif (r2h | r2l | (y.arr[3] \u003c\u003c 1)) == 0 {\n\t\tr2h = ^uint64(0)\n\t\tr2l = ^uint64(0)\n\t}\n\n\t// Third iteration: 128 -\u003e 192\n\n\t// square r2 (keep 256 bits): 2^190/y^2\n\ta3h, a3l := bits.Mul64(r2l, r2l)\n\tb3h, b3l := bits.Mul64(r2l, r2h)\n\tc3h, c3l := bits.Mul64(r2h, r2h)\n\n\ta3h, c = bits.Add64(a3h, b3l, 0)\n\tc3l, c = bits.Add64(c3l, b3h, c)\n\tc3h, _ = bits.Add64(c3h, 0, c)\n\n\ta3h, c = bits.Add64(a3h, b3l, 0)\n\tc3l, c = bits.Add64(c3l, b3h, c)\n\tc3h, _ = bits.Add64(c3h, 0, c)\n\n\t// multiply by y: q = 2^190/y^2 * 2^192/y / 2^192 = 2^190/y\n\n\tx0 := a3l\n\tx1 := a3h\n\tx2 := c3l\n\tx3 := c3h\n\n\tvar q0, q1, q2, q3, q4, t0 uint64\n\n\tq0, _ = bits.Mul64(x2, y.arr[0])\n\tq1, t0 = bits.Mul64(x3, y.arr[0])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, _ = bits.Add64(q1, 0, c)\n\n\tt1, _ = bits.Mul64(x1, y.arr[1])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tq2, t0 = bits.Mul64(x3, y.arr[1])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, _ = bits.Add64(q2, 0, c)\n\n\tt1, t0 = bits.Mul64(x2, y.arr[1])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tq2, _ = bits.Add64(q2, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[2])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tq3, t0 = bits.Mul64(x3, y.arr[2])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, _ = bits.Add64(q3, 0, c)\n\n\tt1, _ = bits.Mul64(x0, y.arr[2])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tt1, t0 = bits.Mul64(x2, y.arr[2])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, c = bits.Add64(q2, t1, c)\n\tq3, _ = bits.Add64(q3, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[3])\n\tq1, c = bits.Add64(q1, t0, 0)\n\tq2, c = bits.Add64(q2, t1, c)\n\tq4, t0 = bits.Mul64(x3, y.arr[3])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, _ = bits.Add64(q4, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, y.arr[3])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tt1, t0 = bits.Mul64(x2, y.arr[3])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, c = bits.Add64(q3, t1, c)\n\tq4, _ = bits.Add64(q4, 0, c)\n\n\t// subtract: t3 = 2^191/y - 2^190/y = 2^190/y\n\t_, b = bits.Sub64(0, q0, 0)\n\t_, b = bits.Sub64(0, q1, b)\n\tt3l, b := bits.Sub64(0, q2, b)\n\tt3m, b := bits.Sub64(r2l, q3, b)\n\tt3h, _ := bits.Sub64(r2h, q4, b)\n\n\t// double: r3 = 2^191/y\n\tr3l, c := bits.Add64(t3l, t3l, 0)\n\tr3m, c := bits.Add64(t3m, t3m, c)\n\tr3h, _ := bits.Add64(t3h, t3h, c)\n\n\t// Fourth iteration: 192 -\u003e 320\n\n\t// square r3\n\n\ta4h, a4l := bits.Mul64(r3l, r3l)\n\tb4h, b4l := bits.Mul64(r3l, r3m)\n\tc4h, c4l := bits.Mul64(r3l, r3h)\n\td4h, d4l := bits.Mul64(r3m, r3m)\n\te4h, e4l := bits.Mul64(r3m, r3h)\n\tf4h, f4l := bits.Mul64(r3h, r3h)\n\n\tb4h, c = bits.Add64(b4h, c4l, 0)\n\te4l, c = bits.Add64(e4l, c4h, c)\n\te4h, _ = bits.Add64(e4h, 0, c)\n\n\ta4h, c = bits.Add64(a4h, b4l, 0)\n\td4l, c = bits.Add64(d4l, b4h, c)\n\td4h, c = bits.Add64(d4h, e4l, c)\n\tf4l, c = bits.Add64(f4l, e4h, c)\n\tf4h, _ = bits.Add64(f4h, 0, c)\n\n\ta4h, c = bits.Add64(a4h, b4l, 0)\n\td4l, c = bits.Add64(d4l, b4h, c)\n\td4h, c = bits.Add64(d4h, e4l, c)\n\tf4l, c = bits.Add64(f4l, e4h, c)\n\tf4h, _ = bits.Add64(f4h, 0, c)\n\n\t// multiply by y\n\n\tx1, x0 = bits.Mul64(d4h, y.arr[0])\n\tx3, x2 = bits.Mul64(f4h, y.arr[0])\n\tt1, t0 = bits.Mul64(f4l, y.arr[0])\n\tx1, c = bits.Add64(x1, t0, 0)\n\tx2, c = bits.Add64(x2, t1, c)\n\tx3, _ = bits.Add64(x3, 0, c)\n\n\tt1, t0 = bits.Mul64(d4h, y.arr[1])\n\tx1, c = bits.Add64(x1, t0, 0)\n\tx2, c = bits.Add64(x2, t1, c)\n\tx4, t0 := bits.Mul64(f4h, y.arr[1])\n\tx3, c = bits.Add64(x3, t0, c)\n\tx4, _ = bits.Add64(x4, 0, c)\n\tt1, t0 = bits.Mul64(d4l, y.arr[1])\n\tx0, c = bits.Add64(x0, t0, 0)\n\tx1, c = bits.Add64(x1, t1, c)\n\tt1, t0 = bits.Mul64(f4l, y.arr[1])\n\tx2, c = bits.Add64(x2, t0, c)\n\tx3, c = bits.Add64(x3, t1, c)\n\tx4, _ = bits.Add64(x4, 0, c)\n\n\tt1, t0 = bits.Mul64(a4h, y.arr[2])\n\tx0, c = bits.Add64(x0, t0, 0)\n\tx1, c = bits.Add64(x1, t1, c)\n\tt1, t0 = bits.Mul64(d4h, y.arr[2])\n\tx2, c = bits.Add64(x2, t0, c)\n\tx3, c = bits.Add64(x3, t1, c)\n\tx5, t0 := bits.Mul64(f4h, y.arr[2])\n\tx4, c = bits.Add64(x4, t0, c)\n\tx5, _ = bits.Add64(x5, 0, c)\n\tt1, t0 = bits.Mul64(d4l, y.arr[2])\n\tx1, c = bits.Add64(x1, t0, 0)\n\tx2, c = bits.Add64(x2, t1, c)\n\tt1, t0 = bits.Mul64(f4l, y.arr[2])\n\tx3, c = bits.Add64(x3, t0, c)\n\tx4, c = bits.Add64(x4, t1, c)\n\tx5, _ = bits.Add64(x5, 0, c)\n\n\tt1, t0 = bits.Mul64(a4h, y.arr[3])\n\tx1, c = bits.Add64(x1, t0, 0)\n\tx2, c = bits.Add64(x2, t1, c)\n\tt1, t0 = bits.Mul64(d4h, y.arr[3])\n\tx3, c = bits.Add64(x3, t0, c)\n\tx4, c = bits.Add64(x4, t1, c)\n\tx6, t0 := bits.Mul64(f4h, y.arr[3])\n\tx5, c = bits.Add64(x5, t0, c)\n\tx6, _ = bits.Add64(x6, 0, c)\n\tt1, t0 = bits.Mul64(a4l, y.arr[3])\n\tx0, c = bits.Add64(x0, t0, 0)\n\tx1, c = bits.Add64(x1, t1, c)\n\tt1, t0 = bits.Mul64(d4l, y.arr[3])\n\tx2, c = bits.Add64(x2, t0, c)\n\tx3, c = bits.Add64(x3, t1, c)\n\tt1, t0 = bits.Mul64(f4l, y.arr[3])\n\tx4, c = bits.Add64(x4, t0, c)\n\tx5, c = bits.Add64(x5, t1, c)\n\tx6, _ = bits.Add64(x6, 0, c)\n\n\t// subtract\n\t_, b = bits.Sub64(0, x0, 0)\n\t_, b = bits.Sub64(0, x1, b)\n\tr4l, b := bits.Sub64(0, x2, b)\n\tr4k, b := bits.Sub64(0, x3, b)\n\tr4j, b := bits.Sub64(r3l, x4, b)\n\tr4i, b := bits.Sub64(r3m, x5, b)\n\tr4h, _ := bits.Sub64(r3h, x6, b)\n\n\t// Multiply candidate for 1/4y by y, with full precision\n\n\tx0 = r4l\n\tx1 = r4k\n\tx2 = r4j\n\tx3 = r4i\n\tx4 = r4h\n\n\tq1, q0 = bits.Mul64(x0, y.arr[0])\n\tq3, q2 = bits.Mul64(x2, y.arr[0])\n\tq5, q4 := bits.Mul64(x4, y.arr[0])\n\n\tt1, t0 = bits.Mul64(x1, y.arr[0])\n\tq1, c = bits.Add64(q1, t0, 0)\n\tq2, c = bits.Add64(q2, t1, c)\n\tt1, t0 = bits.Mul64(x3, y.arr[0])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, c = bits.Add64(q4, t1, c)\n\tq5, _ = bits.Add64(q5, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, y.arr[1])\n\tq1, c = bits.Add64(q1, t0, 0)\n\tq2, c = bits.Add64(q2, t1, c)\n\tt1, t0 = bits.Mul64(x2, y.arr[1])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, c = bits.Add64(q4, t1, c)\n\tq6, t0 := bits.Mul64(x4, y.arr[1])\n\tq5, c = bits.Add64(q5, t0, c)\n\tq6, _ = bits.Add64(q6, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[1])\n\tq2, c = bits.Add64(q2, t0, 0)\n\tq3, c = bits.Add64(q3, t1, c)\n\tt1, t0 = bits.Mul64(x3, y.arr[1])\n\tq4, c = bits.Add64(q4, t0, c)\n\tq5, c = bits.Add64(q5, t1, c)\n\tq6, _ = bits.Add64(q6, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, y.arr[2])\n\tq2, c = bits.Add64(q2, t0, 0)\n\tq3, c = bits.Add64(q3, t1, c)\n\tt1, t0 = bits.Mul64(x2, y.arr[2])\n\tq4, c = bits.Add64(q4, t0, c)\n\tq5, c = bits.Add64(q5, t1, c)\n\tq7, t0 := bits.Mul64(x4, y.arr[2])\n\tq6, c = bits.Add64(q6, t0, c)\n\tq7, _ = bits.Add64(q7, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[2])\n\tq3, c = bits.Add64(q3, t0, 0)\n\tq4, c = bits.Add64(q4, t1, c)\n\tt1, t0 = bits.Mul64(x3, y.arr[2])\n\tq5, c = bits.Add64(q5, t0, c)\n\tq6, c = bits.Add64(q6, t1, c)\n\tq7, _ = bits.Add64(q7, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, y.arr[3])\n\tq3, c = bits.Add64(q3, t0, 0)\n\tq4, c = bits.Add64(q4, t1, c)\n\tt1, t0 = bits.Mul64(x2, y.arr[3])\n\tq5, c = bits.Add64(q5, t0, c)\n\tq6, c = bits.Add64(q6, t1, c)\n\tq8, t0 := bits.Mul64(x4, y.arr[3])\n\tq7, c = bits.Add64(q7, t0, c)\n\tq8, _ = bits.Add64(q8, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, y.arr[3])\n\tq4, c = bits.Add64(q4, t0, 0)\n\tq5, c = bits.Add64(q5, t1, c)\n\tt1, t0 = bits.Mul64(x3, y.arr[3])\n\tq6, c = bits.Add64(q6, t0, c)\n\tq7, c = bits.Add64(q7, t1, c)\n\tq8, _ = bits.Add64(q8, 0, c)\n\n\t// Final adjustment\n\n\t// subtract q from 1/4\n\t_, b = bits.Sub64(0, q0, 0)\n\t_, b = bits.Sub64(0, q1, b)\n\t_, b = bits.Sub64(0, q2, b)\n\t_, b = bits.Sub64(0, q3, b)\n\t_, b = bits.Sub64(0, q4, b)\n\t_, b = bits.Sub64(0, q5, b)\n\t_, b = bits.Sub64(0, q6, b)\n\t_, b = bits.Sub64(0, q7, b)\n\t_, b = bits.Sub64(uint64(1)\u003c\u003c62, q8, b)\n\n\t// decrement the result\n\tx0, t := bits.Sub64(r4l, 1, 0)\n\tx1, t = bits.Sub64(r4k, 0, t)\n\tx2, t = bits.Sub64(r4j, 0, t)\n\tx3, t = bits.Sub64(r4i, 0, t)\n\tx4, _ = bits.Sub64(r4h, 0, t)\n\n\t// commit the decrement if the subtraction underflowed (reciprocal was too large)\n\tif b != 0 {\n\t\tr4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0\n\t}\n\n\t// Shift to correct bit alignment, truncating excess bits\n\n\tp = (p \u0026 63) - 1\n\n\tx0, c = bits.Add64(r4l, r4l, 0)\n\tx1, c = bits.Add64(r4k, r4k, c)\n\tx2, c = bits.Add64(r4j, r4j, c)\n\tx3, c = bits.Add64(r4i, r4i, c)\n\tx4, _ = bits.Add64(r4h, r4h, c)\n\n\tif p \u003c 0 {\n\t\tr4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0\n\t\tp = 0 // avoid negative shift below\n\t}\n\n\t{\n\t\tr := uint(p) // right shift\n\t\tl := uint(64 - r) // left shift\n\n\t\tx0 = (r4l \u003e\u003e r) | (r4k \u003c\u003c l)\n\t\tx1 = (r4k \u003e\u003e r) | (r4j \u003c\u003c l)\n\t\tx2 = (r4j \u003e\u003e r) | (r4i \u003c\u003c l)\n\t\tx3 = (r4i \u003e\u003e r) | (r4h \u003c\u003c l)\n\t\tx4 = (r4h \u003e\u003e r)\n\t}\n\n\tif p \u003e 0 {\n\t\tr4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0\n\t}\n\n\tmu[0] = r4l\n\tmu[1] = r4k\n\tmu[2] = r4j\n\tmu[3] = r4i\n\tmu[4] = r4h\n\n\treturn mu\n}\n\n// reduce4 computes the least non-negative residue of x modulo m\n//\n// requires a four-word modulus (m.arr[3] \u003e 1) and its inverse (mu)\nfunc reduce4(x [8]uint64, m *Uint, mu [5]uint64) (z Uint) {\n\t// NB: Most variable names in the comments match the pseudocode for\n\t// \tBarrett reduction in the Handbook of Applied Cryptography.\n\n\t// q1 = x/2^192\n\n\tx0 := x[3]\n\tx1 := x[4]\n\tx2 := x[5]\n\tx3 := x[6]\n\tx4 := x[7]\n\n\t// q2 = q1 * mu; q3 = q2 / 2^320\n\n\tvar q0, q1, q2, q3, q4, q5, t0, t1, c uint64\n\n\tq0, _ = bits.Mul64(x3, mu[0])\n\tq1, t0 = bits.Mul64(x4, mu[0])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, _ = bits.Add64(q1, 0, c)\n\n\tt1, _ = bits.Mul64(x2, mu[1])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tq2, t0 = bits.Mul64(x4, mu[1])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, _ = bits.Add64(q2, 0, c)\n\n\tt1, t0 = bits.Mul64(x3, mu[1])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tq2, _ = bits.Add64(q2, 0, c)\n\n\tt1, t0 = bits.Mul64(x2, mu[2])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tq3, t0 = bits.Mul64(x4, mu[2])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, _ = bits.Add64(q3, 0, c)\n\n\tt1, _ = bits.Mul64(x1, mu[2])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tt1, t0 = bits.Mul64(x3, mu[2])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, c = bits.Add64(q2, t1, c)\n\tq3, _ = bits.Add64(q3, 0, c)\n\n\tt1, _ = bits.Mul64(x0, mu[3])\n\tq0, c = bits.Add64(q0, t1, 0)\n\tt1, t0 = bits.Mul64(x2, mu[3])\n\tq1, c = bits.Add64(q1, t0, c)\n\tq2, c = bits.Add64(q2, t1, c)\n\tq4, t0 = bits.Mul64(x4, mu[3])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, _ = bits.Add64(q4, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, mu[3])\n\tq0, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tt1, t0 = bits.Mul64(x3, mu[3])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, c = bits.Add64(q3, t1, c)\n\tq4, _ = bits.Add64(q4, 0, c)\n\n\tt1, t0 = bits.Mul64(x0, mu[4])\n\t_, c = bits.Add64(q0, t0, 0)\n\tq1, c = bits.Add64(q1, t1, c)\n\tt1, t0 = bits.Mul64(x2, mu[4])\n\tq2, c = bits.Add64(q2, t0, c)\n\tq3, c = bits.Add64(q3, t1, c)\n\tq5, t0 = bits.Mul64(x4, mu[4])\n\tq4, c = bits.Add64(q4, t0, c)\n\tq5, _ = bits.Add64(q5, 0, c)\n\n\tt1, t0 = bits.Mul64(x1, mu[4])\n\tq1, c = bits.Add64(q1, t0, 0)\n\tq2, c = bits.Add64(q2, t1, c)\n\tt1, t0 = bits.Mul64(x3, mu[4])\n\tq3, c = bits.Add64(q3, t0, c)\n\tq4, c = bits.Add64(q4, t1, c)\n\tq5, _ = bits.Add64(q5, 0, c)\n\n\t// Drop the fractional part of q3\n\n\tq0 = q1\n\tq1 = q2\n\tq2 = q3\n\tq3 = q4\n\tq4 = q5\n\n\t// r1 = x mod 2^320\n\n\tx0 = x[0]\n\tx1 = x[1]\n\tx2 = x[2]\n\tx3 = x[3]\n\tx4 = x[4]\n\n\t// r2 = q3 * m mod 2^320\n\n\tvar r0, r1, r2, r3, r4 uint64\n\n\tr4, r3 = bits.Mul64(q0, m.arr[3])\n\t_, t0 = bits.Mul64(q1, m.arr[3])\n\tr4, _ = bits.Add64(r4, t0, 0)\n\n\tt1, r2 = bits.Mul64(q0, m.arr[2])\n\tr3, c = bits.Add64(r3, t1, 0)\n\t_, t0 = bits.Mul64(q2, m.arr[2])\n\tr4, _ = bits.Add64(r4, t0, c)\n\n\tt1, t0 = bits.Mul64(q1, m.arr[2])\n\tr3, c = bits.Add64(r3, t0, 0)\n\tr4, _ = bits.Add64(r4, t1, c)\n\n\tt1, r1 = bits.Mul64(q0, m.arr[1])\n\tr2, c = bits.Add64(r2, t1, 0)\n\tt1, t0 = bits.Mul64(q2, m.arr[1])\n\tr3, c = bits.Add64(r3, t0, c)\n\tr4, _ = bits.Add64(r4, t1, c)\n\n\tt1, t0 = bits.Mul64(q1, m.arr[1])\n\tr2, c = bits.Add64(r2, t0, 0)\n\tr3, c = bits.Add64(r3, t1, c)\n\t_, t0 = bits.Mul64(q3, m.arr[1])\n\tr4, _ = bits.Add64(r4, t0, c)\n\n\tt1, r0 = bits.Mul64(q0, m.arr[0])\n\tr1, c = bits.Add64(r1, t1, 0)\n\tt1, t0 = bits.Mul64(q2, m.arr[0])\n\tr2, c = bits.Add64(r2, t0, c)\n\tr3, c = bits.Add64(r3, t1, c)\n\t_, t0 = bits.Mul64(q4, m.arr[0])\n\tr4, _ = bits.Add64(r4, t0, c)\n\n\tt1, t0 = bits.Mul64(q1, m.arr[0])\n\tr1, c = bits.Add64(r1, t0, 0)\n\tr2, c = bits.Add64(r2, t1, c)\n\tt1, t0 = bits.Mul64(q3, m.arr[0])\n\tr3, c = bits.Add64(r3, t0, c)\n\tr4, _ = bits.Add64(r4, t1, c)\n\n\t// r = r1 - r2\n\n\tvar b uint64\n\n\tr0, b = bits.Sub64(x0, r0, 0)\n\tr1, b = bits.Sub64(x1, r1, b)\n\tr2, b = bits.Sub64(x2, r2, b)\n\tr3, b = bits.Sub64(x3, r3, b)\n\tr4, b = bits.Sub64(x4, r4, b)\n\n\t// if r\u003c0 then r+=m\n\n\tif b != 0 {\n\t\tr0, c = bits.Add64(r0, m.arr[0], 0)\n\t\tr1, c = bits.Add64(r1, m.arr[1], c)\n\t\tr2, c = bits.Add64(r2, m.arr[2], c)\n\t\tr3, c = bits.Add64(r3, m.arr[3], c)\n\t\tr4, _ = bits.Add64(r4, 0, c)\n\t}\n\n\t// while (r\u003e=m) r-=m\n\n\tfor {\n\t\t// q = r - m\n\t\tq0, b = bits.Sub64(r0, m.arr[0], 0)\n\t\tq1, b = bits.Sub64(r1, m.arr[1], b)\n\t\tq2, b = bits.Sub64(r2, m.arr[2], b)\n\t\tq3, b = bits.Sub64(r3, m.arr[3], b)\n\t\tq4, b = bits.Sub64(r4, 0, b)\n\n\t\t// if borrow break\n\t\tif b != 0 {\n\t\t\tbreak\n\t\t}\n\n\t\t// r = q\n\t\tr4, r3, r2, r1, r0 = q4, q3, q2, q1, q0\n\t}\n\n\tz.arr[3], z.arr[2], z.arr[1], z.arr[0] = r3, r2, r1, r0\n\n\treturn z\n}\n" @@ -1977,10 +1837,6 @@ "name": "conversion_test.gno", "body": "package int256\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/uint256\"\n)\n\nfunc TestSetInt64(t *testing.T) {\n\ttests := []struct {\n\t\tx int64\n\t\twant string\n\t}{\n\t\t{0, \"0\"},\n\t\t{1, \"1\"},\n\t\t{-1, \"-1\"},\n\t\t{9223372036854775807, \"9223372036854775807\"},\n\t\t{-9223372036854775808, \"-9223372036854775808\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tvar z Int\n\t\tz.SetInt64(tc.x)\n\n\t\tgot := z.ToString()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"SetInt64(%d) = %s, want %s\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestSetUint64(t *testing.T) {\n\ttests := []struct {\n\t\tx uint64\n\t\twant string\n\t}{\n\t\t{0, \"0\"},\n\t\t{1, \"1\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tvar z Int\n\t\tz.SetUint64(tc.x)\n\n\t\tgot := z.ToString()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"SetUint64(%d) = %s, want %s\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestUint64(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant uint64\n\t}{\n\t\t{\"0\", 0},\n\t\t{\"1\", 1},\n\t\t{\"9223372036854775807\", 9223372036854775807},\n\t\t{\"9223372036854775808\", 9223372036854775808},\n\t\t{\"18446744073709551615\", 18446744073709551615},\n\t\t{\"18446744073709551616\", 0},\n\t\t{\"18446744073709551617\", 1},\n\t\t{\"-1\", 1},\n\t\t{\"-18446744073709551615\", 18446744073709551615},\n\t\t{\"-18446744073709551616\", 0},\n\t\t{\"-18446744073709551617\", 1},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\n\t\tgot := z.Uint64()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Uint64(%s) = %d, want %d\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestInt64(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant int64\n\t}{\n\t\t{\"0\", 0},\n\t\t{\"1\", 1},\n\t\t{\"9223372036854775807\", 9223372036854775807},\n\t\t{\"18446744073709551616\", 0},\n\t\t{\"18446744073709551617\", 1},\n\t\t{\"-1\", -1},\n\t\t{\"-9223372036854775808\", -9223372036854775808},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\n\t\tgot := z.Int64()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Uint64(%s) = %d, want %d\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestNeg(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant string\n\t}{\n\t\t{\"0\", \"0\"},\n\t\t{\"1\", \"-1\"},\n\t\t{\"-1\", \"1\"},\n\t\t{\"9223372036854775807\", \"-9223372036854775807\"},\n\t\t{\"-18446744073709551615\", \"18446744073709551615\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\t\tz.Neg(z)\n\n\t\tgot := z.ToString()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Neg(%s) = %s, want %s\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestSet(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant string\n\t}{\n\t\t{\"0\", \"0\"},\n\t\t{\"1\", \"1\"},\n\t\t{\"-1\", \"-1\"},\n\t\t{\"9223372036854775807\", \"9223372036854775807\"},\n\t\t{\"-18446744073709551615\", \"-18446744073709551615\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tz := MustFromDecimal(tc.x)\n\t\tz.Set(z)\n\n\t\tgot := z.ToString()\n\t\tif got != tc.want {\n\t\t\tt.Errorf(\"Set(%s) = %s, want %s\", tc.x, got, tc.want)\n\t\t}\n\t}\n}\n\nfunc TestSetUint256(t *testing.T) {\n\ttests := []struct {\n\t\tx string\n\t\twant string\n\t}{\n\t\t{\"0\", \"0\"},\n\t\t{\"1\", \"1\"},\n\t\t{\"9223372036854775807\", \"9223372036854775807\"},\n\t\t{\"18446744073709551615\", \"18446744073709551615\"},\n\t}\n\n\tfor _, tc := range tests {\n\t\tgot := New()\n\n\t\tz := uint256.MustFromDecimal(tc.x)\n\t\tgot.SetUint256(z)\n\n\t\tif got.ToString() != tc.want {\n\t\t\tt.Errorf(\"SetUint256(%s) = %s, want %s\", tc.x, got.ToString(), tc.want)\n\t\t}\n\t}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/int256\n\nrequire gno.land/p/demo/uint256 v0.0.0-latest\n" - }, { "name": "int256.gno", "body": "// This package provides a 256-bit signed integer type, Int, and associated functions.\npackage int256\n\nimport (\n\t\"gno.land/p/demo/uint256\"\n)\n\nvar one = uint256.NewUint(1)\n\ntype Int struct {\n\tabs *uint256.Uint\n\tneg bool\n}\n\n// Zero returns a new Int set to 0.\nfunc Zero() *Int {\n\treturn NewInt(0)\n}\n\n// One returns a new Int set to 1.\nfunc One() *Int {\n\treturn NewInt(1)\n}\n\n// Sign returns:\n//\n//\t-1 if x \u003c 0\n//\t 0 if x == 0\n//\t+1 if x \u003e 0\nfunc (z *Int) Sign() int {\n\tz.initiateAbs()\n\n\tif z.abs.IsZero() {\n\t\treturn 0\n\t}\n\tif z.neg {\n\t\treturn -1\n\t}\n\treturn 1\n}\n\n// New returns a new Int set to 0.\nfunc New() *Int {\n\treturn \u0026Int{\n\t\tabs: new(uint256.Uint),\n\t}\n}\n\n// NewInt allocates and returns a new Int set to x.\nfunc NewInt(x int64) *Int {\n\treturn New().SetInt64(x)\n}\n\n// FromDecimal returns a new Int from a decimal string.\n// Returns a new Int and an error if the string is not a valid decimal.\nfunc FromDecimal(s string) (*Int, error) {\n\treturn new(Int).SetString(s)\n}\n\n// MustFromDecimal returns a new Int from a decimal string.\n// Panics if the string is not a valid decimal.\nfunc MustFromDecimal(s string) *Int {\n\tz, err := FromDecimal(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn z\n}\n\n// SetString sets s to the value of z and returns z and a boolean indicating success.\nfunc (z *Int) SetString(s string) (*Int, error) {\n\tneg := false\n\t// Remove max one leading +\n\tif len(s) \u003e 0 \u0026\u0026 s[0] == '+' {\n\t\tneg = false\n\t\ts = s[1:]\n\t}\n\n\tif len(s) \u003e 0 \u0026\u0026 s[0] == '-' {\n\t\tneg = true\n\t\ts = s[1:]\n\t}\n\tvar (\n\t\tabs *uint256.Uint\n\t\terr error\n\t)\n\tabs, err = uint256.FromDecimal(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn \u0026Int{\n\t\tabs,\n\t\tneg,\n\t}, nil\n}\n\n// FromUint256 is a convenience-constructor from uint256.Uint.\n// Returns a new Int and whether overflow occurred.\n// OBS: If u is `nil`, this method returns `nil, false`\nfunc FromUint256(x *uint256.Uint) *Int {\n\tif x == nil {\n\t\treturn nil\n\t}\n\tz := Zero()\n\n\tz.SetUint256(x)\n\treturn z\n}\n\n// OBS, differs from original mempooler int256\n// NilToZero sets z to 0 and return it if it's nil, otherwise it returns z\nfunc (z *Int) NilToZero() *Int {\n\tif z == nil {\n\t\treturn NewInt(0)\n\t}\n\treturn z\n}\n\n// initiateAbs sets default value for `z` or `z.abs` value if is nil\n// OBS: differs from mempooler int256. It checks not only `z.abs` but also `z`\nfunc (z *Int) initiateAbs() {\n\tif z == nil || z.abs == nil {\n\t\tz.abs = new(uint256.Uint)\n\t}\n}\n" @@ -2018,10 +1874,6 @@ { "name": "eisel_lemire.gno", "body": "// Copyright 2020 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage eisel_lemire\n\n// This file implements the Eisel-Lemire ParseFloat algorithm, published in\n// 2020 and discussed extensively at\n// https://nigeltao.github.io/blog/2020/eisel-lemire.html\n//\n// The original C++ implementation is at\n// https://github.com/lemire/fast_double_parser/blob/644bef4306059d3be01a04e77d3cc84b379c596f/include/fast_double_parser.h#L840\n//\n// This Go re-implementation closely follows the C re-implementation at\n// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/internal/cgen/base/floatconv-submodule-code.c#L990\n//\n// Additional testing (on over several million test strings) is done by\n// https://github.com/nigeltao/parse-number-fxx-test-data/blob/5280dcfccf6d0b02a65ae282dad0b6d9de50e039/script/test-go-strconv.go\n\nimport (\n\t\"math\"\n\t\"math/bits\"\n)\n\nconst (\n\tfloat32ExponentBias = 127\n\tfloat64ExponentBias = 1023\n)\n\n// eiselLemire64 parses a floating-point number from its mantissa and exponent representation.\n// This implementation is based on the Eisel-Lemire ParseFloat algorithm, which is efficient\n// and precise for converting strings to floating-point numbers.\n//\n// Arguments:\n// man (uint64): The mantissa part of the floating-point number.\n// exp10 (int): The exponent part, representing the power of 10.\n// neg (bool): Indicates if the number is negative.\n//\n// Returns:\n// f (float64): The parsed floating-point number.\n// ok (bool): Indicates whether the parsing was successful.\n//\n// The function starts by handling special cases, such as zero mantissa.\n// It then checks if the exponent is within the allowed range.\n// After that, it normalizes the mantissa by left-shifting it to fill\n// the leading zeros. This is followed by the main algorithm logic that\n// converts the normalized mantissa and exponent into a 64-bit floating-point number.\n// The function returns this number along with a boolean indicating the success of the operation.\nfunc EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {\n\t// The terse comments in this function body refer to sections of the\n\t// https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post.\n\n\t// Exp10 Range.\n\tif man == 0 {\n\t\tif neg {\n\t\t\tf = math.Float64frombits(0x80000000_00000000) // Negative zero.\n\t\t}\n\n\t\treturn f, true\n\t}\n\n\tif exp10 \u003c detailedPowersOfTenMinExp10 || detailedPowersOfTenMaxExp10 \u003c exp10 {\n\t\treturn 0, false\n\t}\n\n\t// Normalization.\n\tclz := bits.LeadingZeros64(man)\n\tman \u003c\u003c= uint(clz)\n\tretExp2 := uint64(217706*exp10\u003e\u003e16+64+float64ExponentBias) - uint64(clz)\n\n\t// Multiplication.\n\txHi, xLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][1])\n\n\t// Wider Approximation.\n\tif xHi\u00260x1FF == 0x1FF \u0026\u0026 xLo+man \u003c man {\n\t\tyHi, yLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][0])\n\t\tmergedHi, mergedLo := xHi, xLo+yHi\n\t\tif mergedLo \u003c xLo {\n\t\t\tmergedHi++\n\t\t}\n\n\t\tif mergedHi\u00260x1FF == 0x1FF \u0026\u0026 mergedLo+1 == 0 \u0026\u0026 yLo+man \u003c man {\n\t\t\treturn 0, false\n\t\t}\n\n\t\txHi, xLo = mergedHi, mergedLo\n\t}\n\n\t// Shifting to 54 Bits.\n\tmsb := xHi \u003e\u003e 63\n\tretMantissa := xHi \u003e\u003e (msb + 9)\n\tretExp2 -= 1 ^ msb\n\n\t// Half-way Ambiguity.\n\tif xLo == 0 \u0026\u0026 xHi\u00260x1FF == 0 \u0026\u0026 retMantissa\u00263 == 1 {\n\t\treturn 0, false\n\t}\n\n\t// From 54 to 53 Bits.\n\tretMantissa += retMantissa \u0026 1\n\tretMantissa \u003e\u003e= 1\n\tif retMantissa\u003e\u003e53 \u003e 0 {\n\t\tretMantissa \u003e\u003e= 1\n\t\tretExp2 += 1\n\t}\n\n\t// retExp2 is a uint64. Zero or underflow means that we're in subnormal\n\t// float64 space. 0x7FF or above means that we're in Inf/NaN float64 space.\n\t//\n\t// The if block is equivalent to (but has fewer branches than):\n\t// if retExp2 \u003c= 0 || retExp2 \u003e= 0x7FF { etc }\n\tif retExp2-1 \u003e= 0x7FF-1 {\n\t\treturn 0, false\n\t}\n\n\tretBits := retExp2\u003c\u003c52 | retMantissa\u00260x000FFFFF_FFFFFFFF\n\tif neg {\n\t\tretBits |= 0x80000000_00000000\n\t}\n\n\treturn math.Float64frombits(retBits), true\n}\n\n// detailedPowersOfTen{Min,Max}Exp10 is the power of 10 represented by the\n// first and last rows of detailedPowersOfTen. Both bounds are inclusive.\nconst (\n\tdetailedPowersOfTenMinExp10 = -348\n\tdetailedPowersOfTenMaxExp10 = +347\n)\n\n// detailedPowersOfTen contains 128-bit mantissa approximations (rounded down)\n// to the powers of 10. For example:\n//\n// - 1e43 ≈ (0xE596B7B0_C643C719 * (2 ** 79))\n// - 1e43 = (0xE596B7B0_C643C719_6D9CCD05_D0000000 * (2 ** 15))\n//\n// The mantissas are explicitly listed. The exponents are implied by a linear\n// expression with slope 217706.0/65536.0 ≈ log(10)/log(2).\n//\n// The table was generated by\n// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/script/print-mpb-powers-of-10.go\nvar detailedPowersOfTen = [...][2]uint64{\n\t{0x1732C869CD60E453, 0xFA8FD5A0081C0288}, // 1e-348\n\t{0x0E7FBD42205C8EB4, 0x9C99E58405118195}, // 1e-347\n\t{0x521FAC92A873B261, 0xC3C05EE50655E1FA}, // 1e-346\n\t{0xE6A797B752909EF9, 0xF4B0769E47EB5A78}, // 1e-345\n\t{0x9028BED2939A635C, 0x98EE4A22ECF3188B}, // 1e-344\n\t{0x7432EE873880FC33, 0xBF29DCABA82FDEAE}, // 1e-343\n\t{0x113FAA2906A13B3F, 0xEEF453D6923BD65A}, // 1e-342\n\t{0x4AC7CA59A424C507, 0x9558B4661B6565F8}, // 1e-341\n\t{0x5D79BCF00D2DF649, 0xBAAEE17FA23EBF76}, // 1e-340\n\t{0xF4D82C2C107973DC, 0xE95A99DF8ACE6F53}, // 1e-339\n\t{0x79071B9B8A4BE869, 0x91D8A02BB6C10594}, // 1e-338\n\t{0x9748E2826CDEE284, 0xB64EC836A47146F9}, // 1e-337\n\t{0xFD1B1B2308169B25, 0xE3E27A444D8D98B7}, // 1e-336\n\t{0xFE30F0F5E50E20F7, 0x8E6D8C6AB0787F72}, // 1e-335\n\t{0xBDBD2D335E51A935, 0xB208EF855C969F4F}, // 1e-334\n\t{0xAD2C788035E61382, 0xDE8B2B66B3BC4723}, // 1e-333\n\t{0x4C3BCB5021AFCC31, 0x8B16FB203055AC76}, // 1e-332\n\t{0xDF4ABE242A1BBF3D, 0xADDCB9E83C6B1793}, // 1e-331\n\t{0xD71D6DAD34A2AF0D, 0xD953E8624B85DD78}, // 1e-330\n\t{0x8672648C40E5AD68, 0x87D4713D6F33AA6B}, // 1e-329\n\t{0x680EFDAF511F18C2, 0xA9C98D8CCB009506}, // 1e-328\n\t{0x0212BD1B2566DEF2, 0xD43BF0EFFDC0BA48}, // 1e-327\n\t{0x014BB630F7604B57, 0x84A57695FE98746D}, // 1e-326\n\t{0x419EA3BD35385E2D, 0xA5CED43B7E3E9188}, // 1e-325\n\t{0x52064CAC828675B9, 0xCF42894A5DCE35EA}, // 1e-324\n\t{0x7343EFEBD1940993, 0x818995CE7AA0E1B2}, // 1e-323\n\t{0x1014EBE6C5F90BF8, 0xA1EBFB4219491A1F}, // 1e-322\n\t{0xD41A26E077774EF6, 0xCA66FA129F9B60A6}, // 1e-321\n\t{0x8920B098955522B4, 0xFD00B897478238D0}, // 1e-320\n\t{0x55B46E5F5D5535B0, 0x9E20735E8CB16382}, // 1e-319\n\t{0xEB2189F734AA831D, 0xC5A890362FDDBC62}, // 1e-318\n\t{0xA5E9EC7501D523E4, 0xF712B443BBD52B7B}, // 1e-317\n\t{0x47B233C92125366E, 0x9A6BB0AA55653B2D}, // 1e-316\n\t{0x999EC0BB696E840A, 0xC1069CD4EABE89F8}, // 1e-315\n\t{0xC00670EA43CA250D, 0xF148440A256E2C76}, // 1e-314\n\t{0x380406926A5E5728, 0x96CD2A865764DBCA}, // 1e-313\n\t{0xC605083704F5ECF2, 0xBC807527ED3E12BC}, // 1e-312\n\t{0xF7864A44C633682E, 0xEBA09271E88D976B}, // 1e-311\n\t{0x7AB3EE6AFBE0211D, 0x93445B8731587EA3}, // 1e-310\n\t{0x5960EA05BAD82964, 0xB8157268FDAE9E4C}, // 1e-309\n\t{0x6FB92487298E33BD, 0xE61ACF033D1A45DF}, // 1e-308\n\t{0xA5D3B6D479F8E056, 0x8FD0C16206306BAB}, // 1e-307\n\t{0x8F48A4899877186C, 0xB3C4F1BA87BC8696}, // 1e-306\n\t{0x331ACDABFE94DE87, 0xE0B62E2929ABA83C}, // 1e-305\n\t{0x9FF0C08B7F1D0B14, 0x8C71DCD9BA0B4925}, // 1e-304\n\t{0x07ECF0AE5EE44DD9, 0xAF8E5410288E1B6F}, // 1e-303\n\t{0xC9E82CD9F69D6150, 0xDB71E91432B1A24A}, // 1e-302\n\t{0xBE311C083A225CD2, 0x892731AC9FAF056E}, // 1e-301\n\t{0x6DBD630A48AAF406, 0xAB70FE17C79AC6CA}, // 1e-300\n\t{0x092CBBCCDAD5B108, 0xD64D3D9DB981787D}, // 1e-299\n\t{0x25BBF56008C58EA5, 0x85F0468293F0EB4E}, // 1e-298\n\t{0xAF2AF2B80AF6F24E, 0xA76C582338ED2621}, // 1e-297\n\t{0x1AF5AF660DB4AEE1, 0xD1476E2C07286FAA}, // 1e-296\n\t{0x50D98D9FC890ED4D, 0x82CCA4DB847945CA}, // 1e-295\n\t{0xE50FF107BAB528A0, 0xA37FCE126597973C}, // 1e-294\n\t{0x1E53ED49A96272C8, 0xCC5FC196FEFD7D0C}, // 1e-293\n\t{0x25E8E89C13BB0F7A, 0xFF77B1FCBEBCDC4F}, // 1e-292\n\t{0x77B191618C54E9AC, 0x9FAACF3DF73609B1}, // 1e-291\n\t{0xD59DF5B9EF6A2417, 0xC795830D75038C1D}, // 1e-290\n\t{0x4B0573286B44AD1D, 0xF97AE3D0D2446F25}, // 1e-289\n\t{0x4EE367F9430AEC32, 0x9BECCE62836AC577}, // 1e-288\n\t{0x229C41F793CDA73F, 0xC2E801FB244576D5}, // 1e-287\n\t{0x6B43527578C1110F, 0xF3A20279ED56D48A}, // 1e-286\n\t{0x830A13896B78AAA9, 0x9845418C345644D6}, // 1e-285\n\t{0x23CC986BC656D553, 0xBE5691EF416BD60C}, // 1e-284\n\t{0x2CBFBE86B7EC8AA8, 0xEDEC366B11C6CB8F}, // 1e-283\n\t{0x7BF7D71432F3D6A9, 0x94B3A202EB1C3F39}, // 1e-282\n\t{0xDAF5CCD93FB0CC53, 0xB9E08A83A5E34F07}, // 1e-281\n\t{0xD1B3400F8F9CFF68, 0xE858AD248F5C22C9}, // 1e-280\n\t{0x23100809B9C21FA1, 0x91376C36D99995BE}, // 1e-279\n\t{0xABD40A0C2832A78A, 0xB58547448FFFFB2D}, // 1e-278\n\t{0x16C90C8F323F516C, 0xE2E69915B3FFF9F9}, // 1e-277\n\t{0xAE3DA7D97F6792E3, 0x8DD01FAD907FFC3B}, // 1e-276\n\t{0x99CD11CFDF41779C, 0xB1442798F49FFB4A}, // 1e-275\n\t{0x40405643D711D583, 0xDD95317F31C7FA1D}, // 1e-274\n\t{0x482835EA666B2572, 0x8A7D3EEF7F1CFC52}, // 1e-273\n\t{0xDA3243650005EECF, 0xAD1C8EAB5EE43B66}, // 1e-272\n\t{0x90BED43E40076A82, 0xD863B256369D4A40}, // 1e-271\n\t{0x5A7744A6E804A291, 0x873E4F75E2224E68}, // 1e-270\n\t{0x711515D0A205CB36, 0xA90DE3535AAAE202}, // 1e-269\n\t{0x0D5A5B44CA873E03, 0xD3515C2831559A83}, // 1e-268\n\t{0xE858790AFE9486C2, 0x8412D9991ED58091}, // 1e-267\n\t{0x626E974DBE39A872, 0xA5178FFF668AE0B6}, // 1e-266\n\t{0xFB0A3D212DC8128F, 0xCE5D73FF402D98E3}, // 1e-265\n\t{0x7CE66634BC9D0B99, 0x80FA687F881C7F8E}, // 1e-264\n\t{0x1C1FFFC1EBC44E80, 0xA139029F6A239F72}, // 1e-263\n\t{0xA327FFB266B56220, 0xC987434744AC874E}, // 1e-262\n\t{0x4BF1FF9F0062BAA8, 0xFBE9141915D7A922}, // 1e-261\n\t{0x6F773FC3603DB4A9, 0x9D71AC8FADA6C9B5}, // 1e-260\n\t{0xCB550FB4384D21D3, 0xC4CE17B399107C22}, // 1e-259\n\t{0x7E2A53A146606A48, 0xF6019DA07F549B2B}, // 1e-258\n\t{0x2EDA7444CBFC426D, 0x99C102844F94E0FB}, // 1e-257\n\t{0xFA911155FEFB5308, 0xC0314325637A1939}, // 1e-256\n\t{0x793555AB7EBA27CA, 0xF03D93EEBC589F88}, // 1e-255\n\t{0x4BC1558B2F3458DE, 0x96267C7535B763B5}, // 1e-254\n\t{0x9EB1AAEDFB016F16, 0xBBB01B9283253CA2}, // 1e-253\n\t{0x465E15A979C1CADC, 0xEA9C227723EE8BCB}, // 1e-252\n\t{0x0BFACD89EC191EC9, 0x92A1958A7675175F}, // 1e-251\n\t{0xCEF980EC671F667B, 0xB749FAED14125D36}, // 1e-250\n\t{0x82B7E12780E7401A, 0xE51C79A85916F484}, // 1e-249\n\t{0xD1B2ECB8B0908810, 0x8F31CC0937AE58D2}, // 1e-248\n\t{0x861FA7E6DCB4AA15, 0xB2FE3F0B8599EF07}, // 1e-247\n\t{0x67A791E093E1D49A, 0xDFBDCECE67006AC9}, // 1e-246\n\t{0xE0C8BB2C5C6D24E0, 0x8BD6A141006042BD}, // 1e-245\n\t{0x58FAE9F773886E18, 0xAECC49914078536D}, // 1e-244\n\t{0xAF39A475506A899E, 0xDA7F5BF590966848}, // 1e-243\n\t{0x6D8406C952429603, 0x888F99797A5E012D}, // 1e-242\n\t{0xC8E5087BA6D33B83, 0xAAB37FD7D8F58178}, // 1e-241\n\t{0xFB1E4A9A90880A64, 0xD5605FCDCF32E1D6}, // 1e-240\n\t{0x5CF2EEA09A55067F, 0x855C3BE0A17FCD26}, // 1e-239\n\t{0xF42FAA48C0EA481E, 0xA6B34AD8C9DFC06F}, // 1e-238\n\t{0xF13B94DAF124DA26, 0xD0601D8EFC57B08B}, // 1e-237\n\t{0x76C53D08D6B70858, 0x823C12795DB6CE57}, // 1e-236\n\t{0x54768C4B0C64CA6E, 0xA2CB1717B52481ED}, // 1e-235\n\t{0xA9942F5DCF7DFD09, 0xCB7DDCDDA26DA268}, // 1e-234\n\t{0xD3F93B35435D7C4C, 0xFE5D54150B090B02}, // 1e-233\n\t{0xC47BC5014A1A6DAF, 0x9EFA548D26E5A6E1}, // 1e-232\n\t{0x359AB6419CA1091B, 0xC6B8E9B0709F109A}, // 1e-231\n\t{0xC30163D203C94B62, 0xF867241C8CC6D4C0}, // 1e-230\n\t{0x79E0DE63425DCF1D, 0x9B407691D7FC44F8}, // 1e-229\n\t{0x985915FC12F542E4, 0xC21094364DFB5636}, // 1e-228\n\t{0x3E6F5B7B17B2939D, 0xF294B943E17A2BC4}, // 1e-227\n\t{0xA705992CEECF9C42, 0x979CF3CA6CEC5B5A}, // 1e-226\n\t{0x50C6FF782A838353, 0xBD8430BD08277231}, // 1e-225\n\t{0xA4F8BF5635246428, 0xECE53CEC4A314EBD}, // 1e-224\n\t{0x871B7795E136BE99, 0x940F4613AE5ED136}, // 1e-223\n\t{0x28E2557B59846E3F, 0xB913179899F68584}, // 1e-222\n\t{0x331AEADA2FE589CF, 0xE757DD7EC07426E5}, // 1e-221\n\t{0x3FF0D2C85DEF7621, 0x9096EA6F3848984F}, // 1e-220\n\t{0x0FED077A756B53A9, 0xB4BCA50B065ABE63}, // 1e-219\n\t{0xD3E8495912C62894, 0xE1EBCE4DC7F16DFB}, // 1e-218\n\t{0x64712DD7ABBBD95C, 0x8D3360F09CF6E4BD}, // 1e-217\n\t{0xBD8D794D96AACFB3, 0xB080392CC4349DEC}, // 1e-216\n\t{0xECF0D7A0FC5583A0, 0xDCA04777F541C567}, // 1e-215\n\t{0xF41686C49DB57244, 0x89E42CAAF9491B60}, // 1e-214\n\t{0x311C2875C522CED5, 0xAC5D37D5B79B6239}, // 1e-213\n\t{0x7D633293366B828B, 0xD77485CB25823AC7}, // 1e-212\n\t{0xAE5DFF9C02033197, 0x86A8D39EF77164BC}, // 1e-211\n\t{0xD9F57F830283FDFC, 0xA8530886B54DBDEB}, // 1e-210\n\t{0xD072DF63C324FD7B, 0xD267CAA862A12D66}, // 1e-209\n\t{0x4247CB9E59F71E6D, 0x8380DEA93DA4BC60}, // 1e-208\n\t{0x52D9BE85F074E608, 0xA46116538D0DEB78}, // 1e-207\n\t{0x67902E276C921F8B, 0xCD795BE870516656}, // 1e-206\n\t{0x00BA1CD8A3DB53B6, 0x806BD9714632DFF6}, // 1e-205\n\t{0x80E8A40ECCD228A4, 0xA086CFCD97BF97F3}, // 1e-204\n\t{0x6122CD128006B2CD, 0xC8A883C0FDAF7DF0}, // 1e-203\n\t{0x796B805720085F81, 0xFAD2A4B13D1B5D6C}, // 1e-202\n\t{0xCBE3303674053BB0, 0x9CC3A6EEC6311A63}, // 1e-201\n\t{0xBEDBFC4411068A9C, 0xC3F490AA77BD60FC}, // 1e-200\n\t{0xEE92FB5515482D44, 0xF4F1B4D515ACB93B}, // 1e-199\n\t{0x751BDD152D4D1C4A, 0x991711052D8BF3C5}, // 1e-198\n\t{0xD262D45A78A0635D, 0xBF5CD54678EEF0B6}, // 1e-197\n\t{0x86FB897116C87C34, 0xEF340A98172AACE4}, // 1e-196\n\t{0xD45D35E6AE3D4DA0, 0x9580869F0E7AAC0E}, // 1e-195\n\t{0x8974836059CCA109, 0xBAE0A846D2195712}, // 1e-194\n\t{0x2BD1A438703FC94B, 0xE998D258869FACD7}, // 1e-193\n\t{0x7B6306A34627DDCF, 0x91FF83775423CC06}, // 1e-192\n\t{0x1A3BC84C17B1D542, 0xB67F6455292CBF08}, // 1e-191\n\t{0x20CABA5F1D9E4A93, 0xE41F3D6A7377EECA}, // 1e-190\n\t{0x547EB47B7282EE9C, 0x8E938662882AF53E}, // 1e-189\n\t{0xE99E619A4F23AA43, 0xB23867FB2A35B28D}, // 1e-188\n\t{0x6405FA00E2EC94D4, 0xDEC681F9F4C31F31}, // 1e-187\n\t{0xDE83BC408DD3DD04, 0x8B3C113C38F9F37E}, // 1e-186\n\t{0x9624AB50B148D445, 0xAE0B158B4738705E}, // 1e-185\n\t{0x3BADD624DD9B0957, 0xD98DDAEE19068C76}, // 1e-184\n\t{0xE54CA5D70A80E5D6, 0x87F8A8D4CFA417C9}, // 1e-183\n\t{0x5E9FCF4CCD211F4C, 0xA9F6D30A038D1DBC}, // 1e-182\n\t{0x7647C3200069671F, 0xD47487CC8470652B}, // 1e-181\n\t{0x29ECD9F40041E073, 0x84C8D4DFD2C63F3B}, // 1e-180\n\t{0xF468107100525890, 0xA5FB0A17C777CF09}, // 1e-179\n\t{0x7182148D4066EEB4, 0xCF79CC9DB955C2CC}, // 1e-178\n\t{0xC6F14CD848405530, 0x81AC1FE293D599BF}, // 1e-177\n\t{0xB8ADA00E5A506A7C, 0xA21727DB38CB002F}, // 1e-176\n\t{0xA6D90811F0E4851C, 0xCA9CF1D206FDC03B}, // 1e-175\n\t{0x908F4A166D1DA663, 0xFD442E4688BD304A}, // 1e-174\n\t{0x9A598E4E043287FE, 0x9E4A9CEC15763E2E}, // 1e-173\n\t{0x40EFF1E1853F29FD, 0xC5DD44271AD3CDBA}, // 1e-172\n\t{0xD12BEE59E68EF47C, 0xF7549530E188C128}, // 1e-171\n\t{0x82BB74F8301958CE, 0x9A94DD3E8CF578B9}, // 1e-170\n\t{0xE36A52363C1FAF01, 0xC13A148E3032D6E7}, // 1e-169\n\t{0xDC44E6C3CB279AC1, 0xF18899B1BC3F8CA1}, // 1e-168\n\t{0x29AB103A5EF8C0B9, 0x96F5600F15A7B7E5}, // 1e-167\n\t{0x7415D448F6B6F0E7, 0xBCB2B812DB11A5DE}, // 1e-166\n\t{0x111B495B3464AD21, 0xEBDF661791D60F56}, // 1e-165\n\t{0xCAB10DD900BEEC34, 0x936B9FCEBB25C995}, // 1e-164\n\t{0x3D5D514F40EEA742, 0xB84687C269EF3BFB}, // 1e-163\n\t{0x0CB4A5A3112A5112, 0xE65829B3046B0AFA}, // 1e-162\n\t{0x47F0E785EABA72AB, 0x8FF71A0FE2C2E6DC}, // 1e-161\n\t{0x59ED216765690F56, 0xB3F4E093DB73A093}, // 1e-160\n\t{0x306869C13EC3532C, 0xE0F218B8D25088B8}, // 1e-159\n\t{0x1E414218C73A13FB, 0x8C974F7383725573}, // 1e-158\n\t{0xE5D1929EF90898FA, 0xAFBD2350644EEACF}, // 1e-157\n\t{0xDF45F746B74ABF39, 0xDBAC6C247D62A583}, // 1e-156\n\t{0x6B8BBA8C328EB783, 0x894BC396CE5DA772}, // 1e-155\n\t{0x066EA92F3F326564, 0xAB9EB47C81F5114F}, // 1e-154\n\t{0xC80A537B0EFEFEBD, 0xD686619BA27255A2}, // 1e-153\n\t{0xBD06742CE95F5F36, 0x8613FD0145877585}, // 1e-152\n\t{0x2C48113823B73704, 0xA798FC4196E952E7}, // 1e-151\n\t{0xF75A15862CA504C5, 0xD17F3B51FCA3A7A0}, // 1e-150\n\t{0x9A984D73DBE722FB, 0x82EF85133DE648C4}, // 1e-149\n\t{0xC13E60D0D2E0EBBA, 0xA3AB66580D5FDAF5}, // 1e-148\n\t{0x318DF905079926A8, 0xCC963FEE10B7D1B3}, // 1e-147\n\t{0xFDF17746497F7052, 0xFFBBCFE994E5C61F}, // 1e-146\n\t{0xFEB6EA8BEDEFA633, 0x9FD561F1FD0F9BD3}, // 1e-145\n\t{0xFE64A52EE96B8FC0, 0xC7CABA6E7C5382C8}, // 1e-144\n\t{0x3DFDCE7AA3C673B0, 0xF9BD690A1B68637B}, // 1e-143\n\t{0x06BEA10CA65C084E, 0x9C1661A651213E2D}, // 1e-142\n\t{0x486E494FCFF30A62, 0xC31BFA0FE5698DB8}, // 1e-141\n\t{0x5A89DBA3C3EFCCFA, 0xF3E2F893DEC3F126}, // 1e-140\n\t{0xF89629465A75E01C, 0x986DDB5C6B3A76B7}, // 1e-139\n\t{0xF6BBB397F1135823, 0xBE89523386091465}, // 1e-138\n\t{0x746AA07DED582E2C, 0xEE2BA6C0678B597F}, // 1e-137\n\t{0xA8C2A44EB4571CDC, 0x94DB483840B717EF}, // 1e-136\n\t{0x92F34D62616CE413, 0xBA121A4650E4DDEB}, // 1e-135\n\t{0x77B020BAF9C81D17, 0xE896A0D7E51E1566}, // 1e-134\n\t{0x0ACE1474DC1D122E, 0x915E2486EF32CD60}, // 1e-133\n\t{0x0D819992132456BA, 0xB5B5ADA8AAFF80B8}, // 1e-132\n\t{0x10E1FFF697ED6C69, 0xE3231912D5BF60E6}, // 1e-131\n\t{0xCA8D3FFA1EF463C1, 0x8DF5EFABC5979C8F}, // 1e-130\n\t{0xBD308FF8A6B17CB2, 0xB1736B96B6FD83B3}, // 1e-129\n\t{0xAC7CB3F6D05DDBDE, 0xDDD0467C64BCE4A0}, // 1e-128\n\t{0x6BCDF07A423AA96B, 0x8AA22C0DBEF60EE4}, // 1e-127\n\t{0x86C16C98D2C953C6, 0xAD4AB7112EB3929D}, // 1e-126\n\t{0xE871C7BF077BA8B7, 0xD89D64D57A607744}, // 1e-125\n\t{0x11471CD764AD4972, 0x87625F056C7C4A8B}, // 1e-124\n\t{0xD598E40D3DD89BCF, 0xA93AF6C6C79B5D2D}, // 1e-123\n\t{0x4AFF1D108D4EC2C3, 0xD389B47879823479}, // 1e-122\n\t{0xCEDF722A585139BA, 0x843610CB4BF160CB}, // 1e-121\n\t{0xC2974EB4EE658828, 0xA54394FE1EEDB8FE}, // 1e-120\n\t{0x733D226229FEEA32, 0xCE947A3DA6A9273E}, // 1e-119\n\t{0x0806357D5A3F525F, 0x811CCC668829B887}, // 1e-118\n\t{0xCA07C2DCB0CF26F7, 0xA163FF802A3426A8}, // 1e-117\n\t{0xFC89B393DD02F0B5, 0xC9BCFF6034C13052}, // 1e-116\n\t{0xBBAC2078D443ACE2, 0xFC2C3F3841F17C67}, // 1e-115\n\t{0xD54B944B84AA4C0D, 0x9D9BA7832936EDC0}, // 1e-114\n\t{0x0A9E795E65D4DF11, 0xC5029163F384A931}, // 1e-113\n\t{0x4D4617B5FF4A16D5, 0xF64335BCF065D37D}, // 1e-112\n\t{0x504BCED1BF8E4E45, 0x99EA0196163FA42E}, // 1e-111\n\t{0xE45EC2862F71E1D6, 0xC06481FB9BCF8D39}, // 1e-110\n\t{0x5D767327BB4E5A4C, 0xF07DA27A82C37088}, // 1e-109\n\t{0x3A6A07F8D510F86F, 0x964E858C91BA2655}, // 1e-108\n\t{0x890489F70A55368B, 0xBBE226EFB628AFEA}, // 1e-107\n\t{0x2B45AC74CCEA842E, 0xEADAB0ABA3B2DBE5}, // 1e-106\n\t{0x3B0B8BC90012929D, 0x92C8AE6B464FC96F}, // 1e-105\n\t{0x09CE6EBB40173744, 0xB77ADA0617E3BBCB}, // 1e-104\n\t{0xCC420A6A101D0515, 0xE55990879DDCAABD}, // 1e-103\n\t{0x9FA946824A12232D, 0x8F57FA54C2A9EAB6}, // 1e-102\n\t{0x47939822DC96ABF9, 0xB32DF8E9F3546564}, // 1e-101\n\t{0x59787E2B93BC56F7, 0xDFF9772470297EBD}, // 1e-100\n\t{0x57EB4EDB3C55B65A, 0x8BFBEA76C619EF36}, // 1e-99\n\t{0xEDE622920B6B23F1, 0xAEFAE51477A06B03}, // 1e-98\n\t{0xE95FAB368E45ECED, 0xDAB99E59958885C4}, // 1e-97\n\t{0x11DBCB0218EBB414, 0x88B402F7FD75539B}, // 1e-96\n\t{0xD652BDC29F26A119, 0xAAE103B5FCD2A881}, // 1e-95\n\t{0x4BE76D3346F0495F, 0xD59944A37C0752A2}, // 1e-94\n\t{0x6F70A4400C562DDB, 0x857FCAE62D8493A5}, // 1e-93\n\t{0xCB4CCD500F6BB952, 0xA6DFBD9FB8E5B88E}, // 1e-92\n\t{0x7E2000A41346A7A7, 0xD097AD07A71F26B2}, // 1e-91\n\t{0x8ED400668C0C28C8, 0x825ECC24C873782F}, // 1e-90\n\t{0x728900802F0F32FA, 0xA2F67F2DFA90563B}, // 1e-89\n\t{0x4F2B40A03AD2FFB9, 0xCBB41EF979346BCA}, // 1e-88\n\t{0xE2F610C84987BFA8, 0xFEA126B7D78186BC}, // 1e-87\n\t{0x0DD9CA7D2DF4D7C9, 0x9F24B832E6B0F436}, // 1e-86\n\t{0x91503D1C79720DBB, 0xC6EDE63FA05D3143}, // 1e-85\n\t{0x75A44C6397CE912A, 0xF8A95FCF88747D94}, // 1e-84\n\t{0xC986AFBE3EE11ABA, 0x9B69DBE1B548CE7C}, // 1e-83\n\t{0xFBE85BADCE996168, 0xC24452DA229B021B}, // 1e-82\n\t{0xFAE27299423FB9C3, 0xF2D56790AB41C2A2}, // 1e-81\n\t{0xDCCD879FC967D41A, 0x97C560BA6B0919A5}, // 1e-80\n\t{0x5400E987BBC1C920, 0xBDB6B8E905CB600F}, // 1e-79\n\t{0x290123E9AAB23B68, 0xED246723473E3813}, // 1e-78\n\t{0xF9A0B6720AAF6521, 0x9436C0760C86E30B}, // 1e-77\n\t{0xF808E40E8D5B3E69, 0xB94470938FA89BCE}, // 1e-76\n\t{0xB60B1D1230B20E04, 0xE7958CB87392C2C2}, // 1e-75\n\t{0xB1C6F22B5E6F48C2, 0x90BD77F3483BB9B9}, // 1e-74\n\t{0x1E38AEB6360B1AF3, 0xB4ECD5F01A4AA828}, // 1e-73\n\t{0x25C6DA63C38DE1B0, 0xE2280B6C20DD5232}, // 1e-72\n\t{0x579C487E5A38AD0E, 0x8D590723948A535F}, // 1e-71\n\t{0x2D835A9DF0C6D851, 0xB0AF48EC79ACE837}, // 1e-70\n\t{0xF8E431456CF88E65, 0xDCDB1B2798182244}, // 1e-69\n\t{0x1B8E9ECB641B58FF, 0x8A08F0F8BF0F156B}, // 1e-68\n\t{0xE272467E3D222F3F, 0xAC8B2D36EED2DAC5}, // 1e-67\n\t{0x5B0ED81DCC6ABB0F, 0xD7ADF884AA879177}, // 1e-66\n\t{0x98E947129FC2B4E9, 0x86CCBB52EA94BAEA}, // 1e-65\n\t{0x3F2398D747B36224, 0xA87FEA27A539E9A5}, // 1e-64\n\t{0x8EEC7F0D19A03AAD, 0xD29FE4B18E88640E}, // 1e-63\n\t{0x1953CF68300424AC, 0x83A3EEEEF9153E89}, // 1e-62\n\t{0x5FA8C3423C052DD7, 0xA48CEAAAB75A8E2B}, // 1e-61\n\t{0x3792F412CB06794D, 0xCDB02555653131B6}, // 1e-60\n\t{0xE2BBD88BBEE40BD0, 0x808E17555F3EBF11}, // 1e-59\n\t{0x5B6ACEAEAE9D0EC4, 0xA0B19D2AB70E6ED6}, // 1e-58\n\t{0xF245825A5A445275, 0xC8DE047564D20A8B}, // 1e-57\n\t{0xEED6E2F0F0D56712, 0xFB158592BE068D2E}, // 1e-56\n\t{0x55464DD69685606B, 0x9CED737BB6C4183D}, // 1e-55\n\t{0xAA97E14C3C26B886, 0xC428D05AA4751E4C}, // 1e-54\n\t{0xD53DD99F4B3066A8, 0xF53304714D9265DF}, // 1e-53\n\t{0xE546A8038EFE4029, 0x993FE2C6D07B7FAB}, // 1e-52\n\t{0xDE98520472BDD033, 0xBF8FDB78849A5F96}, // 1e-51\n\t{0x963E66858F6D4440, 0xEF73D256A5C0F77C}, // 1e-50\n\t{0xDDE7001379A44AA8, 0x95A8637627989AAD}, // 1e-49\n\t{0x5560C018580D5D52, 0xBB127C53B17EC159}, // 1e-48\n\t{0xAAB8F01E6E10B4A6, 0xE9D71B689DDE71AF}, // 1e-47\n\t{0xCAB3961304CA70E8, 0x9226712162AB070D}, // 1e-46\n\t{0x3D607B97C5FD0D22, 0xB6B00D69BB55C8D1}, // 1e-45\n\t{0x8CB89A7DB77C506A, 0xE45C10C42A2B3B05}, // 1e-44\n\t{0x77F3608E92ADB242, 0x8EB98A7A9A5B04E3}, // 1e-43\n\t{0x55F038B237591ED3, 0xB267ED1940F1C61C}, // 1e-42\n\t{0x6B6C46DEC52F6688, 0xDF01E85F912E37A3}, // 1e-41\n\t{0x2323AC4B3B3DA015, 0x8B61313BBABCE2C6}, // 1e-40\n\t{0xABEC975E0A0D081A, 0xAE397D8AA96C1B77}, // 1e-39\n\t{0x96E7BD358C904A21, 0xD9C7DCED53C72255}, // 1e-38\n\t{0x7E50D64177DA2E54, 0x881CEA14545C7575}, // 1e-37\n\t{0xDDE50BD1D5D0B9E9, 0xAA242499697392D2}, // 1e-36\n\t{0x955E4EC64B44E864, 0xD4AD2DBFC3D07787}, // 1e-35\n\t{0xBD5AF13BEF0B113E, 0x84EC3C97DA624AB4}, // 1e-34\n\t{0xECB1AD8AEACDD58E, 0xA6274BBDD0FADD61}, // 1e-33\n\t{0x67DE18EDA5814AF2, 0xCFB11EAD453994BA}, // 1e-32\n\t{0x80EACF948770CED7, 0x81CEB32C4B43FCF4}, // 1e-31\n\t{0xA1258379A94D028D, 0xA2425FF75E14FC31}, // 1e-30\n\t{0x096EE45813A04330, 0xCAD2F7F5359A3B3E}, // 1e-29\n\t{0x8BCA9D6E188853FC, 0xFD87B5F28300CA0D}, // 1e-28\n\t{0x775EA264CF55347D, 0x9E74D1B791E07E48}, // 1e-27\n\t{0x95364AFE032A819D, 0xC612062576589DDA}, // 1e-26\n\t{0x3A83DDBD83F52204, 0xF79687AED3EEC551}, // 1e-25\n\t{0xC4926A9672793542, 0x9ABE14CD44753B52}, // 1e-24\n\t{0x75B7053C0F178293, 0xC16D9A0095928A27}, // 1e-23\n\t{0x5324C68B12DD6338, 0xF1C90080BAF72CB1}, // 1e-22\n\t{0xD3F6FC16EBCA5E03, 0x971DA05074DA7BEE}, // 1e-21\n\t{0x88F4BB1CA6BCF584, 0xBCE5086492111AEA}, // 1e-20\n\t{0x2B31E9E3D06C32E5, 0xEC1E4A7DB69561A5}, // 1e-19\n\t{0x3AFF322E62439FCF, 0x9392EE8E921D5D07}, // 1e-18\n\t{0x09BEFEB9FAD487C2, 0xB877AA3236A4B449}, // 1e-17\n\t{0x4C2EBE687989A9B3, 0xE69594BEC44DE15B}, // 1e-16\n\t{0x0F9D37014BF60A10, 0x901D7CF73AB0ACD9}, // 1e-15\n\t{0x538484C19EF38C94, 0xB424DC35095CD80F}, // 1e-14\n\t{0x2865A5F206B06FB9, 0xE12E13424BB40E13}, // 1e-13\n\t{0xF93F87B7442E45D3, 0x8CBCCC096F5088CB}, // 1e-12\n\t{0xF78F69A51539D748, 0xAFEBFF0BCB24AAFE}, // 1e-11\n\t{0xB573440E5A884D1B, 0xDBE6FECEBDEDD5BE}, // 1e-10\n\t{0x31680A88F8953030, 0x89705F4136B4A597}, // 1e-9\n\t{0xFDC20D2B36BA7C3D, 0xABCC77118461CEFC}, // 1e-8\n\t{0x3D32907604691B4C, 0xD6BF94D5E57A42BC}, // 1e-7\n\t{0xA63F9A49C2C1B10F, 0x8637BD05AF6C69B5}, // 1e-6\n\t{0x0FCF80DC33721D53, 0xA7C5AC471B478423}, // 1e-5\n\t{0xD3C36113404EA4A8, 0xD1B71758E219652B}, // 1e-4\n\t{0x645A1CAC083126E9, 0x83126E978D4FDF3B}, // 1e-3\n\t{0x3D70A3D70A3D70A3, 0xA3D70A3D70A3D70A}, // 1e-2\n\t{0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC}, // 1e-1\n\t{0x0000000000000000, 0x8000000000000000}, // 1e0\n\t{0x0000000000000000, 0xA000000000000000}, // 1e1\n\t{0x0000000000000000, 0xC800000000000000}, // 1e2\n\t{0x0000000000000000, 0xFA00000000000000}, // 1e3\n\t{0x0000000000000000, 0x9C40000000000000}, // 1e4\n\t{0x0000000000000000, 0xC350000000000000}, // 1e5\n\t{0x0000000000000000, 0xF424000000000000}, // 1e6\n\t{0x0000000000000000, 0x9896800000000000}, // 1e7\n\t{0x0000000000000000, 0xBEBC200000000000}, // 1e8\n\t{0x0000000000000000, 0xEE6B280000000000}, // 1e9\n\t{0x0000000000000000, 0x9502F90000000000}, // 1e10\n\t{0x0000000000000000, 0xBA43B74000000000}, // 1e11\n\t{0x0000000000000000, 0xE8D4A51000000000}, // 1e12\n\t{0x0000000000000000, 0x9184E72A00000000}, // 1e13\n\t{0x0000000000000000, 0xB5E620F480000000}, // 1e14\n\t{0x0000000000000000, 0xE35FA931A0000000}, // 1e15\n\t{0x0000000000000000, 0x8E1BC9BF04000000}, // 1e16\n\t{0x0000000000000000, 0xB1A2BC2EC5000000}, // 1e17\n\t{0x0000000000000000, 0xDE0B6B3A76400000}, // 1e18\n\t{0x0000000000000000, 0x8AC7230489E80000}, // 1e19\n\t{0x0000000000000000, 0xAD78EBC5AC620000}, // 1e20\n\t{0x0000000000000000, 0xD8D726B7177A8000}, // 1e21\n\t{0x0000000000000000, 0x878678326EAC9000}, // 1e22\n\t{0x0000000000000000, 0xA968163F0A57B400}, // 1e23\n\t{0x0000000000000000, 0xD3C21BCECCEDA100}, // 1e24\n\t{0x0000000000000000, 0x84595161401484A0}, // 1e25\n\t{0x0000000000000000, 0xA56FA5B99019A5C8}, // 1e26\n\t{0x0000000000000000, 0xCECB8F27F4200F3A}, // 1e27\n\t{0x4000000000000000, 0x813F3978F8940984}, // 1e28\n\t{0x5000000000000000, 0xA18F07D736B90BE5}, // 1e29\n\t{0xA400000000000000, 0xC9F2C9CD04674EDE}, // 1e30\n\t{0x4D00000000000000, 0xFC6F7C4045812296}, // 1e31\n\t{0xF020000000000000, 0x9DC5ADA82B70B59D}, // 1e32\n\t{0x6C28000000000000, 0xC5371912364CE305}, // 1e33\n\t{0xC732000000000000, 0xF684DF56C3E01BC6}, // 1e34\n\t{0x3C7F400000000000, 0x9A130B963A6C115C}, // 1e35\n\t{0x4B9F100000000000, 0xC097CE7BC90715B3}, // 1e36\n\t{0x1E86D40000000000, 0xF0BDC21ABB48DB20}, // 1e37\n\t{0x1314448000000000, 0x96769950B50D88F4}, // 1e38\n\t{0x17D955A000000000, 0xBC143FA4E250EB31}, // 1e39\n\t{0x5DCFAB0800000000, 0xEB194F8E1AE525FD}, // 1e40\n\t{0x5AA1CAE500000000, 0x92EFD1B8D0CF37BE}, // 1e41\n\t{0xF14A3D9E40000000, 0xB7ABC627050305AD}, // 1e42\n\t{0x6D9CCD05D0000000, 0xE596B7B0C643C719}, // 1e43\n\t{0xE4820023A2000000, 0x8F7E32CE7BEA5C6F}, // 1e44\n\t{0xDDA2802C8A800000, 0xB35DBF821AE4F38B}, // 1e45\n\t{0xD50B2037AD200000, 0xE0352F62A19E306E}, // 1e46\n\t{0x4526F422CC340000, 0x8C213D9DA502DE45}, // 1e47\n\t{0x9670B12B7F410000, 0xAF298D050E4395D6}, // 1e48\n\t{0x3C0CDD765F114000, 0xDAF3F04651D47B4C}, // 1e49\n\t{0xA5880A69FB6AC800, 0x88D8762BF324CD0F}, // 1e50\n\t{0x8EEA0D047A457A00, 0xAB0E93B6EFEE0053}, // 1e51\n\t{0x72A4904598D6D880, 0xD5D238A4ABE98068}, // 1e52\n\t{0x47A6DA2B7F864750, 0x85A36366EB71F041}, // 1e53\n\t{0x999090B65F67D924, 0xA70C3C40A64E6C51}, // 1e54\n\t{0xFFF4B4E3F741CF6D, 0xD0CF4B50CFE20765}, // 1e55\n\t{0xBFF8F10E7A8921A4, 0x82818F1281ED449F}, // 1e56\n\t{0xAFF72D52192B6A0D, 0xA321F2D7226895C7}, // 1e57\n\t{0x9BF4F8A69F764490, 0xCBEA6F8CEB02BB39}, // 1e58\n\t{0x02F236D04753D5B4, 0xFEE50B7025C36A08}, // 1e59\n\t{0x01D762422C946590, 0x9F4F2726179A2245}, // 1e60\n\t{0x424D3AD2B7B97EF5, 0xC722F0EF9D80AAD6}, // 1e61\n\t{0xD2E0898765A7DEB2, 0xF8EBAD2B84E0D58B}, // 1e62\n\t{0x63CC55F49F88EB2F, 0x9B934C3B330C8577}, // 1e63\n\t{0x3CBF6B71C76B25FB, 0xC2781F49FFCFA6D5}, // 1e64\n\t{0x8BEF464E3945EF7A, 0xF316271C7FC3908A}, // 1e65\n\t{0x97758BF0E3CBB5AC, 0x97EDD871CFDA3A56}, // 1e66\n\t{0x3D52EEED1CBEA317, 0xBDE94E8E43D0C8EC}, // 1e67\n\t{0x4CA7AAA863EE4BDD, 0xED63A231D4C4FB27}, // 1e68\n\t{0x8FE8CAA93E74EF6A, 0x945E455F24FB1CF8}, // 1e69\n\t{0xB3E2FD538E122B44, 0xB975D6B6EE39E436}, // 1e70\n\t{0x60DBBCA87196B616, 0xE7D34C64A9C85D44}, // 1e71\n\t{0xBC8955E946FE31CD, 0x90E40FBEEA1D3A4A}, // 1e72\n\t{0x6BABAB6398BDBE41, 0xB51D13AEA4A488DD}, // 1e73\n\t{0xC696963C7EED2DD1, 0xE264589A4DCDAB14}, // 1e74\n\t{0xFC1E1DE5CF543CA2, 0x8D7EB76070A08AEC}, // 1e75\n\t{0x3B25A55F43294BCB, 0xB0DE65388CC8ADA8}, // 1e76\n\t{0x49EF0EB713F39EBE, 0xDD15FE86AFFAD912}, // 1e77\n\t{0x6E3569326C784337, 0x8A2DBF142DFCC7AB}, // 1e78\n\t{0x49C2C37F07965404, 0xACB92ED9397BF996}, // 1e79\n\t{0xDC33745EC97BE906, 0xD7E77A8F87DAF7FB}, // 1e80\n\t{0x69A028BB3DED71A3, 0x86F0AC99B4E8DAFD}, // 1e81\n\t{0xC40832EA0D68CE0C, 0xA8ACD7C0222311BC}, // 1e82\n\t{0xF50A3FA490C30190, 0xD2D80DB02AABD62B}, // 1e83\n\t{0x792667C6DA79E0FA, 0x83C7088E1AAB65DB}, // 1e84\n\t{0x577001B891185938, 0xA4B8CAB1A1563F52}, // 1e85\n\t{0xED4C0226B55E6F86, 0xCDE6FD5E09ABCF26}, // 1e86\n\t{0x544F8158315B05B4, 0x80B05E5AC60B6178}, // 1e87\n\t{0x696361AE3DB1C721, 0xA0DC75F1778E39D6}, // 1e88\n\t{0x03BC3A19CD1E38E9, 0xC913936DD571C84C}, // 1e89\n\t{0x04AB48A04065C723, 0xFB5878494ACE3A5F}, // 1e90\n\t{0x62EB0D64283F9C76, 0x9D174B2DCEC0E47B}, // 1e91\n\t{0x3BA5D0BD324F8394, 0xC45D1DF942711D9A}, // 1e92\n\t{0xCA8F44EC7EE36479, 0xF5746577930D6500}, // 1e93\n\t{0x7E998B13CF4E1ECB, 0x9968BF6ABBE85F20}, // 1e94\n\t{0x9E3FEDD8C321A67E, 0xBFC2EF456AE276E8}, // 1e95\n\t{0xC5CFE94EF3EA101E, 0xEFB3AB16C59B14A2}, // 1e96\n\t{0xBBA1F1D158724A12, 0x95D04AEE3B80ECE5}, // 1e97\n\t{0x2A8A6E45AE8EDC97, 0xBB445DA9CA61281F}, // 1e98\n\t{0xF52D09D71A3293BD, 0xEA1575143CF97226}, // 1e99\n\t{0x593C2626705F9C56, 0x924D692CA61BE758}, // 1e100\n\t{0x6F8B2FB00C77836C, 0xB6E0C377CFA2E12E}, // 1e101\n\t{0x0B6DFB9C0F956447, 0xE498F455C38B997A}, // 1e102\n\t{0x4724BD4189BD5EAC, 0x8EDF98B59A373FEC}, // 1e103\n\t{0x58EDEC91EC2CB657, 0xB2977EE300C50FE7}, // 1e104\n\t{0x2F2967B66737E3ED, 0xDF3D5E9BC0F653E1}, // 1e105\n\t{0xBD79E0D20082EE74, 0x8B865B215899F46C}, // 1e106\n\t{0xECD8590680A3AA11, 0xAE67F1E9AEC07187}, // 1e107\n\t{0xE80E6F4820CC9495, 0xDA01EE641A708DE9}, // 1e108\n\t{0x3109058D147FDCDD, 0x884134FE908658B2}, // 1e109\n\t{0xBD4B46F0599FD415, 0xAA51823E34A7EEDE}, // 1e110\n\t{0x6C9E18AC7007C91A, 0xD4E5E2CDC1D1EA96}, // 1e111\n\t{0x03E2CF6BC604DDB0, 0x850FADC09923329E}, // 1e112\n\t{0x84DB8346B786151C, 0xA6539930BF6BFF45}, // 1e113\n\t{0xE612641865679A63, 0xCFE87F7CEF46FF16}, // 1e114\n\t{0x4FCB7E8F3F60C07E, 0x81F14FAE158C5F6E}, // 1e115\n\t{0xE3BE5E330F38F09D, 0xA26DA3999AEF7749}, // 1e116\n\t{0x5CADF5BFD3072CC5, 0xCB090C8001AB551C}, // 1e117\n\t{0x73D9732FC7C8F7F6, 0xFDCB4FA002162A63}, // 1e118\n\t{0x2867E7FDDCDD9AFA, 0x9E9F11C4014DDA7E}, // 1e119\n\t{0xB281E1FD541501B8, 0xC646D63501A1511D}, // 1e120\n\t{0x1F225A7CA91A4226, 0xF7D88BC24209A565}, // 1e121\n\t{0x3375788DE9B06958, 0x9AE757596946075F}, // 1e122\n\t{0x0052D6B1641C83AE, 0xC1A12D2FC3978937}, // 1e123\n\t{0xC0678C5DBD23A49A, 0xF209787BB47D6B84}, // 1e124\n\t{0xF840B7BA963646E0, 0x9745EB4D50CE6332}, // 1e125\n\t{0xB650E5A93BC3D898, 0xBD176620A501FBFF}, // 1e126\n\t{0xA3E51F138AB4CEBE, 0xEC5D3FA8CE427AFF}, // 1e127\n\t{0xC66F336C36B10137, 0x93BA47C980E98CDF}, // 1e128\n\t{0xB80B0047445D4184, 0xB8A8D9BBE123F017}, // 1e129\n\t{0xA60DC059157491E5, 0xE6D3102AD96CEC1D}, // 1e130\n\t{0x87C89837AD68DB2F, 0x9043EA1AC7E41392}, // 1e131\n\t{0x29BABE4598C311FB, 0xB454E4A179DD1877}, // 1e132\n\t{0xF4296DD6FEF3D67A, 0xE16A1DC9D8545E94}, // 1e133\n\t{0x1899E4A65F58660C, 0x8CE2529E2734BB1D}, // 1e134\n\t{0x5EC05DCFF72E7F8F, 0xB01AE745B101E9E4}, // 1e135\n\t{0x76707543F4FA1F73, 0xDC21A1171D42645D}, // 1e136\n\t{0x6A06494A791C53A8, 0x899504AE72497EBA}, // 1e137\n\t{0x0487DB9D17636892, 0xABFA45DA0EDBDE69}, // 1e138\n\t{0x45A9D2845D3C42B6, 0xD6F8D7509292D603}, // 1e139\n\t{0x0B8A2392BA45A9B2, 0x865B86925B9BC5C2}, // 1e140\n\t{0x8E6CAC7768D7141E, 0xA7F26836F282B732}, // 1e141\n\t{0x3207D795430CD926, 0xD1EF0244AF2364FF}, // 1e142\n\t{0x7F44E6BD49E807B8, 0x8335616AED761F1F}, // 1e143\n\t{0x5F16206C9C6209A6, 0xA402B9C5A8D3A6E7}, // 1e144\n\t{0x36DBA887C37A8C0F, 0xCD036837130890A1}, // 1e145\n\t{0xC2494954DA2C9789, 0x802221226BE55A64}, // 1e146\n\t{0xF2DB9BAA10B7BD6C, 0xA02AA96B06DEB0FD}, // 1e147\n\t{0x6F92829494E5ACC7, 0xC83553C5C8965D3D}, // 1e148\n\t{0xCB772339BA1F17F9, 0xFA42A8B73ABBF48C}, // 1e149\n\t{0xFF2A760414536EFB, 0x9C69A97284B578D7}, // 1e150\n\t{0xFEF5138519684ABA, 0xC38413CF25E2D70D}, // 1e151\n\t{0x7EB258665FC25D69, 0xF46518C2EF5B8CD1}, // 1e152\n\t{0xEF2F773FFBD97A61, 0x98BF2F79D5993802}, // 1e153\n\t{0xAAFB550FFACFD8FA, 0xBEEEFB584AFF8603}, // 1e154\n\t{0x95BA2A53F983CF38, 0xEEAABA2E5DBF6784}, // 1e155\n\t{0xDD945A747BF26183, 0x952AB45CFA97A0B2}, // 1e156\n\t{0x94F971119AEEF9E4, 0xBA756174393D88DF}, // 1e157\n\t{0x7A37CD5601AAB85D, 0xE912B9D1478CEB17}, // 1e158\n\t{0xAC62E055C10AB33A, 0x91ABB422CCB812EE}, // 1e159\n\t{0x577B986B314D6009, 0xB616A12B7FE617AA}, // 1e160\n\t{0xED5A7E85FDA0B80B, 0xE39C49765FDF9D94}, // 1e161\n\t{0x14588F13BE847307, 0x8E41ADE9FBEBC27D}, // 1e162\n\t{0x596EB2D8AE258FC8, 0xB1D219647AE6B31C}, // 1e163\n\t{0x6FCA5F8ED9AEF3BB, 0xDE469FBD99A05FE3}, // 1e164\n\t{0x25DE7BB9480D5854, 0x8AEC23D680043BEE}, // 1e165\n\t{0xAF561AA79A10AE6A, 0xADA72CCC20054AE9}, // 1e166\n\t{0x1B2BA1518094DA04, 0xD910F7FF28069DA4}, // 1e167\n\t{0x90FB44D2F05D0842, 0x87AA9AFF79042286}, // 1e168\n\t{0x353A1607AC744A53, 0xA99541BF57452B28}, // 1e169\n\t{0x42889B8997915CE8, 0xD3FA922F2D1675F2}, // 1e170\n\t{0x69956135FEBADA11, 0x847C9B5D7C2E09B7}, // 1e171\n\t{0x43FAB9837E699095, 0xA59BC234DB398C25}, // 1e172\n\t{0x94F967E45E03F4BB, 0xCF02B2C21207EF2E}, // 1e173\n\t{0x1D1BE0EEBAC278F5, 0x8161AFB94B44F57D}, // 1e174\n\t{0x6462D92A69731732, 0xA1BA1BA79E1632DC}, // 1e175\n\t{0x7D7B8F7503CFDCFE, 0xCA28A291859BBF93}, // 1e176\n\t{0x5CDA735244C3D43E, 0xFCB2CB35E702AF78}, // 1e177\n\t{0x3A0888136AFA64A7, 0x9DEFBF01B061ADAB}, // 1e178\n\t{0x088AAA1845B8FDD0, 0xC56BAEC21C7A1916}, // 1e179\n\t{0x8AAD549E57273D45, 0xF6C69A72A3989F5B}, // 1e180\n\t{0x36AC54E2F678864B, 0x9A3C2087A63F6399}, // 1e181\n\t{0x84576A1BB416A7DD, 0xC0CB28A98FCF3C7F}, // 1e182\n\t{0x656D44A2A11C51D5, 0xF0FDF2D3F3C30B9F}, // 1e183\n\t{0x9F644AE5A4B1B325, 0x969EB7C47859E743}, // 1e184\n\t{0x873D5D9F0DDE1FEE, 0xBC4665B596706114}, // 1e185\n\t{0xA90CB506D155A7EA, 0xEB57FF22FC0C7959}, // 1e186\n\t{0x09A7F12442D588F2, 0x9316FF75DD87CBD8}, // 1e187\n\t{0x0C11ED6D538AEB2F, 0xB7DCBF5354E9BECE}, // 1e188\n\t{0x8F1668C8A86DA5FA, 0xE5D3EF282A242E81}, // 1e189\n\t{0xF96E017D694487BC, 0x8FA475791A569D10}, // 1e190\n\t{0x37C981DCC395A9AC, 0xB38D92D760EC4455}, // 1e191\n\t{0x85BBE253F47B1417, 0xE070F78D3927556A}, // 1e192\n\t{0x93956D7478CCEC8E, 0x8C469AB843B89562}, // 1e193\n\t{0x387AC8D1970027B2, 0xAF58416654A6BABB}, // 1e194\n\t{0x06997B05FCC0319E, 0xDB2E51BFE9D0696A}, // 1e195\n\t{0x441FECE3BDF81F03, 0x88FCF317F22241E2}, // 1e196\n\t{0xD527E81CAD7626C3, 0xAB3C2FDDEEAAD25A}, // 1e197\n\t{0x8A71E223D8D3B074, 0xD60B3BD56A5586F1}, // 1e198\n\t{0xF6872D5667844E49, 0x85C7056562757456}, // 1e199\n\t{0xB428F8AC016561DB, 0xA738C6BEBB12D16C}, // 1e200\n\t{0xE13336D701BEBA52, 0xD106F86E69D785C7}, // 1e201\n\t{0xECC0024661173473, 0x82A45B450226B39C}, // 1e202\n\t{0x27F002D7F95D0190, 0xA34D721642B06084}, // 1e203\n\t{0x31EC038DF7B441F4, 0xCC20CE9BD35C78A5}, // 1e204\n\t{0x7E67047175A15271, 0xFF290242C83396CE}, // 1e205\n\t{0x0F0062C6E984D386, 0x9F79A169BD203E41}, // 1e206\n\t{0x52C07B78A3E60868, 0xC75809C42C684DD1}, // 1e207\n\t{0xA7709A56CCDF8A82, 0xF92E0C3537826145}, // 1e208\n\t{0x88A66076400BB691, 0x9BBCC7A142B17CCB}, // 1e209\n\t{0x6ACFF893D00EA435, 0xC2ABF989935DDBFE}, // 1e210\n\t{0x0583F6B8C4124D43, 0xF356F7EBF83552FE}, // 1e211\n\t{0xC3727A337A8B704A, 0x98165AF37B2153DE}, // 1e212\n\t{0x744F18C0592E4C5C, 0xBE1BF1B059E9A8D6}, // 1e213\n\t{0x1162DEF06F79DF73, 0xEDA2EE1C7064130C}, // 1e214\n\t{0x8ADDCB5645AC2BA8, 0x9485D4D1C63E8BE7}, // 1e215\n\t{0x6D953E2BD7173692, 0xB9A74A0637CE2EE1}, // 1e216\n\t{0xC8FA8DB6CCDD0437, 0xE8111C87C5C1BA99}, // 1e217\n\t{0x1D9C9892400A22A2, 0x910AB1D4DB9914A0}, // 1e218\n\t{0x2503BEB6D00CAB4B, 0xB54D5E4A127F59C8}, // 1e219\n\t{0x2E44AE64840FD61D, 0xE2A0B5DC971F303A}, // 1e220\n\t{0x5CEAECFED289E5D2, 0x8DA471A9DE737E24}, // 1e221\n\t{0x7425A83E872C5F47, 0xB10D8E1456105DAD}, // 1e222\n\t{0xD12F124E28F77719, 0xDD50F1996B947518}, // 1e223\n\t{0x82BD6B70D99AAA6F, 0x8A5296FFE33CC92F}, // 1e224\n\t{0x636CC64D1001550B, 0xACE73CBFDC0BFB7B}, // 1e225\n\t{0x3C47F7E05401AA4E, 0xD8210BEFD30EFA5A}, // 1e226\n\t{0x65ACFAEC34810A71, 0x8714A775E3E95C78}, // 1e227\n\t{0x7F1839A741A14D0D, 0xA8D9D1535CE3B396}, // 1e228\n\t{0x1EDE48111209A050, 0xD31045A8341CA07C}, // 1e229\n\t{0x934AED0AAB460432, 0x83EA2B892091E44D}, // 1e230\n\t{0xF81DA84D5617853F, 0xA4E4B66B68B65D60}, // 1e231\n\t{0x36251260AB9D668E, 0xCE1DE40642E3F4B9}, // 1e232\n\t{0xC1D72B7C6B426019, 0x80D2AE83E9CE78F3}, // 1e233\n\t{0xB24CF65B8612F81F, 0xA1075A24E4421730}, // 1e234\n\t{0xDEE033F26797B627, 0xC94930AE1D529CFC}, // 1e235\n\t{0x169840EF017DA3B1, 0xFB9B7CD9A4A7443C}, // 1e236\n\t{0x8E1F289560EE864E, 0x9D412E0806E88AA5}, // 1e237\n\t{0xF1A6F2BAB92A27E2, 0xC491798A08A2AD4E}, // 1e238\n\t{0xAE10AF696774B1DB, 0xF5B5D7EC8ACB58A2}, // 1e239\n\t{0xACCA6DA1E0A8EF29, 0x9991A6F3D6BF1765}, // 1e240\n\t{0x17FD090A58D32AF3, 0xBFF610B0CC6EDD3F}, // 1e241\n\t{0xDDFC4B4CEF07F5B0, 0xEFF394DCFF8A948E}, // 1e242\n\t{0x4ABDAF101564F98E, 0x95F83D0A1FB69CD9}, // 1e243\n\t{0x9D6D1AD41ABE37F1, 0xBB764C4CA7A4440F}, // 1e244\n\t{0x84C86189216DC5ED, 0xEA53DF5FD18D5513}, // 1e245\n\t{0x32FD3CF5B4E49BB4, 0x92746B9BE2F8552C}, // 1e246\n\t{0x3FBC8C33221DC2A1, 0xB7118682DBB66A77}, // 1e247\n\t{0x0FABAF3FEAA5334A, 0xE4D5E82392A40515}, // 1e248\n\t{0x29CB4D87F2A7400E, 0x8F05B1163BA6832D}, // 1e249\n\t{0x743E20E9EF511012, 0xB2C71D5BCA9023F8}, // 1e250\n\t{0x914DA9246B255416, 0xDF78E4B2BD342CF6}, // 1e251\n\t{0x1AD089B6C2F7548E, 0x8BAB8EEFB6409C1A}, // 1e252\n\t{0xA184AC2473B529B1, 0xAE9672ABA3D0C320}, // 1e253\n\t{0xC9E5D72D90A2741E, 0xDA3C0F568CC4F3E8}, // 1e254\n\t{0x7E2FA67C7A658892, 0x8865899617FB1871}, // 1e255\n\t{0xDDBB901B98FEEAB7, 0xAA7EEBFB9DF9DE8D}, // 1e256\n\t{0x552A74227F3EA565, 0xD51EA6FA85785631}, // 1e257\n\t{0xD53A88958F87275F, 0x8533285C936B35DE}, // 1e258\n\t{0x8A892ABAF368F137, 0xA67FF273B8460356}, // 1e259\n\t{0x2D2B7569B0432D85, 0xD01FEF10A657842C}, // 1e260\n\t{0x9C3B29620E29FC73, 0x8213F56A67F6B29B}, // 1e261\n\t{0x8349F3BA91B47B8F, 0xA298F2C501F45F42}, // 1e262\n\t{0x241C70A936219A73, 0xCB3F2F7642717713}, // 1e263\n\t{0xED238CD383AA0110, 0xFE0EFB53D30DD4D7}, // 1e264\n\t{0xF4363804324A40AA, 0x9EC95D1463E8A506}, // 1e265\n\t{0xB143C6053EDCD0D5, 0xC67BB4597CE2CE48}, // 1e266\n\t{0xDD94B7868E94050A, 0xF81AA16FDC1B81DA}, // 1e267\n\t{0xCA7CF2B4191C8326, 0x9B10A4E5E9913128}, // 1e268\n\t{0xFD1C2F611F63A3F0, 0xC1D4CE1F63F57D72}, // 1e269\n\t{0xBC633B39673C8CEC, 0xF24A01A73CF2DCCF}, // 1e270\n\t{0xD5BE0503E085D813, 0x976E41088617CA01}, // 1e271\n\t{0x4B2D8644D8A74E18, 0xBD49D14AA79DBC82}, // 1e272\n\t{0xDDF8E7D60ED1219E, 0xEC9C459D51852BA2}, // 1e273\n\t{0xCABB90E5C942B503, 0x93E1AB8252F33B45}, // 1e274\n\t{0x3D6A751F3B936243, 0xB8DA1662E7B00A17}, // 1e275\n\t{0x0CC512670A783AD4, 0xE7109BFBA19C0C9D}, // 1e276\n\t{0x27FB2B80668B24C5, 0x906A617D450187E2}, // 1e277\n\t{0xB1F9F660802DEDF6, 0xB484F9DC9641E9DA}, // 1e278\n\t{0x5E7873F8A0396973, 0xE1A63853BBD26451}, // 1e279\n\t{0xDB0B487B6423E1E8, 0x8D07E33455637EB2}, // 1e280\n\t{0x91CE1A9A3D2CDA62, 0xB049DC016ABC5E5F}, // 1e281\n\t{0x7641A140CC7810FB, 0xDC5C5301C56B75F7}, // 1e282\n\t{0xA9E904C87FCB0A9D, 0x89B9B3E11B6329BA}, // 1e283\n\t{0x546345FA9FBDCD44, 0xAC2820D9623BF429}, // 1e284\n\t{0xA97C177947AD4095, 0xD732290FBACAF133}, // 1e285\n\t{0x49ED8EABCCCC485D, 0x867F59A9D4BED6C0}, // 1e286\n\t{0x5C68F256BFFF5A74, 0xA81F301449EE8C70}, // 1e287\n\t{0x73832EEC6FFF3111, 0xD226FC195C6A2F8C}, // 1e288\n\t{0xC831FD53C5FF7EAB, 0x83585D8FD9C25DB7}, // 1e289\n\t{0xBA3E7CA8B77F5E55, 0xA42E74F3D032F525}, // 1e290\n\t{0x28CE1BD2E55F35EB, 0xCD3A1230C43FB26F}, // 1e291\n\t{0x7980D163CF5B81B3, 0x80444B5E7AA7CF85}, // 1e292\n\t{0xD7E105BCC332621F, 0xA0555E361951C366}, // 1e293\n\t{0x8DD9472BF3FEFAA7, 0xC86AB5C39FA63440}, // 1e294\n\t{0xB14F98F6F0FEB951, 0xFA856334878FC150}, // 1e295\n\t{0x6ED1BF9A569F33D3, 0x9C935E00D4B9D8D2}, // 1e296\n\t{0x0A862F80EC4700C8, 0xC3B8358109E84F07}, // 1e297\n\t{0xCD27BB612758C0FA, 0xF4A642E14C6262C8}, // 1e298\n\t{0x8038D51CB897789C, 0x98E7E9CCCFBD7DBD}, // 1e299\n\t{0xE0470A63E6BD56C3, 0xBF21E44003ACDD2C}, // 1e300\n\t{0x1858CCFCE06CAC74, 0xEEEA5D5004981478}, // 1e301\n\t{0x0F37801E0C43EBC8, 0x95527A5202DF0CCB}, // 1e302\n\t{0xD30560258F54E6BA, 0xBAA718E68396CFFD}, // 1e303\n\t{0x47C6B82EF32A2069, 0xE950DF20247C83FD}, // 1e304\n\t{0x4CDC331D57FA5441, 0x91D28B7416CDD27E}, // 1e305\n\t{0xE0133FE4ADF8E952, 0xB6472E511C81471D}, // 1e306\n\t{0x58180FDDD97723A6, 0xE3D8F9E563A198E5}, // 1e307\n\t{0x570F09EAA7EA7648, 0x8E679C2F5E44FF8F}, // 1e308\n\t{0x2CD2CC6551E513DA, 0xB201833B35D63F73}, // 1e309\n\t{0xF8077F7EA65E58D1, 0xDE81E40A034BCF4F}, // 1e310\n\t{0xFB04AFAF27FAF782, 0x8B112E86420F6191}, // 1e311\n\t{0x79C5DB9AF1F9B563, 0xADD57A27D29339F6}, // 1e312\n\t{0x18375281AE7822BC, 0xD94AD8B1C7380874}, // 1e313\n\t{0x8F2293910D0B15B5, 0x87CEC76F1C830548}, // 1e314\n\t{0xB2EB3875504DDB22, 0xA9C2794AE3A3C69A}, // 1e315\n\t{0x5FA60692A46151EB, 0xD433179D9C8CB841}, // 1e316\n\t{0xDBC7C41BA6BCD333, 0x849FEEC281D7F328}, // 1e317\n\t{0x12B9B522906C0800, 0xA5C7EA73224DEFF3}, // 1e318\n\t{0xD768226B34870A00, 0xCF39E50FEAE16BEF}, // 1e319\n\t{0xE6A1158300D46640, 0x81842F29F2CCE375}, // 1e320\n\t{0x60495AE3C1097FD0, 0xA1E53AF46F801C53}, // 1e321\n\t{0x385BB19CB14BDFC4, 0xCA5E89B18B602368}, // 1e322\n\t{0x46729E03DD9ED7B5, 0xFCF62C1DEE382C42}, // 1e323\n\t{0x6C07A2C26A8346D1, 0x9E19DB92B4E31BA9}, // 1e324\n\t{0xC7098B7305241885, 0xC5A05277621BE293}, // 1e325\n\t{0xB8CBEE4FC66D1EA7, 0xF70867153AA2DB38}, // 1e326\n\t{0x737F74F1DC043328, 0x9A65406D44A5C903}, // 1e327\n\t{0x505F522E53053FF2, 0xC0FE908895CF3B44}, // 1e328\n\t{0x647726B9E7C68FEF, 0xF13E34AABB430A15}, // 1e329\n\t{0x5ECA783430DC19F5, 0x96C6E0EAB509E64D}, // 1e330\n\t{0xB67D16413D132072, 0xBC789925624C5FE0}, // 1e331\n\t{0xE41C5BD18C57E88F, 0xEB96BF6EBADF77D8}, // 1e332\n\t{0x8E91B962F7B6F159, 0x933E37A534CBAAE7}, // 1e333\n\t{0x723627BBB5A4ADB0, 0xB80DC58E81FE95A1}, // 1e334\n\t{0xCEC3B1AAA30DD91C, 0xE61136F2227E3B09}, // 1e335\n\t{0x213A4F0AA5E8A7B1, 0x8FCAC257558EE4E6}, // 1e336\n\t{0xA988E2CD4F62D19D, 0xB3BD72ED2AF29E1F}, // 1e337\n\t{0x93EB1B80A33B8605, 0xE0ACCFA875AF45A7}, // 1e338\n\t{0xBC72F130660533C3, 0x8C6C01C9498D8B88}, // 1e339\n\t{0xEB8FAD7C7F8680B4, 0xAF87023B9BF0EE6A}, // 1e340\n\t{0xA67398DB9F6820E1, 0xDB68C2CA82ED2A05}, // 1e341\n\t{0x88083F8943A1148C, 0x892179BE91D43A43}, // 1e342\n\t{0x6A0A4F6B948959B0, 0xAB69D82E364948D4}, // 1e343\n\t{0x848CE34679ABB01C, 0xD6444E39C3DB9B09}, // 1e344\n\t{0xF2D80E0C0C0B4E11, 0x85EAB0E41A6940E5}, // 1e345\n\t{0x6F8E118F0F0E2195, 0xA7655D1D2103911F}, // 1e346\n\t{0x4B7195F2D2D1A9FB, 0xD13EB46469447567}, // 1e347\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/json/eisel_lemire\n" } ] }, @@ -2057,10 +1909,6 @@ "name": "floatconv_test.gno", "body": "package ryu\n\nimport (\n\t\"math\"\n\t\"testing\"\n)\n\nfunc TestFormatFloat64(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tvalue float64\n\t\texpected string\n\t}{\n\t\t{\"positive infinity\", math.Inf(1), \"+Inf\"},\n\t\t{\"negative infinity\", math.Inf(-1), \"-Inf\"},\n\t\t{\"NaN\", math.NaN(), \"NaN\"},\n\t\t{\"zero\", 0.0, \"0e+00\"},\n\t\t{\"negative zero\", -0.0, \"0e+00\"},\n\t\t{\"positive number\", 3.14159, \"3.14159e+00\"},\n\t\t{\"negative number\", -2.71828, \"-2.71828e+00\"},\n\t\t{\"very small number\", 1.23e-20, \"1.23e-20\"},\n\t\t{\"very large number\", 1.23e+20, \"1.23e+20\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tresult := FormatFloat64(test.value)\n\t\t\tif result != test.expected {\n\t\t\t\tt.Errorf(\"FormatFloat64(%v) = %q, expected %q\", test.value, result, test.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/json/ryu\n" - }, { "name": "ryu64.gno", "body": "package ryu\n\nimport (\n\t\"math/bits\"\n)\n\ntype uint128 struct {\n\tlo uint64\n\thi uint64\n}\n\n// dec64 is a floating decimal type representing m * 10^e.\ntype dec64 struct {\n\tm uint64\n\te int32\n}\n\nfunc (d dec64) append(b []byte, neg bool) []byte {\n\t// Step 5: Print the decimal representation.\n\tif neg {\n\t\tb = append(b, '-')\n\t}\n\n\tout := d.m\n\toutLen := decimalLen64(out)\n\tbufLen := outLen\n\tif bufLen \u003e 1 {\n\t\tbufLen++ // extra space for '.'\n\t}\n\n\t// Print the decimal digits.\n\tn := len(b)\n\tif cap(b)-len(b) \u003e= bufLen {\n\t\t// Avoid function call in the common case.\n\t\tb = b[:len(b)+bufLen]\n\t} else {\n\t\tb = append(b, make([]byte, bufLen)...)\n\t}\n\n\t// Avoid expensive 64-bit divisions.\n\t// We have at most 17 digits, and uint32 can store 9 digits.\n\t// If the output doesn't fit into a uint32, cut off 8 digits\n\t// so the rest will fit into a uint32.\n\tvar i int\n\tif out\u003e\u003e32 \u003e 0 {\n\t\tvar out32 uint32\n\t\tout, out32 = out/1e8, uint32(out%1e8)\n\t\tfor ; i \u003c 8; i++ {\n\t\t\tb[n+outLen-i] = '0' + byte(out32%10)\n\t\t\tout32 /= 10\n\t\t}\n\t}\n\tout32 := uint32(out)\n\tfor ; i \u003c outLen-1; i++ {\n\t\tb[n+outLen-i] = '0' + byte(out32%10)\n\t\tout32 /= 10\n\t}\n\tb[n] = '0' + byte(out32%10)\n\n\t// Print the '.' if needed.\n\tif outLen \u003e 1 {\n\t\tb[n+1] = '.'\n\t}\n\n\t// Print the exponent.\n\tb = append(b, 'e')\n\texp := d.e + int32(outLen) - 1\n\tif exp \u003c 0 {\n\t\tb = append(b, '-')\n\t\texp = -exp\n\t} else {\n\t\t// Unconditionally print a + here to match strconv's formatting.\n\t\tb = append(b, '+')\n\t}\n\t// Always print at least two digits to match strconv's formatting.\n\td2 := exp % 10\n\texp /= 10\n\td1 := exp % 10\n\td0 := exp / 10\n\tif d0 \u003e 0 {\n\t\tb = append(b, '0'+byte(d0))\n\t}\n\tb = append(b, '0'+byte(d1), '0'+byte(d2))\n\n\treturn b\n}\n\nfunc float64ToDecimalExactInt(mant, exp uint64) (d dec64, ok bool) {\n\te := exp - bias64\n\tif e \u003e mantBits64 {\n\t\treturn d, false\n\t}\n\tshift := mantBits64 - e\n\tmant |= 1 \u003c\u003c mantBits64 // implicit 1\n\td.m = mant \u003e\u003e shift\n\tif d.m\u003c\u003cshift != mant {\n\t\treturn d, false\n\t}\n\n\tfor d.m%10 == 0 {\n\t\td.m /= 10\n\t\td.e++\n\t}\n\treturn d, true\n}\n\nfunc float64ToDecimal(mant, exp uint64) dec64 {\n\tvar e2 int32\n\tvar m2 uint64\n\tif exp == 0 {\n\t\t// We subtract 2 so that the bounds computation has\n\t\t// 2 additional bits.\n\t\te2 = 1 - bias64 - mantBits64 - 2\n\t\tm2 = mant\n\t} else {\n\t\te2 = int32(exp) - bias64 - mantBits64 - 2\n\t\tm2 = uint64(1)\u003c\u003cmantBits64 | mant\n\t}\n\teven := m2\u00261 == 0\n\tacceptBounds := even\n\n\t// Step 2: Determine the interval of valid decimal representations.\n\tmv := 4 * m2\n\tmmShift := boolToUint64(mant != 0 || exp \u003c= 1)\n\t// We would compute mp and mm like this:\n\t// mp := 4 * m2 + 2;\n\t// mm := mv - 1 - mmShift;\n\n\t// Step 3: Convert to a decimal power base uing 128-bit arithmetic.\n\tvar (\n\t\tvr, vp, vm uint64\n\t\te10 int32\n\t\tvmIsTrailingZeros bool\n\t\tvrIsTrailingZeros bool\n\t)\n\tif e2 \u003e= 0 {\n\t\t// This expression is slightly faster than max(0, log10Pow2(e2) - 1).\n\t\tq := log10Pow2(e2) - boolToUint32(e2 \u003e 3)\n\t\te10 = int32(q)\n\t\tk := pow5InvNumBits64 + pow5Bits(int32(q)) - 1\n\t\ti := -e2 + int32(q) + k\n\t\tmul := pow5InvSplit64[q]\n\t\tvr = mulShift64(4*m2, mul, i)\n\t\tvp = mulShift64(4*m2+2, mul, i)\n\t\tvm = mulShift64(4*m2-1-mmShift, mul, i)\n\t\tif q \u003c= 21 {\n\t\t\t// This should use q \u003c= 22, but I think 21 is also safe.\n\t\t\t// Smaller values may still be safe, but it's more\n\t\t\t// difficult to reason about them. Only one of mp, mv,\n\t\t\t// and mm can be a multiple of 5, if any.\n\t\t\tif mv%5 == 0 {\n\t\t\t\tvrIsTrailingZeros = multipleOfPowerOfFive64(mv, q)\n\t\t\t} else if acceptBounds {\n\t\t\t\t// Same as min(e2 + (^mm \u0026 1), pow5Factor64(mm)) \u003e= q\n\t\t\t\t// \u003c=\u003e e2 + (^mm \u0026 1) \u003e= q \u0026\u0026 pow5Factor64(mm) \u003e= q\n\t\t\t\t// \u003c=\u003e true \u0026\u0026 pow5Factor64(mm) \u003e= q, since e2 \u003e= q.\n\t\t\t\tvmIsTrailingZeros = multipleOfPowerOfFive64(mv-1-mmShift, q)\n\t\t\t} else if multipleOfPowerOfFive64(mv+2, q) {\n\t\t\t\tvp--\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// This expression is slightly faster than max(0, log10Pow5(-e2) - 1).\n\t\tq := log10Pow5(-e2) - boolToUint32(-e2 \u003e 1)\n\t\te10 = int32(q) + e2\n\t\ti := -e2 - int32(q)\n\t\tk := pow5Bits(i) - pow5NumBits64\n\t\tj := int32(q) - k\n\t\tmul := pow5Split64[i]\n\t\tvr = mulShift64(4*m2, mul, j)\n\t\tvp = mulShift64(4*m2+2, mul, j)\n\t\tvm = mulShift64(4*m2-1-mmShift, mul, j)\n\t\tif q \u003c= 1 {\n\t\t\t// {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.\n\t\t\t// mv = 4 * m2, so it always has at least two trailing 0 bits.\n\t\t\tvrIsTrailingZeros = true\n\t\t\tif acceptBounds {\n\t\t\t\t// mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1.\n\t\t\t\tvmIsTrailingZeros = mmShift == 1\n\t\t\t} else {\n\t\t\t\t// mp = mv + 2, so it always has at least one trailing 0 bit.\n\t\t\t\tvp--\n\t\t\t}\n\t\t} else if q \u003c 63 { // TODO(ulfjack/cespare): Use a tighter bound here.\n\t\t\t// We need to compute min(ntz(mv), pow5Factor64(mv) - e2) \u003e= q - 1\n\t\t\t// \u003c=\u003e ntz(mv) \u003e= q - 1 \u0026\u0026 pow5Factor64(mv) - e2 \u003e= q - 1\n\t\t\t// \u003c=\u003e ntz(mv) \u003e= q - 1 (e2 is negative and -e2 \u003e= q)\n\t\t\t// \u003c=\u003e (mv \u0026 ((1 \u003c\u003c (q - 1)) - 1)) == 0\n\t\t\t// We also need to make sure that the left shift does not overflow.\n\t\t\tvrIsTrailingZeros = multipleOfPowerOfTwo64(mv, q-1)\n\t\t}\n\t}\n\n\t// Step 4: Find the shortest decimal representation\n\t// in the interval of valid representations.\n\tvar removed int32\n\tvar lastRemovedDigit uint8\n\tvar out uint64\n\t// On average, we remove ~2 digits.\n\tif vmIsTrailingZeros || vrIsTrailingZeros {\n\t\t// General case, which happens rarely (~0.7%).\n\t\tfor {\n\t\t\tvpDiv10 := vp / 10\n\t\t\tvmDiv10 := vm / 10\n\t\t\tif vpDiv10 \u003c= vmDiv10 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvmMod10 := vm % 10\n\t\t\tvrDiv10 := vr / 10\n\t\t\tvrMod10 := vr % 10\n\t\t\tvmIsTrailingZeros = vmIsTrailingZeros \u0026\u0026 vmMod10 == 0\n\t\t\tvrIsTrailingZeros = vrIsTrailingZeros \u0026\u0026 lastRemovedDigit == 0\n\t\t\tlastRemovedDigit = uint8(vrMod10)\n\t\t\tvr = vrDiv10\n\t\t\tvp = vpDiv10\n\t\t\tvm = vmDiv10\n\t\t\tremoved++\n\t\t}\n\t\tif vmIsTrailingZeros {\n\t\t\tfor {\n\t\t\t\tvmDiv10 := vm / 10\n\t\t\t\tvmMod10 := vm % 10\n\t\t\t\tif vmMod10 != 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tvpDiv10 := vp / 10\n\t\t\t\tvrDiv10 := vr / 10\n\t\t\t\tvrMod10 := vr % 10\n\t\t\t\tvrIsTrailingZeros = vrIsTrailingZeros \u0026\u0026 lastRemovedDigit == 0\n\t\t\t\tlastRemovedDigit = uint8(vrMod10)\n\t\t\t\tvr = vrDiv10\n\t\t\t\tvp = vpDiv10\n\t\t\t\tvm = vmDiv10\n\t\t\t\tremoved++\n\t\t\t}\n\t\t}\n\t\tif vrIsTrailingZeros \u0026\u0026 lastRemovedDigit == 5 \u0026\u0026 vr%2 == 0 {\n\t\t\t// Round even if the exact number is .....50..0.\n\t\t\tlastRemovedDigit = 4\n\t\t}\n\t\tout = vr\n\t\t// We need to take vr + 1 if vr is outside bounds\n\t\t// or we need to round up.\n\t\tif (vr == vm \u0026\u0026 (!acceptBounds || !vmIsTrailingZeros)) || lastRemovedDigit \u003e= 5 {\n\t\t\tout++\n\t\t}\n\t} else {\n\t\t// Specialized for the common case (~99.3%).\n\t\t// Percentages below are relative to this.\n\t\troundUp := false\n\t\tfor vp/100 \u003e vm/100 {\n\t\t\t// Optimization: remove two digits at a time (~86.2%).\n\t\t\troundUp = vr%100 \u003e= 50\n\t\t\tvr /= 100\n\t\t\tvp /= 100\n\t\t\tvm /= 100\n\t\t\tremoved += 2\n\t\t}\n\t\t// Loop iterations below (approximately), without optimization above:\n\t\t// 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%\n\t\t// Loop iterations below (approximately), with optimization above:\n\t\t// 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%\n\t\tfor vp/10 \u003e vm/10 {\n\t\t\troundUp = vr%10 \u003e= 5\n\t\t\tvr /= 10\n\t\t\tvp /= 10\n\t\t\tvm /= 10\n\t\t\tremoved++\n\t\t}\n\t\t// We need to take vr + 1 if vr is outside bounds\n\t\t// or we need to round up.\n\t\tout = vr + boolToUint64(vr == vm || roundUp)\n\t}\n\n\treturn dec64{m: out, e: e10 + removed}\n}\n\nvar powersOf10 = [...]uint64{\n\t1e0,\n\t1e1,\n\t1e2,\n\t1e3,\n\t1e4,\n\t1e5,\n\t1e6,\n\t1e7,\n\t1e8,\n\t1e9,\n\t1e10,\n\t1e11,\n\t1e12,\n\t1e13,\n\t1e14,\n\t1e15,\n\t1e16,\n\t1e17,\n\t// We only need to find the length of at most 17 digit numbers.\n}\n\nfunc decimalLen64(u uint64) int {\n\t// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10\n\tlog2 := 64 - bits.LeadingZeros64(u) - 1\n\tt := (log2 + 1) * 1233 \u003e\u003e 12\n\treturn t - boolToInt(u \u003c powersOf10[t]) + 1\n}\n\nfunc mulShift64(m uint64, mul uint128, shift int32) uint64 {\n\thihi, hilo := bits.Mul64(m, mul.hi)\n\tlohi, _ := bits.Mul64(m, mul.lo)\n\tsum := uint128{hi: hihi, lo: lohi + hilo}\n\tif sum.lo \u003c lohi {\n\t\tsum.hi++ // overflow\n\t}\n\treturn shiftRight128(sum, shift-64)\n}\n\nfunc shiftRight128(v uint128, shift int32) uint64 {\n\t// The shift value is always modulo 64.\n\t// In the current implementation of the 64-bit version\n\t// of Ryu, the shift value is always \u003c 64.\n\t// (It is in the range [2, 59].)\n\t// Check this here in case a future change requires larger shift\n\t// values. In this case this function needs to be adjusted.\n\tassert(shift \u003c 64, \"shift \u003c 64\")\n\treturn (v.hi \u003c\u003c uint64(64-shift)) | (v.lo \u003e\u003e uint(shift))\n}\n\nfunc pow5Factor64(v uint64) uint32 {\n\tfor n := uint32(0); ; n++ {\n\t\tq, r := v/5, v%5\n\t\tif r != 0 {\n\t\t\treturn n\n\t\t}\n\t\tv = q\n\t}\n}\n\nfunc multipleOfPowerOfFive64(v uint64, p uint32) bool {\n\treturn pow5Factor64(v) \u003e= p\n}\n\nfunc multipleOfPowerOfTwo64(v uint64, p uint32) bool {\n\treturn uint32(bits.TrailingZeros64(v)) \u003e= p\n}\n" @@ -2135,10 +1983,6 @@ "name": "escape_test.gno", "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"unicode/utf8\"\n)\n\nfunc TestHexToInt(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tc byte\n\t\twant int\n\t}{\n\t\t{\"Digit 0\", '0', 0},\n\t\t{\"Digit 9\", '9', 9},\n\t\t{\"Uppercase A\", 'A', 10},\n\t\t{\"Uppercase F\", 'F', 15},\n\t\t{\"Lowercase a\", 'a', 10},\n\t\t{\"Lowercase f\", 'f', 15},\n\t\t{\"Invalid character1\", 'g', badHex},\n\t\t{\"Invalid character2\", 'G', badHex},\n\t\t{\"Invalid character3\", 'z', badHex},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := h2i(tt.c); got != tt.want {\n\t\t\t\tt.Errorf(\"h2i() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsSurrogatePair(t *testing.T) {\n\ttestCases := []struct {\n\t\tname string\n\t\tr rune\n\t\texpected bool\n\t}{\n\t\t{\"high surrogate start\", 0xD800, true},\n\t\t{\"high surrogate end\", 0xDBFF, true},\n\t\t{\"low surrogate start\", 0xDC00, true},\n\t\t{\"low surrogate end\", 0xDFFF, true},\n\t\t{\"Non-surrogate\", 0x0000, false},\n\t\t{\"Non-surrogate 2\", 0xE000, false},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tif got := isSurrogatePair(tc.r); got != tc.expected {\n\t\t\t\tt.Errorf(\"isSurrogate() = %v, want %v\", got, tc.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCombineSurrogates(t *testing.T) {\n\ttestCases := []struct {\n\t\thigh, low rune\n\t\texpected rune\n\t}{\n\t\t{0xD83D, 0xDC36, 0x1F436}, // 🐶 U+1F436 DOG FACE\n\t\t{0xD83D, 0xDE00, 0x1F600}, // 😀 U+1F600 GRINNING FACE\n\t\t{0xD83C, 0xDF03, 0x1F303}, // 🌃 U+1F303 NIGHT WITH STARS\n\t}\n\n\tfor _, tc := range testCases {\n\t\tresult := combineSurrogates(tc.high, tc.low)\n\t\tif result != tc.expected {\n\t\t\tt.Errorf(\"combineSurrogates(%U, %U) = %U; want %U\", tc.high, tc.low, result, tc.expected)\n\t\t}\n\t}\n}\n\nfunc TestDecodeSingleUnicodeEscape(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput []byte\n\t\texpected rune\n\t\tisValid bool\n\t}{\n\t\t// valid unicode escape sequences\n\t\t{[]byte(`\\u0041`), 'A', true},\n\t\t{[]byte(`\\u03B1`), 'α', true},\n\t\t{[]byte(`\\u00E9`), 'é', true}, // valid non-English character\n\t\t{[]byte(`\\u0021`), '!', true}, // valid special character\n\t\t{[]byte(`\\uFF11`), '1', true},\n\t\t{[]byte(`\\uD83D`), 0xD83D, true},\n\t\t{[]byte(`\\uDE03`), 0xDE03, true},\n\n\t\t// invalid unicode escape sequences\n\t\t{[]byte(`\\u004`), utf8.RuneError, false}, // too short\n\t\t{[]byte(`\\uXYZW`), utf8.RuneError, false}, // invalid hex\n\t\t{[]byte(`\\u00G1`), utf8.RuneError, false}, // non-hex character\n\t}\n\n\tfor _, tc := range testCases {\n\t\tresult, isValid := decodeSingleUnicodeEscape(tc.input)\n\t\tif result != tc.expected || isValid != tc.isValid {\n\t\t\tt.Errorf(\"decodeSingleUnicodeEscape(%s) = (%U, %v); want (%U, %v)\", tc.input, result, isValid, tc.expected, tc.isValid)\n\t\t}\n\t}\n}\n\nfunc TestDecodeUnicodeEscape(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput string\n\t\texpected rune\n\t\tsize int\n\t}{\n\t\t{\"\\\\u0041\", 'A', 6},\n\t\t{\"\\\\u03B1\", 'α', 6},\n\t\t{\"\\\\u1F600\", 0x1F60, 6},\n\t\t{\"\\\\uD830\\\\uDE03\", 0x1C203, 12},\n\t\t{\"\\\\uD800\\\\uDC00\", 0x00010000, 12},\n\n\t\t{\"\\\\u004\", utf8.RuneError, -1},\n\t\t{\"\\\\uXYZW\", utf8.RuneError, -1},\n\t\t{\"\\\\uD83D\\\\u0041\", utf8.RuneError, -1},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tr, size := decodeUnicodeEscape([]byte(tc.input))\n\t\tif r != tc.expected || size != tc.size {\n\t\t\tt.Errorf(\"decodeUnicodeEscape(%q) = (%U, %d); want (%U, %d)\", tc.input, r, size, tc.expected, tc.size)\n\t\t}\n\t}\n}\n\nfunc TestUnescapeToUTF8(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput []byte\n\t\texpectedIn int\n\t\texpectedOut int\n\t\tisError bool\n\t}{\n\t\t// valid escape sequences\n\t\t{[]byte(`\\n`), 2, 1, false},\n\t\t{[]byte(`\\t`), 2, 1, false},\n\t\t{[]byte(`\\u0041`), 6, 1, false},\n\t\t{[]byte(`\\u03B1`), 6, 2, false},\n\t\t{[]byte(`\\uD830\\uDE03`), 12, 4, false},\n\n\t\t// invalid escape sequences\n\t\t{[]byte(`\\`), -1, -1, true}, // incomplete escape sequence\n\t\t{[]byte(`\\x`), -1, -1, true}, // invalid escape character\n\t\t{[]byte(`\\u`), -1, -1, true}, // incomplete unicode escape sequence\n\t\t{[]byte(`\\u004`), -1, -1, true}, // invalid unicode escape sequence\n\t\t{[]byte(`\\uXYZW`), -1, -1, true}, // invalid unicode escape sequence\n\t\t{[]byte(`\\uD83D\\u0041`), -1, -1, true}, // invalid unicode escape sequence\n\t}\n\n\tfor _, tc := range testCases {\n\t\tinput := make([]byte, len(tc.input))\n\t\tcopy(input, tc.input)\n\t\toutput := make([]byte, utf8.UTFMax)\n\t\tinLen, outLen, err := processEscapedUTF8(input, output)\n\t\tif (err != nil) != tc.isError {\n\t\t\tt.Errorf(\"processEscapedUTF8(%q) = %v; want %v\", tc.input, err, tc.isError)\n\t\t}\n\n\t\tif inLen != tc.expectedIn || outLen != tc.expectedOut {\n\t\t\tt.Errorf(\"processEscapedUTF8(%q) = (%d, %d); want (%d, %d)\", tc.input, inLen, outLen, tc.expectedIn, tc.expectedOut)\n\t\t}\n\t}\n}\n\nfunc TestUnescape(t *testing.T) {\n\ttestCases := []struct {\n\t\tname string\n\t\tinput []byte\n\t\texpected []byte\n\t}{\n\t\t{\"NoEscape\", []byte(\"hello world\"), []byte(\"hello world\")},\n\t\t{\"SingleEscape\", []byte(\"hello\\\\nworld\"), []byte(\"hello\\nworld\")},\n\t\t{\"MultipleEscapes\", []byte(\"line1\\\\nline2\\\\r\\\\nline3\"), []byte(\"line1\\nline2\\r\\nline3\")},\n\t\t{\"UnicodeEscape\", []byte(\"snowman:\\\\u2603\"), []byte(\"snowman:\\u2603\")},\n\t\t{\"Complex\", []byte(\"tc\\\\n\\\\u2603\\\\r\\\\nend\"), []byte(\"tc\\n\\u2603\\r\\nend\")},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\toutput, _ := Unescape(tc.input, make([]byte, len(tc.input)+10))\n\t\t\tif !bytes.Equal(output, tc.expected) {\n\t\t\t\tt.Errorf(\"unescape(%q) = %q; want %q\", tc.input, output, tc.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnquoteBytes(t *testing.T) {\n\ttests := []struct {\n\t\tinput []byte\n\t\tborder byte\n\t\texpected []byte\n\t\tok bool\n\t}{\n\t\t{[]byte(\"\\\"hello\\\"\"), '\"', []byte(\"hello\"), true},\n\t\t{[]byte(\"'hello'\"), '\\'', []byte(\"hello\"), true},\n\t\t{[]byte(\"\\\"hello\"), '\"', nil, false},\n\t\t{[]byte(\"hello\\\"\"), '\"', nil, false},\n\t\t{[]byte(\"\\\"he\\\\\\\"llo\\\"\"), '\"', []byte(\"he\\\"llo\"), true},\n\t\t{[]byte(\"\\\"he\\\\nllo\\\"\"), '\"', []byte(\"he\\nllo\"), true},\n\t\t{[]byte(\"\\\"\\\"\"), '\"', []byte(\"\"), true},\n\t\t{[]byte(\"''\"), '\\'', []byte(\"\"), true},\n\t\t{[]byte(\"\\\"\\\\u0041\\\"\"), '\"', []byte(\"A\"), true},\n\t\t{[]byte(`\"Hello, 世界\"`), '\"', []byte(\"Hello, 世界\"), true},\n\t\t{[]byte(`\"Hello, \\x80\"`), '\"', nil, false},\n\t}\n\n\tfor _, tc := range tests {\n\t\tresult, pass := unquoteBytes(tc.input, tc.border)\n\n\t\tif pass != tc.ok {\n\t\t\tt.Errorf(\"unquoteBytes(%q) = %v; want %v\", tc.input, pass, tc.ok)\n\t\t}\n\n\t\tif !bytes.Equal(result, tc.expected) {\n\t\t\tt.Errorf(\"unquoteBytes(%q) = %q; want %q\", tc.input, result, tc.expected)\n\t\t}\n\t}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/json\n\nrequire (\n\tgno.land/p/demo/json/eisel_lemire v0.0.0-latest\n\tgno.land/p/demo/json/ryu v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "indent.gno", "body": "package json\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n)\n\n// indentGrowthFactor specifies the growth factor of indenting JSON input.\n// A factor no higher than 2 ensures that wasted space never exceeds 50%.\nconst indentGrowthFactor = 2\n\n// IndentJSON takes a JSON byte slice and a string for indentation,\n// then formats the JSON according to the specified indent string.\n// This function applies indentation rules as follows:\n//\n// 1. For top-level arrays and objects, no additional indentation is applied.\n//\n// 2. For nested structures like arrays within arrays or objects, indentation increases.\n//\n// 3. Indentation is applied after opening brackets ('[' or '{') and before closing brackets (']' or '}').\n//\n// 4. Commas and colons are handled appropriately to maintain valid JSON format.\n//\n// 5. Nested arrays within objects or arrays receive new lines and indentation based on their depth level.\n//\n// The function returns the formatted JSON as a byte slice and an error if any issues occurred during formatting.\nfunc Indent(data []byte, indent string) ([]byte, error) {\n\tvar (\n\t\tout bytes.Buffer\n\t\tlevel int\n\t\tinArray bool\n\t\tarrayDepth int\n\t)\n\n\tfor i := 0; i \u003c len(data); i++ {\n\t\tc := data[i] // current character\n\n\t\tswitch c {\n\t\tcase bracketOpen:\n\t\t\tarrayDepth++\n\t\t\tif arrayDepth \u003e 1 {\n\t\t\t\tlevel++ // increase the level if it's nested array\n\t\t\t\tinArray = true\n\n\t\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// case of the top-level array\n\t\t\t\tinArray = true\n\t\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase bracketClose:\n\t\t\tif inArray \u0026\u0026 arrayDepth \u003e 1 { // nested array\n\t\t\t\tlevel--\n\t\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tarrayDepth--\n\t\t\tif arrayDepth == 0 {\n\t\t\t\tinArray = false\n\t\t\t}\n\n\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\tcase curlyOpen:\n\t\t\t// check if the empty object or array\n\t\t\t// we don't need to apply the indent when it's empty containers.\n\t\t\tif i+1 \u003c len(data) \u0026\u0026 data[i+1] == curlyClose {\n\t\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\ti++ // skip next character\n\t\t\t\tif err := out.WriteByte(data[i]); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tlevel++\n\t\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase curlyClose:\n\t\t\tlevel--\n\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\tcase comma, colon:\n\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif inArray \u0026\u0026 arrayDepth \u003e 1 { // nested array\n\t\t\t\tif err := writeNewlineAndIndent(\u0026out, level, indent); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else if c == colon {\n\t\t\t\tif err := out.WriteByte(' '); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\tdefault:\n\t\t\tif err := out.WriteByte(c); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn out.Bytes(), nil\n}\n\nfunc writeNewlineAndIndent(out *bytes.Buffer, level int, indent string) error {\n\tif err := out.WriteByte('\\n'); err != nil {\n\t\treturn err\n\t}\n\n\tidt := strings.Repeat(indent, level*indentGrowthFactor)\n\tif _, err := out.WriteString(idt); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n" @@ -2205,10 +2049,6 @@ "name": "int32", "path": "gno.land/p/demo/math_eval/int32", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/math_eval/int32\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" - }, { "name": "int32.gno", "body": "// eval/int32 is a evaluator for int32 expressions.\n// This code is heavily forked from https://github.com/dengsgo/math-engine\n// which is licensed under Apache 2.0:\n// https://raw.githubusercontent.com/dengsgo/math-engine/298e2b57b7e7350d0f67bd036916efd5709abe25/LICENSE\npackage int32\n\nimport (\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nconst (\n\tIdentifier = iota\n\tNumber // numbers\n\tOperator // +, -, *, /, etc.\n\tVariable // x, y, z, etc. (one-letter only)\n)\n\ntype expression interface {\n\tString() string\n}\n\ntype expressionRaw struct {\n\texpression string\n\tType int\n\tFlag int\n\tOffset int\n}\n\ntype parser struct {\n\tInput string\n\tch byte\n\toffset int\n\terr error\n}\n\ntype expressionNumber struct {\n\tVal int\n\tStr string\n}\n\ntype expressionVariable struct {\n\tVal int\n\tStr string\n}\n\ntype expressionOperation struct {\n\tOp string\n\tLhs,\n\tRhs expression\n}\n\ntype ast struct {\n\trawexpressions []*expressionRaw\n\tsource string\n\tcurrentexpression *expressionRaw\n\tcurrentIndex int\n\tdepth int\n\terr error\n}\n\n// Parse takes an expression string, e.g. \"1+2\" and returns\n// a parsed expression. If there is an error it will return.\nfunc Parse(s string) (ar expression, err error) {\n\ttoks, err := lexer(s)\n\tif err != nil {\n\t\treturn\n\t}\n\tast, err := newAST(toks, s)\n\tif err != nil {\n\t\treturn\n\t}\n\tar, err = ast.parseExpression()\n\treturn\n}\n\n// Eval takes a parsed expression and a map of variables (or nil). The parsed\n// expression is evaluated using any variables and returns the\n// resulting int and/or error.\nfunc Eval(expr expression, variables map[string]int) (res int, err error) {\n\tif err != nil {\n\t\treturn\n\t}\n\tvar l, r int\n\tswitch expr.(type) {\n\tcase expressionVariable:\n\t\tast := expr.(expressionVariable)\n\t\tok := false\n\t\tif variables != nil {\n\t\t\tres, ok = variables[ast.Str]\n\t\t}\n\t\tif !ok {\n\t\t\terr = ufmt.Errorf(\"variable '%s' not found\", ast.Str)\n\t\t}\n\t\treturn\n\tcase expressionOperation:\n\t\tast := expr.(expressionOperation)\n\t\tl, err = Eval(ast.Lhs, variables)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tr, err = Eval(ast.Rhs, variables)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tswitch ast.Op {\n\t\tcase \"+\":\n\t\t\tres = l + r\n\t\tcase \"-\":\n\t\t\tres = l - r\n\t\tcase \"*\":\n\t\t\tres = l * r\n\t\tcase \"/\":\n\t\t\tif r == 0 {\n\t\t\t\terr = ufmt.Errorf(\"violation of arithmetic specification: a division by zero in Eval: [%d/%d]\", l, r)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tres = l / r\n\t\tcase \"%\":\n\t\t\tif r == 0 {\n\t\t\t\tres = 0\n\t\t\t} else {\n\t\t\t\tres = l % r\n\t\t\t}\n\t\tcase \"^\":\n\t\t\tres = l ^ r\n\t\tcase \"\u003e\u003e\":\n\t\t\tres = l \u003e\u003e r\n\t\tcase \"\u003c\u003c\":\n\t\t\tres = l \u003c\u003c r\n\t\tcase \"\u003e\":\n\t\t\tif l \u003e r {\n\t\t\t\tres = 1\n\t\t\t} else {\n\t\t\t\tres = 0\n\t\t\t}\n\t\tcase \"\u003c\":\n\t\t\tif l \u003c r {\n\t\t\t\tres = 1\n\t\t\t} else {\n\t\t\t\tres = 0\n\t\t\t}\n\t\tcase \"\u0026\":\n\t\t\tres = l \u0026 r\n\t\tcase \"|\":\n\t\t\tres = l | r\n\t\tdefault:\n\n\t\t}\n\tcase expressionNumber:\n\t\tres = expr.(expressionNumber).Val\n\t}\n\n\treturn\n}\n\nfunc expressionError(s string, pos int) string {\n\tr := strings.Repeat(\"-\", len(s)) + \"\\n\"\n\ts += \"\\n\"\n\tfor i := 0; i \u003c pos; i++ {\n\t\ts += \" \"\n\t}\n\ts += \"^\\n\"\n\treturn r + s + r\n}\n\nfunc (n expressionVariable) String() string {\n\treturn ufmt.Sprintf(\n\t\t\"expressionVariable: %s\",\n\t\tn.Str,\n\t)\n}\n\nfunc (n expressionNumber) String() string {\n\treturn ufmt.Sprintf(\n\t\t\"expressionNumber: %s\",\n\t\tn.Str,\n\t)\n}\n\nfunc (b expressionOperation) String() string {\n\treturn ufmt.Sprintf(\n\t\t\"expressionOperation: (%s %s %s)\",\n\t\tb.Op,\n\t\tb.Lhs.String(),\n\t\tb.Rhs.String(),\n\t)\n}\n\nfunc newAST(toks []*expressionRaw, s string) (*ast, error) {\n\ta := \u0026ast{\n\t\trawexpressions: toks,\n\t\tsource: s,\n\t}\n\tif a.rawexpressions == nil || len(a.rawexpressions) == 0 {\n\t\treturn a, errors.New(\"empty token\")\n\t} else {\n\t\ta.currentIndex = 0\n\t\ta.currentexpression = a.rawexpressions[0]\n\t}\n\treturn a, nil\n}\n\nfunc (a *ast) parseExpression() (expression, error) {\n\ta.depth++ // called depth\n\tlhs := a.parsePrimary()\n\tr := a.parseBinOpRHS(0, lhs)\n\ta.depth--\n\tif a.depth == 0 \u0026\u0026 a.currentIndex != len(a.rawexpressions) \u0026\u0026 a.err == nil {\n\t\treturn r, ufmt.Errorf(\"bad expression, reaching the end or missing the operator\\n%s\",\n\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t}\n\treturn r, nil\n}\n\nfunc (a *ast) getNextexpressionRaw() *expressionRaw {\n\ta.currentIndex++\n\tif a.currentIndex \u003c len(a.rawexpressions) {\n\t\ta.currentexpression = a.rawexpressions[a.currentIndex]\n\t\treturn a.currentexpression\n\t}\n\treturn nil\n}\n\nfunc (a *ast) getTokPrecedence() int {\n\tswitch a.currentexpression.expression {\n\tcase \"/\", \"%\", \"*\":\n\t\treturn 100\n\tcase \"\u003c\u003c\", \"\u003e\u003e\":\n\t\treturn 80\n\tcase \"+\", \"-\":\n\t\treturn 75\n\tcase \"\u003c\", \"\u003e\":\n\t\treturn 70\n\tcase \"\u0026\":\n\t\treturn 60\n\tcase \"^\":\n\t\treturn 50\n\tcase \"|\":\n\t\treturn 40\n\t}\n\treturn -1\n}\n\nfunc (a *ast) parseNumber() expressionNumber {\n\tf64, err := strconv.Atoi(a.currentexpression.expression)\n\tif err != nil {\n\t\ta.err = ufmt.Errorf(\"%v\\nwant '(' or '0-9' but get '%s'\\n%s\",\n\t\t\terr.Error(),\n\t\t\ta.currentexpression.expression,\n\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\treturn expressionNumber{}\n\t}\n\tn := expressionNumber{\n\t\tVal: f64,\n\t\tStr: a.currentexpression.expression,\n\t}\n\ta.getNextexpressionRaw()\n\treturn n\n}\n\nfunc (a *ast) parseVariable() expressionVariable {\n\tn := expressionVariable{\n\t\tVal: 0,\n\t\tStr: a.currentexpression.expression,\n\t}\n\ta.getNextexpressionRaw()\n\treturn n\n}\n\nfunc (a *ast) parsePrimary() expression {\n\tswitch a.currentexpression.Type {\n\tcase Variable:\n\t\treturn a.parseVariable()\n\tcase Number:\n\t\treturn a.parseNumber()\n\tcase Operator:\n\t\tif a.currentexpression.expression == \"(\" {\n\t\t\tt := a.getNextexpressionRaw()\n\t\t\tif t == nil {\n\t\t\t\ta.err = ufmt.Errorf(\"want '(' or '0-9' but get EOF\\n%s\",\n\t\t\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\te, _ := a.parseExpression()\n\t\t\tif e == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif a.currentexpression.expression != \")\" {\n\t\t\t\ta.err = ufmt.Errorf(\"want ')' but get %s\\n%s\",\n\t\t\t\t\ta.currentexpression.expression,\n\t\t\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\ta.getNextexpressionRaw()\n\t\t\treturn e\n\t\t} else if a.currentexpression.expression == \"-\" {\n\t\t\tif a.getNextexpressionRaw() == nil {\n\t\t\t\ta.err = ufmt.Errorf(\"want '0-9' but get '-'\\n%s\",\n\t\t\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tbin := expressionOperation{\n\t\t\t\tOp: \"-\",\n\t\t\t\tLhs: expressionNumber{},\n\t\t\t\tRhs: a.parsePrimary(),\n\t\t\t}\n\t\t\treturn bin\n\t\t} else {\n\t\t\treturn a.parseNumber()\n\t\t}\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc (a *ast) parseBinOpRHS(execPrec int, lhs expression) expression {\n\tfor {\n\t\ttokPrec := a.getTokPrecedence()\n\t\tif tokPrec \u003c execPrec {\n\t\t\treturn lhs\n\t\t}\n\t\tbinOp := a.currentexpression.expression\n\t\tif a.getNextexpressionRaw() == nil {\n\t\t\ta.err = ufmt.Errorf(\"want '(' or '0-9' but get EOF\\n%s\",\n\t\t\t\texpressionError(a.source, a.currentexpression.Offset))\n\t\t\treturn nil\n\t\t}\n\t\trhs := a.parsePrimary()\n\t\tif rhs == nil {\n\t\t\treturn nil\n\t\t}\n\t\tnextPrec := a.getTokPrecedence()\n\t\tif tokPrec \u003c nextPrec {\n\t\t\trhs = a.parseBinOpRHS(tokPrec+1, rhs)\n\t\t\tif rhs == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tlhs = expressionOperation{\n\t\t\tOp: binOp,\n\t\t\tLhs: lhs,\n\t\t\tRhs: rhs,\n\t\t}\n\t}\n}\n\nfunc lexer(s string) ([]*expressionRaw, error) {\n\tp := \u0026parser{\n\t\tInput: s,\n\t\terr: nil,\n\t\tch: s[0],\n\t}\n\ttoks := p.parse()\n\tif p.err != nil {\n\t\treturn nil, p.err\n\t}\n\treturn toks, nil\n}\n\nfunc (p *parser) parse() []*expressionRaw {\n\ttoks := make([]*expressionRaw, 0)\n\tfor {\n\t\ttok := p.nextTok()\n\t\tif tok == nil {\n\t\t\tbreak\n\t\t}\n\t\ttoks = append(toks, tok)\n\t}\n\treturn toks\n}\n\nfunc (p *parser) nextTok() *expressionRaw {\n\tif p.offset \u003e= len(p.Input) || p.err != nil {\n\t\treturn nil\n\t}\n\tvar err error\n\tfor p.isWhitespace(p.ch) \u0026\u0026 err == nil {\n\t\terr = p.nextCh()\n\t}\n\tstart := p.offset\n\tvar tok *expressionRaw\n\tswitch p.ch {\n\tcase\n\t\t'(',\n\t\t')',\n\t\t'+',\n\t\t'-',\n\t\t'*',\n\t\t'/',\n\t\t'^',\n\t\t'\u0026',\n\t\t'|',\n\t\t'%':\n\t\ttok = \u0026expressionRaw{\n\t\t\texpression: string(p.ch),\n\t\t\tType: Operator,\n\t\t}\n\t\ttok.Offset = start\n\t\terr = p.nextCh()\n\tcase '\u003e', '\u003c':\n\t\ttokS := string(p.ch)\n\t\tbb, be := p.nextChPeek()\n\t\tif be == nil \u0026\u0026 string(bb) == tokS {\n\t\t\ttokS += string(p.ch)\n\t\t}\n\t\ttok = \u0026expressionRaw{\n\t\t\texpression: tokS,\n\t\t\tType: Operator,\n\t\t}\n\t\ttok.Offset = start\n\t\tif len(tokS) \u003e 1 {\n\t\t\tp.nextCh()\n\t\t}\n\t\terr = p.nextCh()\n\tcase\n\t\t'0',\n\t\t'1',\n\t\t'2',\n\t\t'3',\n\t\t'4',\n\t\t'5',\n\t\t'6',\n\t\t'7',\n\t\t'8',\n\t\t'9':\n\t\tfor p.isDigitNum(p.ch) \u0026\u0026 p.nextCh() == nil {\n\t\t\tif (p.ch == '-' || p.ch == '+') \u0026\u0026 p.Input[p.offset-1] != 'e' {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\ttok = \u0026expressionRaw{\n\t\t\texpression: strings.ReplaceAll(p.Input[start:p.offset], \"_\", \"\"),\n\t\t\tType: Number,\n\t\t}\n\t\ttok.Offset = start\n\tdefault:\n\t\tif p.isChar(p.ch) {\n\t\t\ttok = \u0026expressionRaw{\n\t\t\t\texpression: string(p.ch),\n\t\t\t\tType: Variable,\n\t\t\t}\n\t\t\ttok.Offset = start\n\t\t\terr = p.nextCh()\n\t\t} else if p.ch != ' ' {\n\t\t\tp.err = ufmt.Errorf(\"symbol error: unknown '%v', pos [%v:]\\n%s\",\n\t\t\t\tstring(p.ch),\n\t\t\t\tstart,\n\t\t\t\texpressionError(p.Input, start))\n\t\t}\n\t}\n\treturn tok\n}\n\nfunc (p *parser) nextChPeek() (byte, error) {\n\toffset := p.offset + 1\n\tif offset \u003c len(p.Input) {\n\t\treturn p.Input[offset], nil\n\t}\n\treturn byte(0), errors.New(\"no byte\")\n}\n\nfunc (p *parser) nextCh() error {\n\tp.offset++\n\tif p.offset \u003c len(p.Input) {\n\t\tp.ch = p.Input[p.offset]\n\t\treturn nil\n\t}\n\treturn errors.New(\"EOF\")\n}\n\nfunc (p *parser) isWhitespace(c byte) bool {\n\treturn c == ' ' ||\n\t\tc == '\\t' ||\n\t\tc == '\\n' ||\n\t\tc == '\\v' ||\n\t\tc == '\\f' ||\n\t\tc == '\\r'\n}\n\nfunc (p *parser) isDigitNum(c byte) bool {\n\treturn '0' \u003c= c \u0026\u0026 c \u003c= '9' || c == '.' || c == '_' || c == 'e' || c == '-' || c == '+'\n}\n\nfunc (p *parser) isChar(c byte) bool {\n\treturn 'a' \u003c= c \u0026\u0026 c \u003c= 'z' || 'A' \u003c= c \u0026\u0026 c \u003c= 'Z'\n}\n\nfunc (p *parser) isWordChar(c byte) bool {\n\treturn p.isChar(c) || '0' \u003c= c \u0026\u0026 c \u003c= '9'\n}\n" @@ -2247,10 +2087,6 @@ "name": "errors.gno", "body": "package ownable\n\nimport \"errors\"\n\nvar (\n\tErrUnauthorized = errors.New(\"unauthorized; caller is not owner\")\n\tErrInvalidAddress = errors.New(\"new owner address is invalid\")\n)\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/ownable\n\nrequire (\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n)\n" - }, { "name": "ownable.gno", "body": "package ownable\n\nimport (\n\t\"std\"\n)\n\nconst OwnershipTransferEvent = \"OwnershipTransfer\"\n\n// Ownable is meant to be used as a top-level object to make your contract ownable OR\n// being embedded in a Gno object to manage per-object ownership.\ntype Ownable struct {\n\towner std.Address\n}\n\nfunc New() *Ownable {\n\treturn \u0026Ownable{\n\t\towner: std.PrevRealm().Addr(),\n\t}\n}\n\nfunc NewWithAddress(addr std.Address) *Ownable {\n\treturn \u0026Ownable{owner: addr}\n}\n\n// TransferOwnership transfers ownership of the Ownable struct to a new address\nfunc (o *Ownable) TransferOwnership(newOwner std.Address) error {\n\terr := o.CallerIsOwner()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !newOwner.IsValid() {\n\t\treturn ErrInvalidAddress\n\t}\n\n\tprevOwner := o.owner\n\to.owner = newOwner\n\tstd.Emit(\n\t\tOwnershipTransferEvent,\n\t\t\"from\", string(prevOwner),\n\t\t\"to\", string(newOwner),\n\t)\n\treturn nil\n}\n\n// DropOwnership removes the owner, effectively disabling any owner-related actions\n// Top-level usage: disables all only-owner actions/functions,\n// Embedded usage: behaves like a burn functionality, removing the owner from the struct\nfunc (o *Ownable) DropOwnership() error {\n\terr := o.CallerIsOwner()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tprevOwner := o.owner\n\to.owner = \"\"\n\n\tstd.Emit(\n\t\tOwnershipTransferEvent,\n\t\t\"from\", string(prevOwner),\n\t\t\"to\", \"\",\n\t)\n\n\treturn nil\n}\n\nfunc (o Ownable) Owner() std.Address {\n\treturn o.owner\n}\n\n// CallerIsOwner checks if the caller of the function is the Realm's owner\nfunc (o Ownable) CallerIsOwner() error {\n\tif std.PrevRealm().Addr() == o.owner {\n\t\treturn nil\n\t}\n\treturn ErrUnauthorized\n}\n\nfunc (o Ownable) AssertCallerIsOwner() {\n\tif std.PrevRealm().Addr() != o.owner {\n\t\tpanic(ErrUnauthorized)\n\t}\n}\n" @@ -2289,10 +2125,6 @@ "name": "README.md", "body": "# seqid\n\n```\npackage seqid // import \"gno.land/p/demo/seqid\"\n\nPackage seqid provides a simple way to have sequential IDs which will be ordered\ncorrectly when inserted in an AVL tree.\n\nSample usage:\n\n var id seqid.ID\n var users avl.Tree\n\n func NewUser() {\n \tusers.Set(id.Next().Binary(), \u0026User{ ... })\n }\n\nTYPES\n\ntype ID uint64\n An ID is a simple sequential ID generator.\n\nfunc FromBinary(b string) (ID, bool)\n FromBinary creates a new ID from the given string.\n\nfunc (i ID) Binary() string\n Binary returns a big-endian binary representation of the ID, suitable to be\n used as an AVL key.\n\nfunc (i *ID) Next() ID\n Next advances the ID i. It will panic if increasing ID would overflow.\n\nfunc (i *ID) TryNext() (ID, bool)\n TryNext increases i by 1 and returns its value. It returns true if\n successful, or false if the increment would result in an overflow.\n```\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/seqid\n\nrequire gno.land/p/demo/cford32 v0.0.0-latest\n" - }, { "name": "seqid.gno", "body": "// Package seqid provides a simple way to have sequential IDs which will be\n// ordered correctly when inserted in an AVL tree.\n//\n// Sample usage:\n//\n//\tvar id seqid.ID\n//\tvar users avl.Tree\n//\n//\tfunc NewUser() {\n//\t\tusers.Set(id.Next().String(), \u0026User{ ... })\n//\t}\npackage seqid\n\nimport (\n\t\"encoding/binary\"\n\n\t\"gno.land/p/demo/cford32\"\n)\n\n// An ID is a simple sequential ID generator.\ntype ID uint64\n\n// Next advances the ID i.\n// It will panic if increasing ID would overflow.\nfunc (i *ID) Next() ID {\n\tnext, ok := i.TryNext()\n\tif !ok {\n\t\tpanic(\"seqid: next ID overflows uint64\")\n\t}\n\treturn next\n}\n\nconst maxID ID = 1\u003c\u003c64 - 1\n\n// TryNext increases i by 1 and returns its value.\n// It returns true if successful, or false if the increment would result in\n// an overflow.\nfunc (i *ID) TryNext() (ID, bool) {\n\tif *i == maxID {\n\t\t// Addition will overflow.\n\t\treturn 0, false\n\t}\n\t*i++\n\treturn *i, true\n}\n\n// Binary returns a big-endian binary representation of the ID,\n// suitable to be used as an AVL key.\nfunc (i ID) Binary() string {\n\tbuf := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(buf, uint64(i))\n\treturn string(buf)\n}\n\n// String encodes i using cford32's compact encoding. For more information,\n// see the documentation for package [gno.land/p/demo/cford32].\n//\n// The result of String will be a 7-byte string for IDs [0,2^34), and a\n// 13-byte string for all values following that. All generated string IDs\n// follow the same lexicographic order as their number values; that is, for any\n// two IDs (x, y) such that x \u003c y, x.String() \u003c y.String().\n// As such, this string representation is suitable to be used as an AVL key.\nfunc (i ID) String() string {\n\treturn string(cford32.PutCompact(uint64(i)))\n}\n\n// FromBinary creates a new ID from the given string, expected to be a binary\n// big-endian encoding of an ID (such as that of [ID.Binary]).\n// The second return value is true if the conversion was successful.\nfunc FromBinary(b string) (ID, bool) {\n\tif len(b) != 8 {\n\t\treturn 0, false\n\t}\n\treturn ID(binary.BigEndian.Uint64([]byte(b))), true\n}\n\n// FromString creates a new ID from the given string, expected to be a string\n// representation using cford32, such as that returned by [ID.String].\n//\n// The encoding scheme used by cford32 allows the same ID to have many\n// different representations (though the one returned by [ID.String] is only\n// one, deterministic and safe to be used in AVL). The encoding scheme is\n// \"human-centric\" and is thus case insensitive, and maps some ambiguous\n// characters to be the same, ie. L = I = 1, O = 0. For this reason, when\n// parsing user input to retrieve a key (encoded as a string), always sanitize\n// it first using FromString, then run String(), instead of using the user's\n// input directly.\nfunc FromString(b string) (ID, error) {\n\tn, err := cford32.Uint64([]byte(b))\n\treturn ID(n), err\n}\n" @@ -2327,10 +2159,6 @@ "name": "memeland", "path": "gno.land/p/demo/memeland", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/memeland\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/p/demo/seqid v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "memeland.gno", "body": "package memeland\n\nimport (\n\t\"sort\"\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/p/demo/seqid\"\n)\n\nconst (\n\tDATE_CREATED = \"DATE_CREATED\"\n\tUPVOTES = \"UPVOTES\"\n)\n\ntype Post struct {\n\tID string\n\tData string\n\tAuthor std.Address\n\tTimestamp time.Time\n\tUpvoteTracker *avl.Tree // address \u003e struct{}{}\n}\n\ntype Memeland struct {\n\t*ownable.Ownable\n\tPosts []*Post\n\tMemeCounter seqid.ID\n}\n\nfunc NewMemeland() *Memeland {\n\treturn \u0026Memeland{\n\t\tOwnable: ownable.New(),\n\t\tPosts: make([]*Post, 0),\n\t}\n}\n\n// PostMeme - Adds a new post\nfunc (m *Memeland) PostMeme(data string, timestamp int64) string {\n\tif data == \"\" || timestamp \u003c= 0 {\n\t\tpanic(\"timestamp or data cannot be empty\")\n\t}\n\n\t// Generate ID\n\tid := m.MemeCounter.Next().String()\n\n\tnewPost := \u0026Post{\n\t\tID: id,\n\t\tData: data,\n\t\tAuthor: std.PrevRealm().Addr(),\n\t\tTimestamp: time.Unix(timestamp, 0),\n\t\tUpvoteTracker: avl.NewTree(),\n\t}\n\n\tm.Posts = append(m.Posts, newPost)\n\treturn id\n}\n\nfunc (m *Memeland) Upvote(id string) string {\n\tpost := m.getPost(id)\n\tif post == nil {\n\t\tpanic(\"post with specified ID does not exist\")\n\t}\n\n\tcaller := std.PrevRealm().Addr().String()\n\n\tif _, exists := post.UpvoteTracker.Get(caller); exists {\n\t\tpanic(\"user has already upvoted this post\")\n\t}\n\n\tpost.UpvoteTracker.Set(caller, struct{}{})\n\n\treturn \"upvote successful\"\n}\n\n// GetPostsInRange returns a JSON string of posts within the given timestamp range, supporting pagination\nfunc (m *Memeland) GetPostsInRange(startTimestamp, endTimestamp int64, page, pageSize int, sortBy string) string {\n\tif len(m.Posts) == 0 {\n\t\treturn \"[]\"\n\t}\n\n\tif page \u003c 1 {\n\t\tpanic(\"page number cannot be less than 1\")\n\t}\n\n\t// No empty pages\n\tif pageSize \u003c 1 {\n\t\tpanic(\"page size cannot be less than 1\")\n\t}\n\n\t// No pages larger than 10\n\tif pageSize \u003e 10 {\n\t\tpanic(\"page size cannot be larger than 10\")\n\t}\n\n\t// Need to pass in a sort parameter\n\tif sortBy == \"\" {\n\t\tpanic(\"sort order cannot be empty\")\n\t}\n\n\tvar filteredPosts []*Post\n\n\tstart := time.Unix(startTimestamp, 0)\n\tend := time.Unix(endTimestamp, 0)\n\n\t// Filtering posts\n\tfor _, p := range m.Posts {\n\t\tif !p.Timestamp.Before(start) \u0026\u0026 !p.Timestamp.After(end) {\n\t\t\tfilteredPosts = append(filteredPosts, p)\n\t\t}\n\t}\n\n\tswitch sortBy {\n\t// Sort by upvote descending\n\tcase UPVOTES:\n\t\tdateSorter := PostSorter{\n\t\t\tPosts: filteredPosts,\n\t\t\tLessF: func(i, j int) bool {\n\t\t\t\treturn filteredPosts[i].UpvoteTracker.Size() \u003e filteredPosts[j].UpvoteTracker.Size()\n\t\t\t},\n\t\t}\n\t\tsort.Sort(dateSorter)\n\tcase DATE_CREATED:\n\t\t// Sort by timestamp, beginning with newest\n\t\tdateSorter := PostSorter{\n\t\t\tPosts: filteredPosts,\n\t\t\tLessF: func(i, j int) bool {\n\t\t\t\treturn filteredPosts[i].Timestamp.After(filteredPosts[j].Timestamp)\n\t\t\t},\n\t\t}\n\t\tsort.Sort(dateSorter)\n\tdefault:\n\t\tpanic(\"sort order can only be \\\"UPVOTES\\\" or \\\"DATE_CREATED\\\"\")\n\t}\n\n\t// Pagination\n\tstartIndex := (page - 1) * pageSize\n\tendIndex := startIndex + pageSize\n\n\t// If page does not contain any posts\n\tif startIndex \u003e= len(filteredPosts) {\n\t\treturn \"[]\"\n\t}\n\n\t// If page contains fewer posts than the page size\n\tif endIndex \u003e len(filteredPosts) {\n\t\tendIndex = len(filteredPosts)\n\t}\n\n\t// Return JSON representation of paginated and sorted posts\n\treturn PostsToJSONString(filteredPosts[startIndex:endIndex])\n}\n\n// RemovePost allows the owner to remove a post with a specific ID\nfunc (m *Memeland) RemovePost(id string) string {\n\tif id == \"\" {\n\t\tpanic(\"id cannot be empty\")\n\t}\n\n\tif err := m.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor i, post := range m.Posts {\n\t\tif post.ID == id {\n\t\t\tm.Posts = append(m.Posts[:i], m.Posts[i+1:]...)\n\t\t\treturn id\n\t\t}\n\t}\n\n\tpanic(\"post with specified id does not exist\")\n}\n\n// PostsToJSONString converts a slice of Post structs into a JSON string\nfunc PostsToJSONString(posts []*Post) string {\n\tvar sb strings.Builder\n\tsb.WriteString(\"[\")\n\n\tfor i, post := range posts {\n\t\tif i \u003e 0 {\n\t\t\tsb.WriteString(\",\")\n\t\t}\n\n\t\tsb.WriteString(PostToJSONString(post))\n\t}\n\tsb.WriteString(\"]\")\n\n\treturn sb.String()\n}\n\n// PostToJSONString returns a Post formatted as a JSON string\nfunc PostToJSONString(post *Post) string {\n\tvar sb strings.Builder\n\n\tsb.WriteString(\"{\")\n\tsb.WriteString(`\"id\":\"` + post.ID + `\",`)\n\tsb.WriteString(`\"data\":\"` + escapeString(post.Data) + `\",`)\n\tsb.WriteString(`\"author\":\"` + escapeString(post.Author.String()) + `\",`)\n\tsb.WriteString(`\"timestamp\":\"` + strconv.Itoa(int(post.Timestamp.Unix())) + `\",`)\n\tsb.WriteString(`\"upvotes\":` + strconv.Itoa(post.UpvoteTracker.Size()))\n\tsb.WriteString(\"}\")\n\n\treturn sb.String()\n}\n\n// escapeString escapes quotes in a string for JSON compatibility.\nfunc escapeString(s string) string {\n\treturn strings.ReplaceAll(s, `\"`, `\\\"`)\n}\n\nfunc (m *Memeland) getPost(id string) *Post {\n\tfor _, p := range m.Posts {\n\t\tif p.ID == id {\n\t\t\treturn p\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// PostSorter is a flexible sorter for the *Post slice\ntype PostSorter struct {\n\tPosts []*Post\n\tLessF func(i, j int) bool\n}\n\nfunc (p PostSorter) Len() int {\n\treturn len(p.Posts)\n}\n\nfunc (p PostSorter) Swap(i, j int) {\n\tp.Posts[i], p.Posts[j] = p.Posts[j], p.Posts[i]\n}\n\nfunc (p PostSorter) Less(i, j int) bool {\n\treturn p.LessF(i, j)\n}\n" @@ -2369,10 +2197,6 @@ "name": "README.md", "body": "# p/demo/merkle\n\nThis package implement a merkle tree that is complient with [merkletreejs](https://github.com/merkletreejs/merkletreejs)\n\n## [merkletreejs](https://github.com/merkletreejs/merkletreejs)\n\n```javascript\nconst { MerkleTree } = require(\"merkletreejs\");\nconst SHA256 = require(\"crypto-js/sha256\");\n\nlet leaves = [];\nfor (let i = 0; i \u003c 10; i++) {\n leaves.push(SHA256(`node_${i}`));\n}\n\nconst tree = new MerkleTree(leaves, SHA256);\nconst root = tree.getRoot().toString(\"hex\");\n\nconsole.log(root); // cd8a40502b0b92bf58e7432a5abb2d8b60121cf2b7966d6ebaf103f907a1bc21\n```\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/merkle\n" - }, { "name": "merkle.gno", "body": "package merkle\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"errors\"\n)\n\ntype Hashable interface {\n\tBytes() []byte\n}\n\ntype nodes []Node\n\ntype Node struct {\n\thash []byte\n\n\tposition uint8\n}\n\nfunc (n Node) Hash() string {\n\treturn hex.EncodeToString(n.hash[:])\n}\n\ntype Tree struct {\n\tlayers []nodes\n}\n\n// Root return the merkle root of the tree\nfunc (t *Tree) Root() string {\n\tfor _, l := range t.layers {\n\t\tif len(l) == 1 {\n\t\t\treturn l[0].Hash()\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// NewTree create a new Merkle Tree\nfunc NewTree(data []Hashable) *Tree {\n\ttree := \u0026Tree{}\n\n\tleaves := make([]Node, len(data))\n\n\tfor i, d := range data {\n\t\thash := sha256.Sum256(d.Bytes())\n\t\tleaves[i] = Node{hash: hash[:]}\n\t}\n\n\ttree.layers = []nodes{nodes(leaves)}\n\n\tvar buff bytes.Buffer\n\tfor len(leaves) \u003e 1 {\n\t\tlevel := make([]Node, 0, len(leaves)/2+1)\n\t\tfor i := 0; i \u003c len(leaves); i += 2 {\n\t\t\tbuff.Reset()\n\n\t\t\tif i \u003c len(leaves)-1 {\n\t\t\t\tbuff.Write(leaves[i].hash)\n\t\t\t\tbuff.Write(leaves[i+1].hash)\n\t\t\t\thash := sha256.Sum256(buff.Bytes())\n\t\t\t\tlevel = append(level, Node{\n\t\t\t\t\thash: hash[:],\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlevel = append(level, leaves[i])\n\t\t\t}\n\t\t}\n\t\tleaves = level\n\t\ttree.layers = append(tree.layers, level)\n\t}\n\treturn tree\n}\n\n// Proof return a MerkleProof\nfunc (t *Tree) Proof(data Hashable) ([]Node, error) {\n\ttargetHash := sha256.Sum256(data.Bytes())\n\ttargetIndex := -1\n\n\tfor i, layer := range t.layers[0] {\n\t\tif bytes.Equal(targetHash[:], layer.hash) {\n\t\t\ttargetIndex = i\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif targetIndex == -1 {\n\t\treturn nil, errors.New(\"target not found\")\n\t}\n\n\tproofs := make([]Node, 0, len(t.layers))\n\n\tfor _, layer := range t.layers {\n\t\tvar pairIndex int\n\n\t\tif targetIndex%2 == 0 {\n\t\t\tpairIndex = targetIndex + 1\n\t\t} else {\n\t\t\tpairIndex = targetIndex - 1\n\t\t}\n\t\tif pairIndex \u003c len(layer) {\n\t\t\tproofs = append(proofs, Node{\n\t\t\t\thash: layer[pairIndex].hash,\n\t\t\t\tposition: uint8(targetIndex) % 2,\n\t\t\t})\n\t\t}\n\t\ttargetIndex /= 2\n\t}\n\treturn proofs, nil\n}\n\n// Verify if a merkle proof is valid\nfunc (t *Tree) Verify(leaf Hashable, proofs []Node) bool {\n\treturn Verify(t.Root(), leaf, proofs)\n}\n\n// Verify if a merkle proof is valid\nfunc Verify(root string, leaf Hashable, proofs []Node) bool {\n\thash := sha256.Sum256(leaf.Bytes())\n\n\tfor i := 0; i \u003c len(proofs); i += 1 {\n\t\tvar h []byte\n\t\tif proofs[i].position == 0 {\n\t\t\th = append(hash[:], proofs[i].hash...)\n\t\t} else {\n\t\t\th = append(proofs[i].hash, hash[:]...)\n\t\t}\n\t\thash = sha256.Sum256(h)\n\t}\n\treturn hex.EncodeToString(hash[:]) == root\n}\n" @@ -2407,10 +2231,6 @@ "name": "microblog", "path": "gno.land/p/demo/microblog", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/microblog\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "microblog.gno", "body": "package microblog\n\nimport (\n\t\"errors\"\n\t\"sort\"\n\t\"std\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar (\n\tErrNotFound = errors.New(\"not found\")\n\tStatusNotFound = \"404\"\n)\n\ntype Microblog struct {\n\tTitle string\n\tPrefix string // i.e. r/gnoland/blog:\n\tPages avl.Tree // author (string) -\u003e Page\n}\n\nfunc NewMicroblog(title string, prefix string) (m *Microblog) {\n\treturn \u0026Microblog{\n\t\tTitle: title,\n\t\tPrefix: prefix,\n\t\tPages: avl.Tree{},\n\t}\n}\n\nfunc (m *Microblog) GetPages() []*Page {\n\tvar (\n\t\tpages = make([]*Page, m.Pages.Size())\n\t\tindex = 0\n\t)\n\n\tm.Pages.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tpages[index] = value.(*Page)\n\t\tindex++\n\t\treturn false\n\t})\n\n\tsort.Sort(byLastPosted(pages))\n\n\treturn pages\n}\n\nfunc (m *Microblog) NewPost(text string) error {\n\tauthor := std.GetOrigCaller()\n\t_, found := m.Pages.Get(author.String())\n\tif !found {\n\t\t// make a new page for the new author\n\t\tm.Pages.Set(author.String(), \u0026Page{\n\t\t\tAuthor: author,\n\t\t\tCreatedAt: time.Now(),\n\t\t})\n\t}\n\n\tpage, err := m.GetPage(author.String())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn page.NewPost(text)\n}\n\nfunc (m *Microblog) GetPage(author string) (*Page, error) {\n\tsilo, found := m.Pages.Get(author)\n\tif !found {\n\t\treturn nil, ErrNotFound\n\t}\n\treturn silo.(*Page), nil\n}\n\ntype Page struct {\n\tID int\n\tAuthor std.Address\n\tCreatedAt time.Time\n\tLastPosted time.Time\n\tPosts avl.Tree // time -\u003e Post\n}\n\n// byLastPosted implements sort.Interface for []Page based on\n// the LastPosted field.\ntype byLastPosted []*Page\n\nfunc (a byLastPosted) Len() int { return len(a) }\nfunc (a byLastPosted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }\nfunc (a byLastPosted) Less(i, j int) bool { return a[i].LastPosted.After(a[j].LastPosted) }\n\nfunc (p *Page) NewPost(text string) error {\n\tnow := time.Now()\n\tp.LastPosted = now\n\tp.Posts.Set(ufmt.Sprintf(\"%s%d\", now.Format(time.RFC3339), p.Posts.Size()), \u0026Post{\n\t\tID: p.Posts.Size(),\n\t\tText: text,\n\t\tCreatedAt: now,\n\t})\n\treturn nil\n}\n\nfunc (p *Page) GetPosts() []*Post {\n\tposts := make([]*Post, p.Posts.Size())\n\ti := 0\n\tp.Posts.ReverseIterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tpostParsed := value.(*Post)\n\t\tposts[i] = postParsed\n\t\ti++\n\t\treturn false\n\t})\n\treturn posts\n}\n\n// Post lists the specific update\ntype Post struct {\n\tID int\n\tCreatedAt time.Time\n\tText string\n}\n\nfunc (p *Post) String() string {\n\treturn \"\u003e \" + strings.ReplaceAll(p.Text, \"\\n\", \"\\n\u003e\\n\u003e\") + \"\\n\u003e\\n\u003e *\" + p.CreatedAt.Format(time.RFC1123) + \"*\"\n}\n" @@ -2441,10 +2261,6 @@ "name": "pausable", "path": "gno.land/p/demo/pausable", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/pausable\n\nrequire gno.land/p/demo/ownable v0.0.0-latest\n" - }, { "name": "pausable.gno", "body": "package pausable\n\nimport \"gno.land/p/demo/ownable\"\n\ntype Pausable struct {\n\t*ownable.Ownable\n\tpaused bool\n}\n\n// New returns a new Pausable struct with non-paused state as default\nfunc New() *Pausable {\n\treturn \u0026Pausable{\n\t\tOwnable: ownable.New(),\n\t\tpaused: false,\n\t}\n}\n\n// NewFromOwnable is the same as New, but with a pre-existing top-level ownable\nfunc NewFromOwnable(ownable *ownable.Ownable) *Pausable {\n\treturn \u0026Pausable{\n\t\tOwnable: ownable,\n\t\tpaused: false,\n\t}\n}\n\n// IsPaused checks if Pausable is paused\nfunc (p Pausable) IsPaused() bool {\n\treturn p.paused\n}\n\n// Pause sets the state of Pausable to true, meaning all pausable functions are paused\nfunc (p *Pausable) Pause() error {\n\tif err := p.CallerIsOwner(); err != nil {\n\t\treturn err\n\t}\n\n\tp.paused = true\n\treturn nil\n}\n\n// Unpause sets the state of Pausable to false, meaning all pausable functions are resumed\nfunc (p *Pausable) Unpause() error {\n\tif err := p.CallerIsOwner(); err != nil {\n\t\treturn err\n\t}\n\n\tp.paused = false\n\treturn nil\n}\n" @@ -2483,10 +2299,6 @@ "name": "changelog.gno", "body": "package releases\n\ntype changelog struct {\n\tname string\n\treleases []release\n}\n\nfunc NewChangelog(name string) *changelog {\n\treturn \u0026changelog{\n\t\tname: name,\n\t\treleases: make([]release, 0),\n\t}\n}\n\nfunc (c *changelog) NewRelease(version, url, notes string) {\n\tif latest := c.Latest(); latest != nil {\n\t\tlatest.isLatest = false\n\t}\n\n\trelease := release{\n\t\t// manual\n\t\tversion: version,\n\t\turl: url,\n\t\tnotes: notes,\n\n\t\t// internal\n\t\tchangelog: c,\n\t\tisLatest: true,\n\t}\n\n\tc.releases = append(c.releases, release)\n}\n\nfunc (c *changelog) Render(path string) string {\n\tif path == \"\" {\n\t\toutput := \"# \" + c.name + \"\\n\\n\"\n\t\tmax := len(c.releases) - 1\n\t\tmin := 0\n\t\tif max-min \u003e 10 {\n\t\t\tmin = max - 10\n\t\t}\n\t\tfor i := max; i \u003e= min; i-- {\n\t\t\trelease := c.releases[i]\n\t\t\toutput += release.Render()\n\t\t}\n\t\treturn output\n\t}\n\n\trelease := c.ByVersion(path)\n\tif release != nil {\n\t\treturn release.Render()\n\t}\n\n\treturn \"no such release\"\n}\n\nfunc (c *changelog) Latest() *release {\n\tif len(c.releases) \u003e 0 {\n\t\tpos := len(c.releases) - 1\n\t\treturn \u0026c.releases[pos]\n\t}\n\treturn nil\n}\n\nfunc (c *changelog) ByVersion(version string) *release {\n\tfor _, release := range c.releases {\n\t\tif release.version == version {\n\t\t\treturn \u0026release\n\t\t}\n\t}\n\treturn nil\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/releases\n" - }, { "name": "release.gno", "body": "package releases\n\ntype release struct {\n\t// manual\n\tversion string\n\turl string\n\tnotes string\n\n\t// internal\n\tisLatest bool\n\tchangelog *changelog\n}\n\nfunc (r *release) URL() string { return r.url }\nfunc (r *release) Version() string { return r.version }\nfunc (r *release) Notes() string { return r.notes }\nfunc (r *release) IsLatest() bool { return r.isLatest }\n\nfunc (r *release) Title() string {\n\toutput := r.changelog.name + \" \" + r.version\n\tif r.isLatest {\n\t\toutput += \" (latest)\"\n\t}\n\treturn output\n}\n\nfunc (r *release) Link() string {\n\treturn \"[\" + r.Title() + \"](\" + r.url + \")\"\n}\n\nfunc (r *release) Render() string {\n\toutput := \"\"\n\toutput += \"## \" + r.Link() + \"\\n\\n\"\n\tif r.notes != \"\" {\n\t\toutput += r.notes + \"\\n\\n\"\n\t}\n\treturn output\n}\n" @@ -2517,10 +2329,6 @@ "name": "stack", "path": "gno.land/p/demo/stack", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/stack\n" - }, { "name": "stack.gno", "body": "package stack\n\ntype Stack struct {\n\ttop *node\n\tlength int\n}\n\ntype node struct {\n\tvalue interface{}\n\tprev *node\n}\n\nfunc New() *Stack {\n\treturn \u0026Stack{nil, 0}\n}\n\nfunc (s *Stack) Len() int {\n\treturn s.length\n}\n\nfunc (s *Stack) Top() interface{} {\n\tif s.length == 0 {\n\t\treturn nil\n\t}\n\treturn s.top.value\n}\n\nfunc (s *Stack) Pop() interface{} {\n\tif s.length == 0 {\n\t\treturn nil\n\t}\n\n\tnode := s.top\n\ts.top = node.prev\n\ts.length -= 1\n\treturn node.value\n}\n\nfunc (s *Stack) Push(value interface{}) {\n\tnode := \u0026node{value, s.top}\n\ts.top = node\n\ts.length += 1\n}\n" @@ -2559,10 +2367,6 @@ "name": "doc.gno", "body": "/*\nPackage svg is a minimalist SVG generation library for Gno.\n\nThe svg package provides a simple and lightweight solution for programmatically generating SVG (Scalable Vector Graphics) markup in Gno. It allows you to create basic shapes like rectangles and circles, and output the generated SVG to a\n\nExample:\n\n\timport \"gno.land/p/demo/svg\"\"\n\n\tfunc Foo() string {\n\t canvas := svg.Canvas{Width: 200, Height: 200}\n\t canvas.DrawRectangle(50, 50, 100, 100, \"red\")\n\t canvas.DrawCircle(100, 100, 50, \"blue\")\n\t return canvas.String()\n\t}\n*/\npackage svg // import \"gno.land/p/demo/svg\"\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/svg\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" - }, { "name": "svg.gno", "body": "package svg\n\nimport \"gno.land/p/demo/ufmt\"\n\ntype Canvas struct {\n\tWidth int\n\tHeight int\n\tElems []Elem\n}\n\ntype Elem interface{ String() string }\n\nfunc (c Canvas) String() string {\n\toutput := \"\"\n\toutput += ufmt.Sprintf(`\u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"%d\" height=\"%d\"\u003e`, c.Width, c.Height)\n\tfor _, elem := range c.Elems {\n\t\toutput += elem.String()\n\t}\n\toutput += \"\u003c/svg\u003e\"\n\treturn output\n}\n\nfunc (c *Canvas) Append(elem Elem) {\n\tc.Elems = append(c.Elems, elem)\n}\n\ntype Circle struct {\n\tCX int // center X\n\tCY int // center Y\n\tR int // radius\n\tFill string\n}\n\nfunc (c Circle) String() string {\n\treturn ufmt.Sprintf(`\u003ccircle cx=\"%d\" cy=\"%d\" r=\"%d\" fill=\"%s\" /\u003e`, c.CX, c.CY, c.R, c.Fill)\n}\n\nfunc (c *Canvas) DrawCircle(cx, cy, r int, fill string) {\n\tc.Append(Circle{\n\t\tCX: cx,\n\t\tCY: cy,\n\t\tR: r,\n\t\tFill: fill,\n\t})\n}\n\ntype Rectangle struct {\n\tX, Y, Width, Height int\n\tFill string\n}\n\nfunc (c Rectangle) String() string {\n\treturn ufmt.Sprintf(`\u003crect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"%s\" /\u003e`, c.X, c.Y, c.Width, c.Height, c.Fill)\n}\n\nfunc (c *Canvas) DrawRectangle(x, y, width, height int, fill string) {\n\tc.Append(Rectangle{\n\t\tX: x,\n\t\tY: y,\n\t\tWidth: width,\n\t\tHeight: height,\n\t\tFill: fill,\n\t})\n}\n\ntype Text struct {\n\tX, Y int\n\tText, Fill string\n}\n\nfunc (c Text) String() string {\n\treturn ufmt.Sprintf(`\u003ctext x=\"%d\" y=\"%d\" fill=\"%s\"\u003e%s\u003c/text\u003e`, c.X, c.Y, c.Fill, c.Text)\n}\n\nfunc (c *Canvas) DrawText(x, y int, text, fill string) {\n\tc.Append(Text{\n\t\tX: x,\n\t\tY: y,\n\t\tText: text,\n\t\tFill: fill,\n\t})\n}\n" @@ -2601,10 +2405,6 @@ "name": "tamagotchi", "path": "gno.land/p/demo/tamagotchi", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/tamagotchi\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" - }, { "name": "tamagotchi.gno", "body": "package tamagotchi\n\nimport (\n\t\"time\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// Tamagotchi structure\ntype Tamagotchi struct {\n\tname string\n\thunger int\n\thappiness int\n\thealth int\n\tage int\n\tmaxAge int\n\tsleepy int\n\tcreated time.Time\n\tlastUpdated time.Time\n}\n\nfunc New(name string) *Tamagotchi {\n\tnow := time.Now()\n\treturn \u0026Tamagotchi{\n\t\tname: name,\n\t\thunger: 50,\n\t\thappiness: 50,\n\t\thealth: 50,\n\t\tmaxAge: 100,\n\t\tlastUpdated: now,\n\t\tcreated: now,\n\t}\n}\n\nfunc (t *Tamagotchi) Name() string {\n\tt.update()\n\treturn t.name\n}\n\nfunc (t *Tamagotchi) Hunger() int {\n\tt.update()\n\treturn t.hunger\n}\n\nfunc (t *Tamagotchi) Happiness() int {\n\tt.update()\n\treturn t.happiness\n}\n\nfunc (t *Tamagotchi) Health() int {\n\tt.update()\n\treturn t.health\n}\n\nfunc (t *Tamagotchi) Age() int {\n\tt.update()\n\treturn t.age\n}\n\nfunc (t *Tamagotchi) Sleepy() int {\n\tt.update()\n\treturn t.sleepy\n}\n\n// Feed method for Tamagotchi\nfunc (t *Tamagotchi) Feed() {\n\tt.update()\n\tif t.dead() {\n\t\treturn\n\t}\n\tt.hunger = bound(t.hunger-10, 0, 100)\n}\n\n// Play method for Tamagotchi\nfunc (t *Tamagotchi) Play() {\n\tt.update()\n\tif t.dead() {\n\t\treturn\n\t}\n\tt.happiness = bound(t.happiness+10, 0, 100)\n}\n\n// Heal method for Tamagotchi\nfunc (t *Tamagotchi) Heal() {\n\tt.update()\n\n\tif t.dead() {\n\t\treturn\n\t}\n\tt.health = bound(t.health+10, 0, 100)\n}\n\nfunc (t Tamagotchi) dead() bool { return t.health == 0 }\n\n// Update applies changes based on the duration since the last update\nfunc (t *Tamagotchi) update() {\n\tif t.dead() {\n\t\treturn\n\t}\n\n\tnow := time.Now()\n\tif t.lastUpdated == now {\n\t\treturn\n\t}\n\n\tduration := now.Sub(t.lastUpdated)\n\telapsedMins := int(duration.Minutes())\n\n\tt.hunger = bound(t.hunger+elapsedMins, 0, 100)\n\tt.happiness = bound(t.happiness-elapsedMins, 0, 100)\n\tt.health = bound(t.health-elapsedMins, 0, 100)\n\tt.sleepy = bound(t.sleepy+elapsedMins, 0, 100)\n\n\t// age is hours since created\n\tt.age = int(now.Sub(t.created).Hours())\n\tif t.age \u003e t.maxAge {\n\t\tt.age = t.maxAge\n\t\tt.health = 0\n\t}\n\tif t.health == 0 {\n\t\tt.sleepy = 0\n\t\tt.happiness = 0\n\t\tt.hunger = 0\n\t}\n\n\tt.lastUpdated = now\n}\n\n// Face returns an ASCII art representation of the Tamagotchi's current state\nfunc (t *Tamagotchi) Face() string {\n\tt.update()\n\treturn t.face()\n}\n\nfunc (t *Tamagotchi) face() string {\n\tswitch {\n\tcase t.health == 0:\n\t\treturn \"😵\" // dead face\n\tcase t.health \u003c 30:\n\t\treturn \"😷\" // sick face\n\tcase t.happiness \u003c 30:\n\t\treturn \"😢\" // sad face\n\tcase t.hunger \u003e 70:\n\t\treturn \"😫\" // hungry face\n\tcase t.sleepy \u003e 70:\n\t\treturn \"😴\" // sleepy face\n\tdefault:\n\t\treturn \"😃\" // happy face\n\t}\n}\n\n// Markdown method for Tamagotchi\nfunc (t *Tamagotchi) Markdown() string {\n\tt.update()\n\treturn ufmt.Sprintf(`# %s %s\n\n* age: %d\n* hunger: %d\n* happiness: %d\n* health: %d\n* sleepy: %d`,\n\t\tt.name, t.Face(),\n\t\tt.age, t.hunger, t.happiness, t.health, t.sleepy,\n\t)\n}\n\nfunc bound(n, min, max int) int {\n\tif n \u003c min {\n\t\treturn min\n\t}\n\tif n \u003e max {\n\t\treturn max\n\t}\n\treturn n\n}\n" @@ -2639,10 +2439,6 @@ "name": "subtests", "path": "gno.land/p/demo/tests/subtests", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/tests/subtests\n\n// TODO: this file should not exist.\n// This is a temporary workaround. Until https://github.com/gnolang/gno/issues/852\n" - }, { "name": "subtests.gno", "body": "package subtests\n\nimport (\n\t\"std\"\n)\n\nfunc GetCurrentRealm() std.Realm {\n\treturn std.CurrentRealm()\n}\n\nfunc GetPrevRealm() std.Realm {\n\treturn std.PrevRealm()\n}\n\nfunc Exec(fn func()) {\n\tfn()\n}\n" @@ -2673,10 +2469,6 @@ "name": "subtests", "path": "gno.land/r/demo/tests/subtests", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/tests/subtests\n\n// TODO: this file should not exist.\n// This is a temporary workaround. Until https://github.com/gnolang/gno/issues/852\n" - }, { "name": "subtests.gno", "body": "package subtests\n\nimport (\n\t\"std\"\n)\n\nfunc GetCurrentRealm() std.Realm {\n\treturn std.CurrentRealm()\n}\n\nfunc GetPrevRealm() std.Realm {\n\treturn std.PrevRealm()\n}\n\nfunc Exec(fn func()) {\n\tfn()\n}\n\nfunc CallAssertOriginCall() {\n\tstd.AssertOriginCall()\n}\n\nfunc CallIsOriginCall() bool {\n\treturn std.IsOriginCall()\n}\n" @@ -2711,10 +2503,6 @@ "name": "README.md", "body": "Modules here are only useful for file realm tests.\nThey can be safely ignored for other purposes.\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/tests\n\nrequire gno.land/r/demo/tests/subtests v0.0.0-latest\n" - }, { "name": "interfaces.gno", "body": "package tests\n\nimport (\n\t\"strconv\"\n)\n\ntype Stringer interface {\n\tString() string\n}\n\nvar stringers []Stringer\n\nfunc AddStringer(str Stringer) {\n\t// NOTE: this is ridiculous, a slice that will become too long\n\t// eventually. Don't do this in production programs; use\n\t// gno.land/p/demo/avl or similar structures.\n\tstringers = append(stringers, str)\n}\n\nfunc Render(path string) string {\n\tres := \"\"\n\t// NOTE: like the function above, this function too will eventually\n\t// become too expensive to call.\n\tfor i, stringer := range stringers {\n\t\tres += strconv.Itoa(i) + \": \" + stringer.String() + \"\\n\"\n\t}\n\treturn res\n}\n" @@ -2773,10 +2561,6 @@ "name": "README.md", "body": "Modules here are only useful for file realm tests.\nThey can be safely ignored for other purposes.\n" }, - { - "name": "gno.mod", - "body": "module gno.land/p/demo/tests\n\nrequire (\n\tgno.land/p/demo/tests/subtests v0.0.0-latest\n\tgno.land/r/demo/tests v0.0.0-latest\n)\n" - }, { "name": "tests.gno", "body": "package tests\n\nimport (\n\t\"std\"\n\n\tpsubtests \"gno.land/p/demo/tests/subtests\"\n\t\"gno.land/r/demo/tests\"\n\trtests \"gno.land/r/demo/tests\"\n)\n\nconst World = \"world\"\n\n// IncCounter demonstrates that it's possible to call a realm function from\n// a package. So a package can potentially write into the store, by calling\n// an other realm.\nfunc IncCounter() {\n\ttests.IncCounter()\n}\n\nfunc CurrentRealmPath() string {\n\treturn std.CurrentRealm().PkgPath()\n}\n\n//----------------------------------------\n// cross realm test vars\n\ntype TestRealmObject2 struct {\n\tField string\n}\n\nfunc (o2 *TestRealmObject2) Modify() {\n\to2.Field = \"modified\"\n}\n\nvar (\n\tsomevalue1 TestRealmObject2\n\tSomeValue2 TestRealmObject2\n\tSomeValue3 *TestRealmObject2\n)\n\nfunc init() {\n\tsomevalue1 = TestRealmObject2{Field: \"init\"}\n\tSomeValue2 = TestRealmObject2{Field: \"init\"}\n\tSomeValue3 = \u0026TestRealmObject2{Field: \"init\"}\n}\n\nfunc ModifyTestRealmObject2a() {\n\tsomevalue1.Field = \"modified\"\n}\n\nfunc ModifyTestRealmObject2b() {\n\tSomeValue2.Field = \"modified\"\n}\n\nfunc ModifyTestRealmObject2c() {\n\tSomeValue3.Field = \"modified\"\n}\n\nfunc GetPrevRealm() std.Realm {\n\treturn std.PrevRealm()\n}\n\nfunc GetPSubtestsPrevRealm() std.Realm {\n\treturn psubtests.GetPrevRealm()\n}\n\nfunc GetRTestsGetPrevRealm() std.Realm {\n\treturn rtests.GetPrevRealm()\n}\n\n// Warning: unsafe pattern.\nfunc Exec(fn func()) {\n\tfn()\n}\n" @@ -2815,10 +2599,6 @@ "name": "p_crossrealm", "path": "gno.land/p/demo/tests/p_crossrealm", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/tests/p_crossrealm\n" - }, { "name": "p_crossrealm.gno", "body": "package p_crossrealm\n\ntype Stringer interface {\n\tString() string\n}\n\ntype Container struct {\n\tA int\n\tB Stringer\n}\n\nfunc (c *Container) Touch() *Container {\n\tc.A += 1\n\treturn c\n}\n\nfunc (c *Container) Print() {\n\tprintln(\"A:\", c.A)\n\tif c.B == nil {\n\t\tprintln(\"B: undefined\")\n\t} else {\n\t\tprintln(\"B:\", c.B.String())\n\t}\n}\n" @@ -2849,10 +2629,6 @@ "name": "todolist", "path": "gno.land/p/demo/todolist", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/todolist\n\nrequire gno.land/p/demo/avl v0.0.0-latest\n" - }, { "name": "todolist.gno", "body": "package todolist\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\ntype TodoList struct {\n\tTitle string\n\tTasks *avl.Tree\n\tOwner std.Address\n}\n\ntype Task struct {\n\tTitle string\n\tDone bool\n}\n\nfunc NewTodoList(title string) *TodoList {\n\treturn \u0026TodoList{\n\t\tTitle: title,\n\t\tTasks: avl.NewTree(),\n\t\tOwner: std.GetOrigCaller(),\n\t}\n}\n\nfunc NewTask(title string) *Task {\n\treturn \u0026Task{\n\t\tTitle: title,\n\t\tDone: false,\n\t}\n}\n\nfunc (tl *TodoList) AddTask(id int, task *Task) {\n\ttl.Tasks.Set(strconv.Itoa(id), task)\n}\n\nfunc ToggleTaskStatus(task *Task) {\n\ttask.Done = !task.Done\n}\n\nfunc (tl *TodoList) RemoveTask(taskId string) {\n\ttl.Tasks.Remove(taskId)\n}\n\nfunc (tl *TodoList) GetTasks() []*Task {\n\ttasks := make([]*Task, 0, tl.Tasks.Size())\n\ttl.Tasks.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\ttasks = append(tasks, value.(*Task))\n\t\treturn false\n\t})\n\treturn tasks\n}\n\nfunc (tl *TodoList) GetTodolistOwner() std.Address {\n\treturn tl.Owner\n}\n\nfunc (tl *TodoList) GetTodolistTitle() string {\n\treturn tl.Title\n}\n" @@ -2887,10 +2663,6 @@ "name": "ui", "path": "gno.land/p/demo/ui", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/ui\n" - }, { "name": "ui.gno", "body": "package ui\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype DOM struct {\n\t// metadata\n\tPrefix string\n\tTitle string\n\tWithComments bool\n\tClasses []string\n\n\t// elements\n\tHeader Element\n\tBody Element\n\tFooter Element\n}\n\nfunc (dom DOM) String() string {\n\tclasses := strings.Join(dom.Classes, \" \")\n\n\toutput := \"\"\n\n\tif classes != \"\" {\n\t\toutput += \"\u003cmain class='\" + classes + \"'\u003e\" + \"\\n\\n\"\n\t}\n\n\tif dom.Title != \"\" {\n\t\toutput += H1(dom.Title).String(dom) + \"\\n\"\n\t}\n\n\tif header := dom.Header.String(dom); header != \"\" {\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- header --\u003e\"\n\t\t}\n\t\toutput += header + \"\\n\"\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- /header --\u003e\"\n\t\t}\n\t}\n\n\tif body := dom.Body.String(dom); body != \"\" {\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- body --\u003e\"\n\t\t}\n\t\toutput += body + \"\\n\"\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- /body --\u003e\"\n\t\t}\n\t}\n\n\tif footer := dom.Footer.String(dom); footer != \"\" {\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- footer --\u003e\"\n\t\t}\n\t\toutput += footer + \"\\n\"\n\t\tif dom.WithComments {\n\t\t\toutput += \"\u003c!-- /footer --\u003e\"\n\t\t}\n\t}\n\n\tif classes != \"\" {\n\t\toutput += \"\u003c/main\u003e\"\n\t}\n\n\t// TODO: cleanup double new-lines.\n\n\treturn output\n}\n\ntype Jumbotron []DomStringer\n\nfunc (j Jumbotron) String(dom DOM) string {\n\toutput := `\u003cdiv class=\"jumbotron\"\u003e` + \"\\n\\n\"\n\tfor _, elem := range j {\n\t\toutput += elem.String(dom) + \"\\n\"\n\t}\n\toutput += `\u003c/div\u003e\u003c!-- /jumbotron --\u003e` + \"\\n\"\n\treturn output\n}\n\n// XXX: rename Element to Div?\ntype Element []DomStringer\n\nfunc (e *Element) Append(elems ...DomStringer) {\n\t*e = append(*e, elems...)\n}\n\nfunc (e *Element) String(dom DOM) string {\n\toutput := \"\"\n\tfor _, elem := range *e {\n\t\toutput += elem.String(dom) + \"\\n\"\n\t}\n\treturn output\n}\n\ntype Breadcrumb []DomStringer\n\nfunc (b *Breadcrumb) Append(elems ...DomStringer) {\n\t*b = append(*b, elems...)\n}\n\nfunc (b Breadcrumb) String(dom DOM) string {\n\toutput := \"\"\n\tfor idx, entry := range b {\n\t\tif idx \u003e 0 {\n\t\t\toutput += \" / \"\n\t\t}\n\t\toutput += entry.String(dom)\n\t}\n\treturn output\n}\n\ntype Columns struct {\n\tMaxWidth int\n\tColumns []Element\n}\n\nfunc (c *Columns) Append(elems ...Element) {\n\tc.Columns = append(c.Columns, elems...)\n}\n\nfunc (c Columns) String(dom DOM) string {\n\toutput := `\u003cdiv class=\"columns-` + strconv.Itoa(c.MaxWidth) + `\"\u003e` + \"\\n\"\n\tfor _, entry := range c.Columns {\n\t\toutput += `\u003cdiv class=\"column\"\u003e` + \"\\n\\n\"\n\t\toutput += entry.String(dom)\n\t\toutput += \"\u003c/div\u003e\u003c!-- /column--\u003e\\n\"\n\t}\n\toutput += \"\u003c/div\u003e\u003c!-- /columns-\" + strconv.Itoa(c.MaxWidth) + \" --\u003e\\n\"\n\treturn output\n}\n\ntype Link struct {\n\tText string\n\tPath string\n\tURL string\n}\n\n// TODO: image\n\n// TODO: pager\n\nfunc (l Link) String(dom DOM) string {\n\turl := \"\"\n\tswitch {\n\tcase l.Path != \"\" \u0026\u0026 l.URL != \"\":\n\t\tpanic(\"a link should have a path or a URL, not both.\")\n\tcase l.Path != \"\":\n\t\tif l.Text == \"\" {\n\t\t\tl.Text = l.Path\n\t\t}\n\t\turl = dom.Prefix + l.Path\n\tcase l.URL != \"\":\n\t\tif l.Text == \"\" {\n\t\t\tl.Text = l.URL\n\t\t}\n\t\turl = l.URL\n\t}\n\n\treturn \"[\" + l.Text + \"](\" + url + \")\"\n}\n\ntype BulletList []DomStringer\n\nfunc (bl BulletList) String(dom DOM) string {\n\toutput := \"\"\n\n\tfor _, entry := range bl {\n\t\toutput += \"- \" + entry.String(dom) + \"\\n\"\n\t}\n\n\treturn output\n}\n\nfunc Text(s string) DomStringer {\n\treturn Raw{Content: s}\n}\n\ntype DomStringer interface {\n\tString(dom DOM) string\n}\n\ntype Raw struct {\n\tContent string\n}\n\nfunc (r Raw) String(_ DOM) string {\n\treturn r.Content\n}\n\ntype (\n\tH1 string\n\tH2 string\n\tH3 string\n\tH4 string\n\tH5 string\n\tH6 string\n\tBold string\n\tItalic string\n\tCode string\n\tParagraph string\n\tQuote string\n\tHR struct{}\n)\n\nfunc (text H1) String(_ DOM) string { return \"# \" + string(text) + \"\\n\" }\nfunc (text H2) String(_ DOM) string { return \"## \" + string(text) + \"\\n\" }\nfunc (text H3) String(_ DOM) string { return \"### \" + string(text) + \"\\n\" }\nfunc (text H4) String(_ DOM) string { return \"#### \" + string(text) + \"\\n\" }\nfunc (text H5) String(_ DOM) string { return \"##### \" + string(text) + \"\\n\" }\nfunc (text H6) String(_ DOM) string { return \"###### \" + string(text) + \"\\n\" }\nfunc (text Quote) String(_ DOM) string { return \"\u003e \" + string(text) + \"\\n\" }\nfunc (text Bold) String(_ DOM) string { return \"**\" + string(text) + \"**\" }\nfunc (text Italic) String(_ DOM) string { return \"_\" + string(text) + \"_\" }\nfunc (text Paragraph) String(_ DOM) string { return \"\\n\" + string(text) + \"\\n\" }\nfunc (_ HR) String(_ DOM) string { return \"\\n---\\n\" }\n\nfunc (text Code) String(_ DOM) string {\n\t// multiline\n\tif strings.Contains(string(text), \"\\n\") {\n\t\treturn \"\\n```\\n\" + string(text) + \"\\n```\\n\"\n\t}\n\n\t// single line\n\treturn \"`\" + string(text) + \"`\"\n}\n" @@ -2925,10 +2697,6 @@ "name": "watchdog", "path": "gno.land/p/demo/watchdog", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/demo/watchdog\n\nrequire gno.land/p/demo/uassert v0.0.0-latest\n" - }, { "name": "watchdog.gno", "body": "package watchdog\n\nimport \"time\"\n\ntype Watchdog struct {\n\tDuration time.Duration\n\tlastUpdate time.Time\n\tlastDown time.Time\n}\n\nfunc (w *Watchdog) Alive() {\n\tnow := time.Now()\n\tif !w.IsAlive() {\n\t\tw.lastDown = now\n\t}\n\tw.lastUpdate = now\n}\n\nfunc (w Watchdog) Status() string {\n\tif w.IsAlive() {\n\t\treturn \"OK\"\n\t}\n\treturn \"KO\"\n}\n\nfunc (w Watchdog) IsAlive() bool {\n\treturn time.Since(w.lastUpdate) \u003c w.Duration\n}\n\nfunc (w Watchdog) UpSince() time.Time {\n\treturn w.lastDown\n}\n\nfunc (w Watchdog) DownSince() time.Time {\n\tif !w.IsAlive() {\n\t\treturn w.lastUpdate\n\t}\n\treturn time.Time{}\n}\n" @@ -2963,10 +2731,6 @@ "name": "proposal", "path": "gno.land/p/gov/proposal", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/gov/proposal\n\nrequire (\n\tgno.land/p/demo/context v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n)\n" - }, { "name": "proposal.gno", "body": "// Package proposal provides a structure for executing proposals.\npackage proposal\n\nimport (\n\t\"errors\"\n\t\"std\"\n\n\t\"gno.land/p/demo/context\"\n)\n\nvar errNotGovDAO = errors.New(\"only r/gov/dao can be the caller\")\n\n// NewExecutor creates a new executor with the provided callback function.\nfunc NewExecutor(callback func() error) Executor {\n\treturn \u0026executorImpl{\n\t\tcallback: callback,\n\t\tdone: false,\n\t}\n}\n\n// NewCtxExecutor creates a new executor with the provided callback function.\nfunc NewCtxExecutor(callback func(ctx context.Context) error) Executor {\n\treturn \u0026executorImpl{\n\t\tcallbackCtx: callback,\n\t\tdone: false,\n\t}\n}\n\n// executorImpl is an implementation of the Executor interface.\ntype executorImpl struct {\n\tcallback func() error\n\tcallbackCtx func(ctx context.Context) error\n\tdone bool\n\tsuccess bool\n}\n\n// Execute runs the executor's callback function.\nfunc (exec *executorImpl) Execute() error {\n\tif exec.done {\n\t\treturn ErrAlreadyDone\n\t}\n\n\t// Verify the executor is r/gov/dao\n\tassertCalledByGovdao()\n\n\tvar err error\n\tif exec.callback != nil {\n\t\terr = exec.callback()\n\t} else if exec.callbackCtx != nil {\n\t\tctx := context.WithValue(context.Empty(), statusContextKey, approvedStatus)\n\t\terr = exec.callbackCtx(ctx)\n\t}\n\texec.done = true\n\texec.success = err == nil\n\n\treturn err\n}\n\n// IsDone returns whether the executor has been executed.\nfunc (exec *executorImpl) IsDone() bool {\n\treturn exec.done\n}\n\n// IsSuccessful returns whether the execution was successful.\nfunc (exec *executorImpl) IsSuccessful() bool {\n\treturn exec.success\n}\n\n// IsExpired returns whether the execution had expired or not.\n// This implementation never expires.\nfunc (exec *executorImpl) IsExpired() bool {\n\treturn false\n}\n\nfunc IsApprovedByGovdaoContext(ctx context.Context) bool {\n\tv := ctx.Value(statusContextKey)\n\tif v == nil {\n\t\treturn false\n\t}\n\tvs, ok := v.(string)\n\treturn ok \u0026\u0026 vs == approvedStatus\n}\n\nfunc AssertContextApprovedByGovDAO(ctx context.Context) {\n\tif !IsApprovedByGovdaoContext(ctx) {\n\t\tpanic(\"not approved by govdao\")\n\t}\n}\n\n// assertCalledByGovdao asserts that the calling Realm is /r/gov/dao\nfunc assertCalledByGovdao() {\n\tcaller := std.CurrentRealm().PkgPath()\n\n\tif caller != daoPkgPath {\n\t\tpanic(errNotGovDAO)\n\t}\n}\n\ntype propContextKey string\n\nfunc (k propContextKey) String() string { return string(k) }\n\nconst (\n\tstatusContextKey = propContextKey(\"govdao-prop-status\")\n\tapprovedStatus = \"approved\"\n)\n" @@ -3005,10 +2769,6 @@ "name": "validators", "path": "gno.land/p/sys/validators", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/sys/validators\n" - }, { "name": "types.gno", "body": "package validators\n\nimport (\n\t\"errors\"\n\t\"std\"\n)\n\n// ValsetProtocol defines the validator set protocol (PoA / PoS / PoC / ?)\ntype ValsetProtocol interface {\n\t// AddValidator adds a new validator to the validator set.\n\t// If the validator is already present, the method should error out\n\t//\n\t// TODO: This API is not ideal -- the address should be derived from\n\t// the public key, and not be passed in as such, but currently Gno\n\t// does not support crypto address derivation\n\tAddValidator(address std.Address, pubKey string, power uint64) (Validator, error)\n\n\t// RemoveValidator removes the given validator from the set.\n\t// If the validator is not present in the set, the method should error out\n\tRemoveValidator(address std.Address) (Validator, error)\n\n\t// IsValidator returns a flag indicating if the given\n\t// bech32 address is part of the validator set\n\tIsValidator(address std.Address) bool\n\n\t// GetValidator returns the validator using the given address\n\tGetValidator(address std.Address) (Validator, error)\n\n\t// GetValidators returns the currently active validator set\n\tGetValidators() []Validator\n}\n\n// Validator represents a single chain validator\ntype Validator struct {\n\tAddress std.Address // bech32 address\n\tPubKey string // bech32 representation of the public key\n\tVotingPower uint64\n}\n\nconst (\n\tValidatorAddedEvent = \"ValidatorAdded\" // emitted when a validator was added to the set\n\tValidatorRemovedEvent = \"ValidatorRemoved\" // emitted when a validator was removed from the set\n)\n\nvar (\n\t// ErrValidatorExists is returned when the validator is already in the set\n\tErrValidatorExists = errors.New(\"validator already exists\")\n\n\t// ErrValidatorMissing is returned when the validator is not in the set\n\tErrValidatorMissing = errors.New(\"validator doesn't exist\")\n)\n" @@ -3039,10 +2799,6 @@ "name": "poa", "path": "gno.land/p/nt/poa", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/p/nt/poa\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n\tgno.land/p/sys/validators v0.0.0-latest\n)\n" - }, { "name": "option.gno", "body": "package poa\n\nimport \"gno.land/p/sys/validators\"\n\ntype Option func(*PoA)\n\n// WithInitialSet sets the initial PoA validator set\nfunc WithInitialSet(validators []validators.Validator) Option {\n\treturn func(p *PoA) {\n\t\tfor _, validator := range validators {\n\t\t\tp.validators.Set(validator.Address.String(), validator)\n\t\t}\n\t}\n}\n" @@ -3081,10 +2837,6 @@ "name": "gnoface", "path": "gno.land/r/demo/art/gnoface", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/art/gnoface\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" - }, { "name": "gnoface.gno", "body": "package gnoface\n\nimport (\n\t\"math/rand\"\n\t\"std\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nfunc Render(path string) string {\n\tseed := uint64(std.GetHeight())\n\n\tpath = strings.TrimSpace(path)\n\tif path != \"\" {\n\t\ts, err := strconv.Atoi(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tseed = uint64(s)\n\t}\n\n\toutput := ufmt.Sprintf(\"Gnoface #%d\\n\", seed)\n\toutput += \"```\\n\" + Draw(seed) + \"```\\n\"\n\treturn output\n}\n\nfunc Draw(seed uint64) string {\n\tvar (\n\t\thairs = []string{\n\t\t\t\" s\",\n\t\t\t\" .......\",\n\t\t\t\" s s s\",\n\t\t\t\" /\\\\ /\\\\\",\n\t\t\t\" |||||||\",\n\t\t}\n\t\theadtop = []string{\n\t\t\t\" /-------\\\\\",\n\t\t\t\" /~~~~~~~\\\\\",\n\t\t\t\" /|||||||\\\\\",\n\t\t\t\" ////////\\\\\",\n\t\t\t\" |||||||||\",\n\t\t\t\" /\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\",\n\t\t}\n\t\theadspace = []string{\n\t\t\t\" | |\",\n\t\t}\n\t\teyebrow = []string{\n\t\t\t\"~\",\n\t\t\t\"*\",\n\t\t\t\"_\",\n\t\t\t\".\",\n\t\t}\n\t\tear = []string{\n\t\t\t\"o\",\n\t\t\t\" \",\n\t\t\t\"D\",\n\t\t\t\"O\",\n\t\t\t\"\u003c\",\n\t\t\t\"\u003e\",\n\t\t\t\".\",\n\t\t\t\"|\",\n\t\t\t\")\",\n\t\t\t\"(\",\n\t\t}\n\t\teyesmiddle = []string{\n\t\t\t\"| o o |\",\n\t\t\t\"| o _ |\",\n\t\t\t\"| _ o |\",\n\t\t\t\"| . . |\",\n\t\t\t\"| O O |\",\n\t\t\t\"| v v |\",\n\t\t\t\"| X X |\",\n\t\t\t\"| x X |\",\n\t\t\t\"| X D |\",\n\t\t\t\"| ~ ~ |\",\n\t\t}\n\t\tnose = []string{\n\t\t\t\" | o |\",\n\t\t\t\" | O |\",\n\t\t\t\" | V |\",\n\t\t\t\" | L |\",\n\t\t\t\" | C |\",\n\t\t\t\" | ~ |\",\n\t\t\t\" | . . |\",\n\t\t\t\" | . |\",\n\t\t}\n\t\tmouth = []string{\n\t\t\t\" | __/ |\",\n\t\t\t\" | \\\\_/ |\",\n\t\t\t\" | . |\",\n\t\t\t\" | ___ |\",\n\t\t\t\" | ~~~ |\",\n\t\t\t\" | === |\",\n\t\t\t\" | \u003c=\u003e |\",\n\t\t}\n\t\theadbottom = []string{\n\t\t\t\" \\\\-------/\",\n\t\t\t\" \\\\~~~~~~~/\",\n\t\t\t\" \\\\_______/\",\n\t\t}\n\t)\n\n\tr := rand.New(rand.NewPCG(seed, 0xdeadbeef))\n\n\treturn pick(r, hairs) + \"\\n\" +\n\t\tpick(r, headtop) + \"\\n\" +\n\t\tpick(r, headspace) + \"\\n\" +\n\t\t\" | \" + pick(r, eyebrow) + \" \" + pick(r, eyebrow) + \" |\\n\" +\n\t\tpick(r, ear) + pick(r, eyesmiddle) + pick(r, ear) + \"\\n\" +\n\t\tpick(r, headspace) + \"\\n\" +\n\t\tpick(r, nose) + \"\\n\" +\n\t\tpick(r, headspace) + \"\\n\" +\n\t\tpick(r, mouth) + \"\\n\" +\n\t\tpick(r, headspace) + \"\\n\" +\n\t\tpick(r, headbottom) + \"\\n\"\n}\n\nfunc pick(r *rand.Rand, slice []string) string {\n\treturn slice[r.IntN(len(slice))]\n}\n\n// based on https://github.com/moul/pipotron/blob/master/dict/ascii-face.yml\n" @@ -3119,10 +2871,6 @@ "name": "millipede", "path": "gno.land/r/demo/art/millipede", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/art/millipede\n\nrequire gno.land/p/demo/ufmt v0.0.0-latest\n" - }, { "name": "millipede.gno", "body": "package millipede\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/ufmt\"\n)\n\nconst (\n\tminSize = 1\n\tdefaultSize = 20\n\tmaxSize = 100\n)\n\nfunc Draw(size int) string {\n\tif size \u003c minSize || size \u003e maxSize {\n\t\tpanic(\"invalid millipede size\")\n\t}\n\tpaddings := []string{\" \", \" \", \"\", \" \", \" \", \" \", \" \", \" \", \" \"}\n\tvar b strings.Builder\n\tb.WriteString(\" ╚⊙ ⊙╝\\n\")\n\tfor i := 0; i \u003c size; i++ {\n\t\tb.WriteString(paddings[i%9] + \"╚═(███)═╝\\n\")\n\t}\n\treturn b.String()\n}\n\nfunc Render(path string) string {\n\tsize := defaultSize\n\n\tpath = strings.TrimSpace(path)\n\tif path != \"\" {\n\t\tvar err error\n\t\tsize, err = strconv.Atoi(path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\toutput := \"```\\n\" + Draw(size) + \"```\\n\"\n\tif size \u003e minSize {\n\t\toutput += ufmt.Sprintf(\"[%d](/r/demo/art/millpede:%d)\u003c \", size-1, size-1)\n\t}\n\tif size \u003c maxSize {\n\t\toutput += ufmt.Sprintf(\" \u003e[%d](/r/demo/art/millipede:%d)\", size+1, size+1)\n\t}\n\treturn output\n}\n\n// based on https://github.com/getmillipede/millipede-go/blob/977f046c39c35a650eac0fd30245e96b22c7803c/main.go\n" @@ -3165,10 +2913,6 @@ "name": "banktest.gno", "body": "package banktest\n\nimport (\n\t\"std\"\n\t\"time\"\n)\n\ntype activity struct {\n\tcaller std.Address\n\tsent std.Coins\n\treturned std.Coins\n\ttime time.Time\n}\n\nfunc (act *activity) String() string {\n\treturn act.caller.String() + \" \" +\n\t\tact.sent.String() + \" sent, \" +\n\t\tact.returned.String() + \" returned, at \" +\n\t\tact.time.Format(\"2006-01-02 3:04pm MST\")\n}\n\nvar latest [10]*activity\n\n// Deposit will take the coins (to the realm's pkgaddr) or return them to user.\nfunc Deposit(returnDenom string, returnAmount int64) string {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tsend := std.Coins{{returnDenom, returnAmount}}\n\t// record activity\n\tact := \u0026activity{\n\t\tcaller: caller,\n\t\tsent: std.GetOrigSend(),\n\t\treturned: send,\n\t\ttime: time.Now(),\n\t}\n\tfor i := len(latest) - 2; i \u003e= 0; i-- {\n\t\tlatest[i+1] = latest[i] // shift by +1.\n\t}\n\tlatest[0] = act\n\t// return if any.\n\tif returnAmount \u003e 0 {\n\t\tbanker := std.GetBanker(std.BankerTypeOrigSend)\n\t\tpkgaddr := std.GetOrigPkgAddr()\n\t\t// TODO: use std.Coins constructors, this isn't generally safe.\n\t\tbanker.SendCoins(pkgaddr, caller, send)\n\t\treturn \"returned!\"\n\t} else {\n\t\treturn \"thank you!\"\n\t}\n}\n\nfunc Render(path string) string {\n\t// get realm coins.\n\tbanker := std.GetBanker(std.BankerTypeReadonly)\n\tcoins := banker.GetCoins(std.GetOrigPkgAddr())\n\n\t// render\n\tres := \"\"\n\tres += \"## recent activity\\n\"\n\tres += \"\\n\"\n\tfor _, act := range latest {\n\t\tif act == nil {\n\t\t\tbreak\n\t\t}\n\t\tres += \" * \" + act.String() + \"\\n\"\n\t}\n\tres += \"\\n\"\n\tres += \"## total deposits\\n\"\n\tres += coins.String()\n\treturn res\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/banktest\n" - }, { "name": "z_0_filetest.gno", "body": "// SEND: 100000000ugnot\n\npackage main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/demo/banktest\"\n)\n\nfunc main() {\n\t// set up main address and banktest addr.\n\tbanktestAddr := std.DerivePkgAddr(\"gno.land/r/demo/banktest\")\n\tmainaddr := std.DerivePkgAddr(\"main\")\n\tstd.TestSetOrigCaller(mainaddr)\n\tstd.TestSetOrigPkgAddr(banktestAddr)\n\n\t// get and print balance of mainaddr.\n\t// with the SEND, + 200 gnot given by the TestContext, main should have 300gnot.\n\tbanker := std.GetBanker(std.BankerTypeRealmSend)\n\tmainbal := banker.GetCoins(mainaddr)\n\tprintln(\"main before:\", mainbal)\n\n\t// simulate a Deposit call. use Send + OrigSend to simulate -send.\n\tbanker.SendCoins(mainaddr, banktestAddr, std.Coins{{\"ugnot\", 100_000_000}})\n\tstd.TestSetOrigSend(std.Coins{{\"ugnot\", 100_000_000}}, nil)\n\tres := banktest.Deposit(\"ugnot\", 50_000_000)\n\tprintln(\"Deposit():\", res)\n\n\t// print main balance after.\n\tmainbal = banker.GetCoins(mainaddr)\n\tprintln(\"main after:\", mainbal)\n\n\t// simulate a Render(). banker should have given back all coins.\n\tres = banktest.Render(\"\")\n\tprintln(res)\n}\n\n// Output:\n// main before: 300000000ugnot\n// Deposit(): returned!\n// main after: 250000000ugnot\n// ## recent activity\n//\n// * g17rgsdnfxzza0sdfsdma37sdwxagsz378833ca4 100000000ugnot sent, 50000000ugnot returned, at 2009-02-13 11:31pm UTC\n//\n// ## total deposits\n// 50000000ugnot\n" @@ -3218,10 +2962,6 @@ { "name": "bar20_test.gno", "body": "package bar20\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/urequire\"\n)\n\nfunc TestPackage(t *testing.T) {\n\talice := testutils.TestAddress(\"alice\")\n\tstd.TestSetRealm(std.NewUserRealm(alice))\n\tstd.TestSetOrigCaller(alice) // XXX: should not need this\n\n\turequire.Equal(t, Token.BalanceOf(alice), uint64(0))\n\turequire.Equal(t, Faucet(), \"OK\")\n\turequire.Equal(t, Token.BalanceOf(alice), uint64(1_000_000))\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/bar20\n\nrequire (\n\tgno.land/p/demo/grc/grc20 v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n)\n" } ] }, @@ -3249,10 +2989,6 @@ "name": "deep", "path": "gno.land/r/demo/deep/very/deep", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/deep/very/deep\n" - }, { "name": "render.gno", "body": "package deep\n\nfunc Render(path string) string {\n\tif path == \"\" {\n\t\treturn \"it works!\"\n\t} else {\n\t\treturn \"hi \" + path\n\t}\n}\n" @@ -3290,10 +3026,6 @@ { "name": "echo_test.gno", "body": "package echo\n\nimport \"testing\"\n\nfunc Test(t *testing.T) {\n\tif Render(\"aa\") != \"aa\" {\n\t\tt.Fail()\n\t}\n\tif Render(\"\") != \"\" {\n\t\tt.Fail()\n\t}\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/echo\n" } ] }, @@ -3324,10 +3056,6 @@ { "name": "event.gno", "body": "package event\n\nimport (\n\t\"std\"\n)\n\nfunc Emit(value string) {\n\tstd.Emit(\"TAG\", \"key\", value)\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/event\n" } ] }, @@ -3362,10 +3090,6 @@ { "name": "foo1155_test.gno", "body": "package foo1155\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/grc/grc1155\"\n\t\"gno.land/p/demo/users\"\n)\n\nfunc TestFoo721(t *testing.T) {\n\tadmin := users.AddressOrName(\"g10x5phu0k6p64cwrhfpsc8tk43st9kug6wft530\")\n\tbob := users.AddressOrName(\"g1ze6et22ces5atv79y4xh38s4kuraey4y2fr6tw\")\n\ttid1 := grc1155.TokenID(\"1\")\n\ttid2 := grc1155.TokenID(\"2\")\n\n\tfor i, tc := range []struct {\n\t\tname string\n\t\texpected interface{}\n\t\tfn func() interface{}\n\t}{\n\t\t{\"BalanceOf(admin, tid1)\", uint64(100), func() interface{} { return BalanceOf(admin, tid1) }},\n\t\t{\"BalanceOf(bob, tid1)\", uint64(0), func() interface{} { return BalanceOf(bob, tid1) }},\n\t\t{\"IsApprovedForAll(admin, bob)\", false, func() interface{} { return IsApprovedForAll(admin, bob) }},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := tc.fn()\n\t\t\tif tc.expected != got {\n\t\t\t\tt.Errorf(\"expected: %v got: %v\", tc.expected, got)\n\t\t\t}\n\t\t})\n\t}\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/foo1155\n\nrequire (\n\tgno.land/p/demo/grc/grc1155 v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" } ] }, @@ -3400,10 +3124,6 @@ { "name": "foo20_test.gno", "body": "package foo20\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/uassert\"\n\tpusers \"gno.land/p/demo/users\"\n\t\"gno.land/r/demo/users\"\n)\n\nfunc TestReadOnlyPublicMethods(t *testing.T) {\n\tvar (\n\t\tadmin = pusers.AddressOrName(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\t\talice = pusers.AddressOrName(testutils.TestAddress(\"alice\"))\n\t\tbob = pusers.AddressOrName(testutils.TestAddress(\"bob\"))\n\t)\n\n\ttype test struct {\n\t\tname string\n\t\tbalance uint64\n\t\tfn func() uint64\n\t}\n\n\t// check balances #1.\n\t{\n\t\ttests := []test{\n\t\t\t{\"TotalSupply\", 10_000_000_000, func() uint64 { return TotalSupply() }},\n\t\t\t{\"BalanceOf(admin)\", 10_000_000_000, func() uint64 { return BalanceOf(admin) }},\n\t\t\t{\"BalanceOf(alice)\", 0, func() uint64 { return BalanceOf(alice) }},\n\t\t\t{\"Allowance(admin, alice)\", 0, func() uint64 { return Allowance(admin, alice) }},\n\t\t\t{\"BalanceOf(bob)\", 0, func() uint64 { return BalanceOf(bob) }},\n\t\t}\n\t\tfor _, tc := range tests {\n\t\t\tgot := tc.fn()\n\t\t\tuassert.Equal(t, got, tc.balance)\n\t\t}\n\t}\n\n\t// bob uses the faucet.\n\tstd.TestSetOrigCaller(users.Resolve(bob))\n\tFaucet()\n\n\t// check balances #2.\n\t{\n\t\ttests := []test{\n\t\t\t{\"TotalSupply\", 10_010_000_000, func() uint64 { return TotalSupply() }},\n\t\t\t{\"BalanceOf(admin)\", 10_000_000_000, func() uint64 { return BalanceOf(admin) }},\n\t\t\t{\"BalanceOf(alice)\", 0, func() uint64 { return BalanceOf(alice) }},\n\t\t\t{\"Allowance(admin, alice)\", 0, func() uint64 { return Allowance(admin, alice) }},\n\t\t\t{\"BalanceOf(bob)\", 10_000_000, func() uint64 { return BalanceOf(bob) }},\n\t\t}\n\t\tfor _, tc := range tests {\n\t\t\tgot := tc.fn()\n\t\t\tuassert.Equal(t, got, tc.balance)\n\t\t}\n\t}\n}\n\nfunc TestErrConditions(t *testing.T) {\n\tvar (\n\t\tadmin = pusers.AddressOrName(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\t\talice = pusers.AddressOrName(testutils.TestAddress(\"alice\"))\n\t\tempty = pusers.AddressOrName(\"\")\n\t)\n\n\ttype test struct {\n\t\tname string\n\t\tmsg string\n\t\tfn func()\n\t}\n\n\tstd.TestSetOrigCaller(users.Resolve(admin))\n\t{\n\t\ttests := []test{\n\t\t\t{\"Transfer(admin, 1)\", \"cannot send transfer to self\", func() { Transfer(admin, 1) }},\n\t\t\t{\"Approve(empty, 1))\", \"invalid address\", func() { Approve(empty, 1) }},\n\t\t}\n\t\tfor _, tc := range tests {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tuassert.PanicsWithMessage(t, tc.msg, tc.fn)\n\t\t\t})\n\t\t}\n\t}\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/foo20\n\nrequire (\n\tgno.land/p/demo/grc/grc20 v0.0.0-latest\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" } ] }, @@ -3438,10 +3158,6 @@ { "name": "foo721_test.gno", "body": "package foo721\n\nimport (\n\t\"testing\"\n\n\t\"gno.land/p/demo/grc/grc721\"\n\t\"gno.land/r/demo/users\"\n\n\tpusers \"gno.land/p/demo/users\"\n)\n\nfunc TestFoo721(t *testing.T) {\n\tadmin := pusers.AddressOrName(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\")\n\thariom := pusers.AddressOrName(\"g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm\")\n\n\tfor i, tc := range []struct {\n\t\tname string\n\t\texpected interface{}\n\t\tfn func() interface{}\n\t}{\n\t\t{\"BalanceOf(admin)\", uint64(10), func() interface{} { return BalanceOf(admin) }},\n\t\t{\"BalanceOf(hariom)\", uint64(5), func() interface{} { return BalanceOf(hariom) }},\n\t\t{\"OwnerOf(0)\", users.Resolve(admin), func() interface{} { return OwnerOf(grc721.TokenID(\"0\")) }},\n\t\t{\"IsApprovedForAll(admin, hariom)\", false, func() interface{} { return IsApprovedForAll(admin, hariom) }},\n\t} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := tc.fn()\n\t\t\tif tc.expected != got {\n\t\t\t\tt.Errorf(\"expected: %v got: %v\", tc.expected, got)\n\t\t\t}\n\t\t})\n\t}\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/foo721\n\nrequire (\n\tgno.land/p/demo/grc/grc721 v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" } ] }, @@ -3469,10 +3185,6 @@ "name": "foo20", "path": "gno.land/r/demo/grc20factory", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/grc20factory\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/grc/grc20 v0.0.0-latest\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "grc20factory.gno", "body": "package foo20\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/grc/grc20\"\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar instances avl.Tree // symbol -\u003e instance\n\nfunc New(name, symbol string, decimals uint, initialMint, faucet uint64) {\n\tcaller := std.PrevRealm().Addr()\n\tNewWithAdmin(name, symbol, decimals, initialMint, faucet, caller)\n}\n\nfunc NewWithAdmin(name, symbol string, decimals uint, initialMint, faucet uint64, admin std.Address) {\n\texists := instances.Has(symbol)\n\tif exists {\n\t\tpanic(\"token already exists\")\n\t}\n\n\tbanker := grc20.NewBanker(name, symbol, decimals)\n\tif initialMint \u003e 0 {\n\t\tbanker.Mint(admin, initialMint)\n\t}\n\n\tinst := instance{\n\t\tbanker: banker,\n\t\tadmin: ownable.NewWithAddress(admin),\n\t\tfaucet: faucet,\n\t}\n\n\tinstances.Set(symbol, \u0026inst)\n}\n\ntype instance struct {\n\tbanker *grc20.Banker\n\tadmin *ownable.Ownable\n\tfaucet uint64 // per-request amount. disabled if 0.\n}\n\nfunc (inst instance) Token() grc20.Token { return inst.banker.Token() }\n\nfunc TotalSupply(symbol string) uint64 {\n\tinst := mustGetInstance(symbol)\n\treturn inst.Token().TotalSupply()\n}\n\nfunc BalanceOf(symbol string, owner std.Address) uint64 {\n\tinst := mustGetInstance(symbol)\n\treturn inst.Token().BalanceOf(owner)\n}\n\nfunc Allowance(symbol string, owner, spender std.Address) uint64 {\n\tinst := mustGetInstance(symbol)\n\treturn inst.Token().Allowance(owner, spender)\n}\n\nfunc Transfer(symbol string, to std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tcheckErr(inst.Token().Transfer(to, amount))\n}\n\nfunc Approve(symbol string, spender std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tcheckErr(inst.Token().Approve(spender, amount))\n}\n\nfunc TransferFrom(symbol string, from, to std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tcheckErr(inst.Token().TransferFrom(from, to, amount))\n}\n\n// faucet.\nfunc Faucet(symbol string) {\n\tinst := mustGetInstance(symbol)\n\tif inst.faucet == 0 {\n\t\tpanic(\"faucet disabled for this token\")\n\t}\n\t// FIXME: add limits?\n\t// FIXME: add payment in gnot?\n\tcaller := std.PrevRealm().Addr()\n\tcheckErr(inst.banker.Mint(caller, inst.faucet))\n}\n\nfunc Mint(symbol string, to std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tinst.admin.AssertCallerIsOwner()\n\tcheckErr(inst.banker.Mint(to, amount))\n}\n\nfunc Burn(symbol string, from std.Address, amount uint64) {\n\tinst := mustGetInstance(symbol)\n\tinst.admin.AssertCallerIsOwner()\n\tcheckErr(inst.banker.Burn(from, amount))\n}\n\nfunc Render(path string) string {\n\tparts := strings.Split(path, \"/\")\n\tc := len(parts)\n\n\tswitch {\n\tcase path == \"\":\n\t\treturn \"TODO: list existing tokens and admins\"\n\tcase c == 1:\n\t\tsymbol := parts[0]\n\t\tinst := mustGetInstance(symbol)\n\t\treturn inst.banker.RenderHome()\n\tcase c == 3 \u0026\u0026 parts[1] == \"balance\":\n\t\tsymbol := parts[0]\n\t\tinst := mustGetInstance(symbol)\n\t\towner := std.Address(parts[2])\n\t\tbalance := inst.Token().BalanceOf(owner)\n\t\treturn ufmt.Sprintf(\"%d\", balance)\n\tdefault:\n\t\treturn \"404\\n\"\n\t}\n}\n\nfunc mustGetInstance(symbol string) *instance {\n\tt, exists := instances.Get(symbol)\n\tif !exists {\n\t\tpanic(\"token instance does not exist\")\n\t}\n\treturn t.(*instance)\n}\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" @@ -3511,10 +3223,6 @@ "name": "README.md", "body": "### - test package\n\n ./build/gno test examples/gno.land/r/demo/groups/\n\n### - add pkg\n\n ./build/gnokey maketx addpkg -pkgdir \"examples/gno.land/r/demo/groups\" -deposit 100000000ugnot -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1 \n\n### - create group\n\n ./build/gnokey maketx call -func \"CreateGroup\" -args \"dao_trinity_ngo\" -gas-fee \"1000000ugnot\" -gas-wanted 4000000 -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1 \n\n### - add member\n\n ./build/gnokey maketx call -func \"AddMember\" -args \"1\" -args \"g1hd3gwzevxlqmd3jsf64mpfczag8a8e5j2wdn3c\" -args 12 -args \"i am new user\" -gas-fee \"1000000ugnot\" -gas-wanted \"4000000\" -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1\n\n### - delete member\n\n ./build/gnokey maketx call -func \"DeleteMember\" -args \"1\" -args \"0\" -gas-fee \"1000000ugnot\" -gas-wanted \"4000000\" -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1\n\n### - delete group\n\n ./build/gnokey maketx call -func \"DeleteGroup\" -args \"1\" -gas-fee \"1000000ugnot\" -gas-wanted \"4000000\" -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath \"gno.land/r/demo/groups\" test1\n\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/groups\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" - }, { "name": "group.gno", "body": "package groups\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\ntype GroupID uint64\n\nfunc (gid GroupID) String() string {\n\treturn strconv.Itoa(int(gid))\n}\n\ntype Group struct {\n\tid GroupID\n\turl string\n\tname string\n\tlastMemberID MemberID\n\tmembers avl.Tree\n\tcreator std.Address\n\tcreatedAt time.Time\n}\n\nfunc newGroup(url string, name string, creator std.Address) *Group {\n\tif !reName.MatchString(name) {\n\t\tpanic(\"invalid name: \" + name)\n\t}\n\tif gGroupsByName.Has(name) {\n\t\tpanic(\"Group with such name already exists\")\n\t}\n\treturn \u0026Group{\n\t\tid: incGetGroupID(),\n\t\turl: url,\n\t\tname: name,\n\t\tcreator: creator,\n\t\tmembers: avl.Tree{},\n\t\tcreatedAt: time.Now(),\n\t}\n}\n\nfunc (group *Group) newMember(id MemberID, address std.Address, weight int, metadata string) *Member {\n\tif group.members.Has(address.String()) {\n\t\tpanic(\"this member for this group already exists\")\n\t}\n\treturn \u0026Member{\n\t\tid: id,\n\t\taddress: address,\n\t\tweight: weight,\n\t\tmetadata: metadata,\n\t\tcreatedAt: time.Now(),\n\t}\n}\n\nfunc (group *Group) HasPermission(addr std.Address, perm Permission) bool {\n\tif group.creator != addr {\n\t\treturn false\n\t}\n\treturn isValidPermission(perm)\n}\n\nfunc (group *Group) RenderGroup() string {\n\tstr := \"Group ID: \" + groupIDKey(group.id) + \"\\n\\n\" +\n\t\t\"Group Name: \" + group.name + \"\\n\\n\" +\n\t\t\"Group Creator: \" + usernameOf(group.creator) + \"\\n\\n\" +\n\t\t\"Group createdAt: \" + group.createdAt.String() + \"\\n\\n\" +\n\t\t\"Group Last MemberID: \" + memberIDKey(group.lastMemberID) + \"\\n\\n\"\n\n\tstr += \"Group Members: \\n\\n\"\n\tgroup.members.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\tmember := value.(*Member)\n\t\tstr += member.getMemberStr()\n\t\treturn false\n\t})\n\treturn str\n}\n\nfunc (group *Group) deleteGroup() {\n\tgidkey := groupIDKey(group.id)\n\t_, gGroupsRemoved := gGroups.Remove(gidkey)\n\tif !gGroupsRemoved {\n\t\tpanic(\"group does not exist with id \" + group.id.String())\n\t}\n\tgGroupsByName.Remove(group.name)\n}\n\nfunc (group *Group) deleteMember(mid MemberID) {\n\tgidkey := groupIDKey(group.id)\n\tif !gGroups.Has(gidkey) {\n\t\tpanic(\"group does not exist with id \" + group.id.String())\n\t}\n\n\tg := getGroup(group.id)\n\tmidkey := memberIDKey(mid)\n\tg.members.Remove(midkey)\n}\n" @@ -3617,10 +3325,6 @@ "name": "keystore", "path": "gno.land/r/demo/keystore", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/keystore\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "keystore.gno", "body": "package keystore\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar data avl.Tree\n\nconst (\n\tBaseURL = \"/r/demo/keystore\"\n\tStatusOK = \"ok\"\n\tStatusNoUser = \"user not found\"\n\tStatusNotFound = \"key not found\"\n\tStatusNoWriteAccess = \"no write access\"\n\tStatusCouldNotExecute = \"could not execute\"\n\tStatusNoDatabases = \"no databases\"\n)\n\nfunc init() {\n\tdata = avl.Tree{} // user -\u003e avl.Tree\n}\n\n// KeyStore stores the owner-specific avl.Tree\ntype KeyStore struct {\n\tOwner std.Address\n\tData avl.Tree\n}\n\n// Set will set a value to a key\n// requires write-access (original caller must be caller)\nfunc Set(k, v string) string {\n\torigOwner := std.GetOrigCaller()\n\treturn set(origOwner.String(), k, v)\n}\n\n// set (private) will set a key to value\n// requires write-access (original caller must be caller)\nfunc set(owner, k, v string) string {\n\torigOwner := std.GetOrigCaller()\n\tif origOwner.String() != owner {\n\t\treturn StatusNoWriteAccess\n\t}\n\tvar keystore *KeyStore\n\tkeystoreInterface, exists := data.Get(owner)\n\tif !exists {\n\t\tkeystore = \u0026KeyStore{\n\t\t\tOwner: origOwner,\n\t\t\tData: avl.Tree{},\n\t\t}\n\t\tdata.Set(owner, keystore)\n\t} else {\n\t\tkeystore = keystoreInterface.(*KeyStore)\n\t}\n\tkeystore.Data.Set(k, v)\n\treturn StatusOK\n}\n\n// Remove removes a key\n// requires write-access (original owner must be caller)\nfunc Remove(k string) string {\n\torigOwner := std.GetOrigCaller()\n\treturn remove(origOwner.String(), k)\n}\n\n// remove (private) removes a key\n// requires write-access (original owner must be caller)\nfunc remove(owner, k string) string {\n\torigOwner := std.GetOrigCaller()\n\tif origOwner.String() != owner {\n\t\treturn StatusNoWriteAccess\n\t}\n\tvar keystore *KeyStore\n\tkeystoreInterface, exists := data.Get(owner)\n\tif !exists {\n\t\tkeystore = \u0026KeyStore{\n\t\t\tOwner: origOwner,\n\t\t\tData: avl.Tree{},\n\t\t}\n\t\tdata.Set(owner, keystore)\n\t} else {\n\t\tkeystore = keystoreInterface.(*KeyStore)\n\t}\n\t_, removed := keystore.Data.Remove(k)\n\tif !removed {\n\t\treturn StatusCouldNotExecute\n\t}\n\treturn StatusOK\n}\n\n// Get returns a value for a key\n// read-only\nfunc Get(k string) string {\n\torigOwner := std.GetOrigCaller()\n\treturn remove(origOwner.String(), k)\n}\n\n// get (private) returns a value for a key\n// read-only\nfunc get(owner, k string) string {\n\tkeystoreInterface, exists := data.Get(owner)\n\tif !exists {\n\t\treturn StatusNoUser\n\t}\n\tkeystore := keystoreInterface.(*KeyStore)\n\tval, found := keystore.Data.Get(k)\n\tif !found {\n\t\treturn StatusNotFound\n\t}\n\treturn val.(string)\n}\n\n// Size returns size of database\n// read-only\nfunc Size() string {\n\torigOwner := std.GetOrigCaller()\n\treturn size(origOwner.String())\n}\n\nfunc size(owner string) string {\n\tkeystoreInterface, exists := data.Get(owner)\n\tif !exists {\n\t\treturn StatusNoUser\n\t}\n\tkeystore := keystoreInterface.(*KeyStore)\n\treturn ufmt.Sprintf(\"%d\", keystore.Data.Size())\n}\n\n// Render provides read-only url access to the functions of the keystore\n// \"\" -\u003e show all keystores listed by owner\n// \"owner\" -\u003e show all keys for that owner's keystore\n// \"owner:size\" -\u003e returns size of owner's keystore\n// \"owner:get:key\" -\u003e show value for that key in owner's keystore\nfunc Render(p string) string {\n\tvar response string\n\targs := strings.Split(p, \":\")\n\tnumArgs := len(args)\n\tif p == \"\" {\n\t\tnumArgs = 0\n\t}\n\tswitch numArgs {\n\tcase 0:\n\t\tif data.Size() == 0 {\n\t\t\treturn StatusNoDatabases\n\t\t}\n\t\tdata.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tks := value.(*KeyStore)\n\t\t\tresponse += ufmt.Sprintf(\"- [%s](%s:%s) (%d keys)\\n\", ks.Owner, BaseURL, ks.Owner, ks.Data.Size())\n\t\t\treturn false\n\t\t})\n\tcase 1:\n\t\towner := args[0]\n\t\tkeystoreInterface, exists := data.Get(owner)\n\t\tif !exists {\n\t\t\treturn StatusNoUser\n\t\t}\n\t\tks := keystoreInterface.(*KeyStore)\n\t\ti := 0\n\t\tresponse += ufmt.Sprintf(\"# %s database\\n\\n\", ks.Owner)\n\t\tks.Data.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t\tresponse += ufmt.Sprintf(\"- %d [%s](%s:%s:get:%s)\\n\", i, key, BaseURL, ks.Owner, key)\n\t\t\ti++\n\t\t\treturn false\n\t\t})\n\tcase 2:\n\t\towner := args[0]\n\t\tcmd := args[1]\n\t\tif cmd == \"size\" {\n\t\t\treturn size(owner)\n\t\t}\n\tcase 3:\n\t\towner := args[0]\n\t\tcmd := args[1]\n\t\tkey := args[2]\n\t\tif cmd == \"get\" {\n\t\t\treturn get(owner, key)\n\t\t}\n\t}\n\n\treturn response\n}\n" @@ -3655,10 +3359,6 @@ "name": "markdown", "path": "gno.land/r/demo/markdown_test", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/markdown_test\n" - }, { "name": "markdown.gno", "body": "package markdown\n\n// this package can be used to test markdown rendering engines.\n\nfunc Render(path string) string {\n\toutput := `_imported from https://github.com/markedjs/marked/blob/master/docs/demo/quickref.md_\n\nMarkdown Quick Reference\n========================\n\nThis guide is a very brief overview, with examples, of the syntax that [Markdown] supports. It is itself written in Markdown and you can copy the samples over to the left-hand pane for experimentation. It's shown as *text* and not *rendered HTML*.\n\n[Markdown]: http://daringfireball.net/projects/markdown/\n\n\nSimple Text Formatting\n======================\n\nFirst thing is first. You can use *stars* or _underscores_ for italics. **Double stars** and __double underscores__ for bold. ***Three together*** for ___both___.\n\nParagraphs are pretty easy too. Just have a blank line between chunks of text.\n\n\u003e This chunk of text is in a block quote. Its multiple lines will all be\n\u003e indented a bit from the rest of the text.\n\u003e\n\u003e \u003e Multiple levels of block quotes also work.\n\nSometimes you want to include code, such as when you are explaining how ` + \"`\u003ch1\u003e`\" + ` HTML tags work, or maybe you are a programmer and you are discussing ` + \"`someMethod()`\" + `.\n\nIf you want to include code and have new\nlines preserved, indent the line with a tab\nor at least four spaces:\n\n Extra spaces work here too.\n This is also called preformatted text and it is useful for showing examples.\n The text will stay as text, so any *markdown* or \u003cu\u003eHTML\u003c/u\u003e you add will\n not show up formatted. This way you can show markdown examples in a\n markdown document.\n\n\u003e You can also use preformatted text with your blockquotes\n\u003e as long as you add at least five spaces.\n\n\nHeadings\n========\n\nThere are a couple of ways to make headings. Using three or more equals signs on a line under a heading makes it into an \"h1\" style. Three or more hyphens under a line makes it \"h2\" (slightly smaller). You can also use multiple pound symbols (` + \"`#`\" + `) before and after a heading. Pounds after the title are ignored. Here are some examples:\n\nThis is H1\n==========\n\nThis is H2\n----------\n\n# This is H1\n## This is H2\n### This is H3 with some extra pounds ###\n#### You get the idea ####\n##### I don't need extra pounds at the end\n###### H6 is the max\n\n\nLinks\n=====\n\nLet's link to a few sites. First, let's use the bare URL, like \u003chttps://www.github.com\u003e. Great for text, but ugly for HTML.\nNext is an inline link to [Google](https://www.google.com). A little nicer.\nThis is a reference-style link to [Wikipedia] [1].\nLastly, here's a pretty link to [Yahoo]. The reference-style and pretty links both automatically use the links defined below, but they could be defined *anywhere* in the markdown and are removed from the HTML. The names are also case insensitive, so you can use [YaHoO] and have it link properly.\n\n[1]: https://www.wikipedia.org\n[Yahoo]: https://www.yahoo.com\n\nTitle attributes may be added to links by adding text after a link.\nThis is the [inline link](https://www.bing.com \"Bing\") with a \"Bing\" title.\nYou can also go to [W3C] [2] and maybe visit a [friend].\n\n[2]: https://w3c.org (The W3C puts out specs for web-based things)\n[Friend]: https://facebook.com \"Facebook!\"\n\nEmail addresses in plain text are not linked: test@example.com.\nEmail addresses wrapped in angle brackets are linked: \u003ctest@example.com\u003e.\nThey are also obfuscated so that email harvesting spam robots hopefully won't get them.\n\n\nLists\n=====\n\n* This is a bulleted list\n* Great for shopping lists\n- You can also use hyphens\n+ Or plus symbols\n\nThe above is an \"unordered\" list. Now, on for a bit of order.\n\n1. Numbered lists are also easy\n2. Just start with a number\n3738762. However, the actual number doesn't matter when converted to HTML.\n1. This will still show up as 4.\n\nYou might want a few advanced lists:\n\n- This top-level list is wrapped in paragraph tags\n- This generates an extra space between each top-level item.\n\n- You do it by adding a blank line\n\n- This nested list also has blank lines between the list items.\n\n- How to create nested lists\n 1. Start your regular list\n 2. Indent nested lists with two spaces\n 3. Further nesting means you should indent with two more spaces\n * This line is indented with four spaces.\n\n- List items can be quite lengthy. You can keep typing and either continue\nthem on the next line with no indentation.\n\n- Alternately, if that looks ugly, you can also\n indent the next line a bit for a prettier look.\n\n- You can put large blocks of text in your list by just indenting with two spaces.\n\n This is formatted the same as code, but you can inspect the HTML\n and find that it's just wrapped in a ` + \"`\u003cp\u003e`\" + ` tag and *won't* be shown\n as preformatted text.\n\n You can keep adding more and more paragraphs to a single\n list item by adding the traditional blank line and then keep\n on indenting the paragraphs with two spaces.\n\n You really only need to indent the first line,\nbut that looks ugly.\n\n- Lists support blockquotes\n\n \u003e Just like this example here. By the way, you can\n \u003e nest lists inside blockquotes!\n \u003e - Fantastic!\n\n- Lists support preformatted text\n\n You just need to indent an additional four spaces.\n\n\nEven More\n=========\n\nHorizontal Rule\n---------------\n\nIf you need a horizontal rule you just need to put at least three hyphens, asterisks, or underscores on a line by themselves. You can also even put spaces between the characters.\n\n---\n****************************\n_ _ _ _ _ _ _\n\nThose three all produced horizontal lines. Keep in mind that three hyphens under any text turns that text into a heading, so add a blank like if you use hyphens.\n\nImages\n------\n\nImages work exactly like links, but they have exclamation points in front. They work with references and titles too.\n\n![Google Logo](https://www.google.com/images/errors/logo_sm.gif) and ![Happy].\n\n[Happy]: https://wpclipart.com/smiley/happy/simple_colors/smiley_face_simple_green_small.png (\"Smiley face\")\n\n\nInline HTML\n-----------\n\nIf markdown is too limiting, you can just insert your own \u003cstrike\u003ecrazy\u003c/strike\u003e HTML. Span-level HTML \u003cu\u003ecan *still* use markdown\u003c/u\u003e. Block level elements must be separated from text by a blank line and must not have any spaces before the opening and closing HTML.\n\n\u003cdiv style='font-family: \"Comic Sans MS\", \"Comic Sans\", cursive;'\u003e\nIt is a pity, but markdown does **not** work in here for most markdown parsers.\n[Marked] handles it pretty well.\n\u003c/div\u003e`\n\treturn output\n}\n" @@ -3693,10 +3393,6 @@ "name": "eval", "path": "gno.land/r/demo/math_eval", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/math_eval\n\nrequire (\n\tgno.land/p/demo/math_eval/int32 v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "math_eval.gno", "body": "// eval realm is capable of evaluating 32-bit integer\n// expressions as they would appear in Go. For example:\n// /r/demo/math_eval:(4+12)/2-1+11*15\npackage eval\n\nimport (\n\tevalint32 \"gno.land/p/demo/math_eval/int32\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nfunc Render(p string) string {\n\tif len(p) == 0 {\n\t\treturn `\nevaluates 32-bit integer expressions. for example:\n\t\t\n[(4+12)/2-1+11*15](/r/demo/math_eval:(4+12)/2-1+11*15)\n\n`\n\t}\n\texpr, err := evalint32.Parse(p)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\tres, err := evalint32.Eval(expr, nil)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\n\treturn ufmt.Sprintf(\"%s = %d\", p, res)\n}\n" @@ -3727,10 +3423,6 @@ "name": "memeland", "path": "gno.land/r/demo/memeland", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/memeland\n\nrequire gno.land/p/demo/memeland v0.0.0-latest\n" - }, { "name": "memeland.gno", "body": "package memeland\n\nimport (\n\t\"std\"\n\t\"time\"\n\n\t\"gno.land/p/demo/memeland\"\n)\n\nvar m *memeland.Memeland\n\nfunc init() {\n\tm = memeland.NewMemeland()\n\tm.TransferOwnership(\"g125em6arxsnj49vx35f0n0z34putv5ty3376fg5\")\n}\n\nfunc PostMeme(data string, timestamp int64) string {\n\treturn m.PostMeme(data, timestamp)\n}\n\nfunc Upvote(id string) string {\n\treturn m.Upvote(id)\n}\n\nfunc GetPostsInRange(startTimestamp, endTimestamp int64, page, pageSize int, sortBy string) string {\n\treturn m.GetPostsInRange(startTimestamp, endTimestamp, page, pageSize, sortBy)\n}\n\nfunc RemovePost(id string) string {\n\treturn m.RemovePost(id)\n}\n\nfunc GetOwner() std.Address {\n\treturn m.Owner()\n}\n\nfunc TransferOwnership(newOwner std.Address) {\n\tif err := m.TransferOwnership(newOwner); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc Render(path string) string {\n\tnumOfMemes := int(m.MemeCounter)\n\tif numOfMemes == 0 {\n\t\treturn \"No memes posted yet! :/\"\n\t}\n\n\t// Default render is get Posts since year 2000 to now\n\treturn m.GetPostsInRange(0, time.Now().Unix(), 1, 10, \"DATE_CREATED\")\n}\n" @@ -3765,10 +3457,6 @@ "name": "README.md", "body": "# microblog realm\n\n## Getting started:\n\n(One-time) Add the microblog package:\n\n```\ngnokey maketx addpkg --pkgpath \"gno.land/p/demo/microblog\" --pkgdir \"examples/gno.land/p/demo/microblog\" \\\n --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast --chainid dev --remote localhost:26657 \u003cYOURKEY\u003e\n```\n\n(One-time) Add the microblog realm:\n\n```\ngnokey maketx addpkg --pkgpath \"gno.land/r/demo/microblog\" --pkgdir \"examples/gno.land/r/demo/microblog\" \\\n --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast --chainid dev --remote localhost:26657 \u003cYOURKEY\u003e\n```\n\nAdd a microblog post:\n\n```\ngnokey maketx call --pkgpath \"gno.land/r/demo/microblog\" --func \"NewPost\" --args \"hello, world\" \\\n --gas-fee \"1000000ugnot\" --gas-wanted \"2000000\" --broadcast --chainid dev --remote localhost:26657 \u003cYOURKEY\u003e\n```" }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/microblog\n\nrequire (\n\tgno.land/p/demo/microblog v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" - }, { "name": "microblog.gno", "body": "// Microblog is a website with shortform posts from users.\n// The API is simple - \"AddPost\" takes markdown and\n// adds it to the users site.\n// The microblog location is determined by the user address\n// /r/demo/microblog:\u003cYOUR-ADDRESS\u003e\npackage microblog\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/microblog\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\ttitle = \"gno-based microblog\"\n\tprefix = \"/r/demo/microblog:\"\n\tm *microblog.Microblog\n)\n\nfunc init() {\n\tm = microblog.NewMicroblog(title, prefix)\n}\n\nfunc renderHome() string {\n\toutput := ufmt.Sprintf(\"# %s\\n\\n\", m.Title)\n\toutput += \"# pages\\n\\n\"\n\n\tfor _, page := range m.GetPages() {\n\t\tif u := users.GetUserByAddress(page.Author); u != nil {\n\t\t\toutput += ufmt.Sprintf(\"- [%s (%s)](%s%s)\\n\", u.Name, page.Author.String(), m.Prefix, page.Author.String())\n\t\t} else {\n\t\t\toutput += ufmt.Sprintf(\"- [%s](%s%s)\\n\", page.Author.String(), m.Prefix, page.Author.String())\n\t\t}\n\t}\n\n\treturn output\n}\n\nfunc renderUser(user string) string {\n\tsilo, found := m.Pages.Get(user)\n\tif !found {\n\t\treturn \"404\" // StatusNotFound\n\t}\n\n\treturn PageToString((silo.(*microblog.Page)))\n}\n\nfunc Render(path string) string {\n\tparts := strings.Split(path, \"/\")\n\n\tisHome := path == \"\"\n\tisUser := len(parts) == 1\n\n\tswitch {\n\tcase isHome:\n\t\treturn renderHome()\n\n\tcase isUser:\n\t\treturn renderUser(parts[0])\n\t}\n\n\treturn \"404\" // StatusNotFound\n}\n\nfunc PageToString(p *microblog.Page) string {\n\to := \"\"\n\tif u := users.GetUserByAddress(p.Author); u != nil {\n\t\to += ufmt.Sprintf(\"# [%s](/r/demo/users:%s)\\n\\n\", u, u)\n\t\to += ufmt.Sprintf(\"%s\\n\\n\", u.Profile)\n\t}\n\to += ufmt.Sprintf(\"## [%s](/r/demo/microblog:%s)\\n\\n\", p.Author, p.Author)\n\n\to += ufmt.Sprintf(\"joined %s, last updated %s\\n\\n\", p.CreatedAt.Format(\"2006-02-01\"), p.LastPosted.Format(\"2006-02-01\"))\n\to += \"## feed\\n\\n\"\n\tfor _, u := range p.GetPosts() {\n\t\to += u.String() + \"\\n\\n\"\n\t}\n\treturn o\n}\n\n// NewPost takes a single argument (post markdown) and\n// adds a post to the address of the caller.\nfunc NewPost(text string) string {\n\tif err := m.NewPost(text); err != nil {\n\t\treturn \"unable to add new post\"\n\t}\n\treturn \"added new post\"\n}\n\nfunc Register(name, profile string) string {\n\tcaller := std.GetOrigCaller() // main\n\tusers.Register(caller, name, profile)\n\treturn \"OK\"\n}\n" @@ -3807,10 +3495,6 @@ "name": "README.md", "body": "NFT's are all the rage these days, for various reasons.\n\nI read over EIP-721 which appears to be the de-facto NFT standard on Ethereum. Then, made a sample implementation of EIP-721 (let's here called GRC-721). The implementation isn't complete, but it demonstrates the main functionality.\n\n- [EIP-721](https://eips.ethereum.org/EIPS/eip-721)\n- [gno.land/r/demo/nft/nft.go](https://gno.land/r/demo/nft/nft.go)\n- [zrealm_nft3.go test](https://github.com/gnolang/gno/blob/master/tests/files2/zrealm_nft3.gno)\n\nIn short, this demonstrates how to implement Ethereum contract interfaces in gno.land; by using only standard Go language features.\n\nPlease leave a comment ([guide](https://gno.land/r/demo/boards:gnolang/1)).\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/nft\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/grc/grc721 v0.0.0-latest\n)\n" - }, { "name": "nft.gno", "body": "package nft\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/grc/grc721\"\n)\n\ntype token struct {\n\tgrc721.IGRC721 // implements the GRC721 interface\n\n\ttokenCounter int\n\ttokens avl.Tree // grc721.TokenID -\u003e *NFToken{}\n\toperators avl.Tree // owner std.Address -\u003e operator std.Address\n}\n\ntype NFToken struct {\n\towner std.Address\n\tapproved std.Address\n\ttokenID grc721.TokenID\n\tdata string\n}\n\nvar gToken = \u0026token{}\n\nfunc GetToken() *token { return gToken }\n\nfunc (grc *token) nextTokenID() grc721.TokenID {\n\tgrc.tokenCounter++\n\ts := strconv.Itoa(grc.tokenCounter)\n\treturn grc721.TokenID(s)\n}\n\nfunc (grc *token) getToken(tid grc721.TokenID) (*NFToken, bool) {\n\ttoken, ok := grc.tokens.Get(string(tid))\n\tif !ok {\n\t\treturn nil, false\n\t}\n\treturn token.(*NFToken), true\n}\n\nfunc (grc *token) Mint(to std.Address, data string) grc721.TokenID {\n\ttid := grc.nextTokenID()\n\tgrc.tokens.Set(string(tid), \u0026NFToken{\n\t\towner: to,\n\t\ttokenID: tid,\n\t\tdata: data,\n\t})\n\treturn tid\n}\n\nfunc (grc *token) BalanceOf(owner std.Address) (count int64) {\n\tpanic(\"not yet implemented\")\n}\n\nfunc (grc *token) OwnerOf(tid grc721.TokenID) std.Address {\n\ttoken, ok := grc.getToken(tid)\n\tif !ok {\n\t\tpanic(\"token does not exist\")\n\t}\n\treturn token.owner\n}\n\n// XXX not fully implemented yet.\nfunc (grc *token) SafeTransferFrom(from, to std.Address, tid grc721.TokenID) {\n\tgrc.TransferFrom(from, to, tid)\n\t// When transfer is complete, this function checks if `_to` is a smart\n\t// contract (code size \u003e 0). If so, it calls `onERC721Received` on\n\t// `_to` and throws if the return value is not\n\t// `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`.\n\t// XXX ensure \"to\" is a realm with onERC721Received() signature.\n}\n\nfunc (grc *token) TransferFrom(from, to std.Address, tid grc721.TokenID) {\n\tcaller := std.GetCallerAt(2)\n\ttoken, ok := grc.getToken(tid)\n\t// Throws if `_tokenId` is not a valid NFT.\n\tif !ok {\n\t\tpanic(\"token does not exist\")\n\t}\n\t// Throws unless `msg.sender` is the current owner, an authorized\n\t// operator, or the approved address for this NFT.\n\tif caller != token.owner \u0026\u0026 caller != token.approved {\n\t\toperator, ok := grc.operators.Get(token.owner.String())\n\t\tif !ok || caller != operator.(std.Address) {\n\t\t\tpanic(\"unauthorized\")\n\t\t}\n\t}\n\t// Throws if `_from` is not the current owner.\n\tif from != token.owner {\n\t\tpanic(\"from is not the current owner\")\n\t}\n\t// Throws if `_to` is the zero address.\n\tif to == \"\" {\n\t\tpanic(\"to cannot be empty\")\n\t}\n\t// Good.\n\ttoken.owner = to\n}\n\nfunc (grc *token) Approve(approved std.Address, tid grc721.TokenID) {\n\tcaller := std.GetCallerAt(2)\n\ttoken, ok := grc.getToken(tid)\n\t// Throws if `_tokenId` is not a valid NFT.\n\tif !ok {\n\t\tpanic(\"token does not exist\")\n\t}\n\t// Throws unless `msg.sender` is the current owner,\n\t// or an authorized operator.\n\tif caller != token.owner {\n\t\toperator, ok := grc.operators.Get(token.owner.String())\n\t\tif !ok || caller != operator.(std.Address) {\n\t\t\tpanic(\"unauthorized\")\n\t\t}\n\t}\n\t// Good.\n\ttoken.approved = approved\n}\n\n// XXX make it work for set of operators.\nfunc (grc *token) SetApprovalForAll(operator std.Address, approved bool) {\n\tcaller := std.GetCallerAt(2)\n\tgrc.operators.Set(caller.String(), operator)\n}\n\nfunc (grc *token) GetApproved(tid grc721.TokenID) std.Address {\n\ttoken, ok := grc.getToken(tid)\n\t// Throws if `_tokenId` is not a valid NFT.\n\tif !ok {\n\t\tpanic(\"token does not exist\")\n\t}\n\treturn token.approved\n}\n\n// XXX make it work for set of operators\nfunc (grc *token) IsApprovedForAll(owner, operator std.Address) bool {\n\toperator2, ok := grc.operators.Get(owner.String())\n\tif !ok {\n\t\treturn false\n\t}\n\treturn operator == operator2.(std.Address)\n}\n" @@ -3869,10 +3553,6 @@ "name": "example.gno", "body": "// this package demonstrates a way to manage contract releases.\npackage releases_example\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/releases\"\n)\n\nvar (\n\tchangelog = releases.NewChangelog(\"example_app\")\n\tadmin = std.Address(\"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj\") // @administrator\n)\n\nfunc init() {\n\t// FIXME: admin = std.GetCreator()\n}\n\nfunc NewRelease(name, url, notes string) {\n\tcaller := std.GetOrigCaller()\n\tif caller != admin {\n\t\tpanic(\"restricted area\")\n\t}\n\tchangelog.NewRelease(name, url, notes)\n}\n\nfunc UpdateAdmin(address std.Address) {\n\tcaller := std.GetOrigCaller()\n\tif caller != admin {\n\t\tpanic(\"restricted area\")\n\t}\n\tadmin = address\n}\n\nfunc Render(path string) string {\n\treturn changelog.Render(path)\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/releases_example\n\nrequire gno.land/p/demo/releases v0.0.0-latest\n" - }, { "name": "releases0_filetest.gno", "body": "package main\n\nimport (\n\t\"gno.land/p/demo/releases\"\n)\n\nfunc main() {\n\tprintln(\"-----------\")\n\tchangelog := releases.NewChangelog(\"example\")\n\tprint(changelog.Render(\"\"))\n\n\tprintln(\"-----------\")\n\tchangelog.NewRelease(\"v1\", \"r/blahblah\", \"* initial version\")\n\tprint(changelog.Render(\"\"))\n\n\tprintln(\"-----------\")\n\tchangelog.NewRelease(\"v2\", \"r/blahblah2\", \"* various improvements\\n* new shiny logo\")\n\tprint(changelog.Render(\"\"))\n\n\tprintln(\"-----------\")\n\tprint(changelog.Latest().Render())\n}\n\n// Output:\n// -----------\n// # example\n//\n// -----------\n// # example\n//\n// ## [example v1 (latest)](r/blahblah)\n//\n// * initial version\n//\n// -----------\n// # example\n//\n// ## [example v2 (latest)](r/blahblah2)\n//\n// * various improvements\n// * new shiny logo\n//\n// ## [example v1](r/blahblah)\n//\n// * initial version\n//\n// -----------\n// ## [example v2 (latest)](r/blahblah2)\n//\n// * various improvements\n// * new shiny logo\n" @@ -3907,10 +3587,6 @@ "name": "tamagotchi", "path": "gno.land/r/demo/tamagotchi", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/tamagotchi\n\nrequire (\n\tgno.land/p/demo/tamagotchi v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "realm.gno", "body": "package tamagotchi\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/tamagotchi\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\nvar t *tamagotchi.Tamagotchi\n\nfunc init() {\n\tReset(\"gnome#0\")\n}\n\nfunc Reset(optionalName string) string {\n\tname := optionalName\n\tif name == \"\" {\n\t\theight := std.GetHeight()\n\t\tname = ufmt.Sprintf(\"gnome#%d\", height)\n\t}\n\n\tt = tamagotchi.New(name)\n\n\treturn ufmt.Sprintf(\"A new tamagotchi is born. Their name is %s %s.\", t.Name(), t.Face())\n}\n\nfunc Feed() string {\n\tt.Feed()\n\treturn t.Markdown()\n}\n\nfunc Play() string {\n\tt.Play()\n\treturn t.Markdown()\n}\n\nfunc Heal() string {\n\tt.Heal()\n\treturn t.Markdown()\n}\n\nfunc Render(path string) string {\n\ttama := t.Markdown()\n\tlinks := `Actions:\n* [Feed](/r/demo/tamagotchi?help\u0026__func=Feed)\n* [Play](/r/demo/tamagotchi?help\u0026__func=Play)\n* [Heal](/r/demo/tamagotchi?help\u0026__func=Heal)\n* [Reset](/r/demo/tamagotchi?help\u0026__func=Reset)\n`\n\n\treturn tama + \"\\n\\n\" + links\n}\n" @@ -3948,10 +3624,6 @@ { "name": "crossrealm.gno", "body": "package crossrealm\n\nimport (\n\t\"gno.land/p/demo/tests/p_crossrealm\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype LocalStruct struct {\n\tA int\n}\n\nfunc (ls *LocalStruct) String() string {\n\treturn ufmt.Sprintf(\"LocalStruct{%d}\", ls.A)\n}\n\n// local is saved locally in this realm\nvar local *LocalStruct\n\nfunc init() {\n\tlocal = \u0026LocalStruct{A: 123}\n}\n\n// Make1 returns a local object wrapped by a p struct\nfunc Make1() *p_crossrealm.Container {\n\treturn \u0026p_crossrealm.Container{\n\t\tA: 1,\n\t\tB: local,\n\t}\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/tests/crossrealm\n\nrequire (\n\tgno.land/p/demo/tests/p_crossrealm v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" } ] }, @@ -3982,10 +3654,6 @@ { "name": "foo.gno", "body": "package tests_foo\n\nimport (\n\t\"gno.land/r/demo/tests\"\n)\n\n// for testing gno.land/r/demo/tests/interfaces.go\n\ntype FooStringer struct {\n\tFieldA string\n}\n\nfunc (fs *FooStringer) String() string {\n\treturn \"\u0026FooStringer{\" + fs.FieldA + \"}\"\n}\n\nfunc AddFooStringer(fa string) {\n\ttests.AddStringer(\u0026FooStringer{fa})\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/demo/tests_foo\n\nrequire gno.land/r/demo/tests v0.0.0-latest\n" } ] }, @@ -4013,10 +3681,6 @@ "name": "todolistrealm", "path": "gno.land/r/demo/todolist", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/todolist\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/seqid v0.0.0-latest\n\tgno.land/p/demo/todolist v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "todolist.gno", "body": "package todolistrealm\n\nimport (\n\t\"bytes\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/seqid\"\n\t\"gno.land/p/demo/todolist\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// State variables\nvar (\n\ttodolistTree *avl.Tree\n\ttlid seqid.ID\n)\n\n// Constructor\nfunc init() {\n\ttodolistTree = avl.NewTree()\n}\n\nfunc NewTodoList(title string) (int, string) {\n\t// Create new Todolist\n\ttl := todolist.NewTodoList(title)\n\t// Update AVL tree with new state\n\ttlid.Next()\n\ttodolistTree.Set(strconv.Itoa(int(tlid)), tl)\n\treturn int(tlid), \"created successfully\"\n}\n\nfunc AddTask(todolistID int, title string) string {\n\t// Get Todolist from AVL tree\n\ttl, ok := todolistTree.Get(strconv.Itoa(todolistID))\n\tif !ok {\n\t\tpanic(\"Todolist not found\")\n\t}\n\n\t// get the number of tasks in the todolist\n\tid := tl.(*todolist.TodoList).Tasks.Size()\n\n\t// create the task\n\ttask := todolist.NewTask(title)\n\n\t// Cast raw data from tree into Todolist struct\n\ttl.(*todolist.TodoList).AddTask(id, task)\n\n\treturn \"task added successfully\"\n}\n\nfunc ToggleTaskStatus(todolistID int, taskID int) string {\n\t// Get Todolist from AVL tree\n\ttl, ok := todolistTree.Get(strconv.Itoa(todolistID))\n\tif !ok {\n\t\tpanic(\"Todolist not found\")\n\t}\n\n\t// Get the task from the todolist\n\ttask, found := tl.(*todolist.TodoList).Tasks.Get(strconv.Itoa(taskID))\n\tif !found {\n\t\tpanic(\"Task not found\")\n\t}\n\n\t// Change the status of the task\n\ttodolist.ToggleTaskStatus(task.(*todolist.Task))\n\n\treturn \"task status changed successfully\"\n}\n\nfunc RemoveTask(todolistID int, taskID int) string {\n\t// Get Todolist from AVL tree\n\ttl, ok := todolistTree.Get(strconv.Itoa(todolistID))\n\tif !ok {\n\t\tpanic(\"Todolist not found\")\n\t}\n\n\t// Get the task from the todolist\n\t_, ok = tl.(*todolist.TodoList).Tasks.Get(strconv.Itoa(taskID))\n\tif !ok {\n\t\tpanic(\"Task not found\")\n\t}\n\n\t// Change the status of the task\n\ttl.(*todolist.TodoList).RemoveTask(strconv.Itoa(taskID))\n\n\treturn \"task status changed successfully\"\n}\n\nfunc RemoveTodoList(todolistID int) string {\n\t// Get Todolist from AVL tree\n\t_, ok := todolistTree.Get(strconv.Itoa(todolistID))\n\tif !ok {\n\t\tpanic(\"Todolist not found\")\n\t}\n\n\t// Remove the todolist\n\ttodolistTree.Remove(strconv.Itoa(todolistID))\n\n\treturn \"Todolist removed successfully\"\n}\n\nfunc Render(path string) string {\n\tif path == \"\" {\n\t\treturn renderHomepage()\n\t}\n\n\treturn \"unknown page\"\n}\n\nfunc renderHomepage() string {\n\t// Define empty buffer\n\tvar b bytes.Buffer\n\n\tb.WriteString(\"# Welcome to ToDolist\\n\\n\")\n\n\t// If no todolists have been created\n\tif todolistTree.Size() == 0 {\n\t\tb.WriteString(\"### No todolists available currently!\")\n\t\treturn b.String()\n\t}\n\n\t// Iterate through AVL tree\n\ttodolistTree.Iterate(\"\", \"\", func(key string, value interface{}) bool {\n\t\t// cast raw data from tree into Todolist struct\n\t\ttl := value.(*todolist.TodoList)\n\n\t\t// Add Todolist name\n\t\tb.WriteString(\n\t\t\tufmt.Sprintf(\n\t\t\t\t\"## Todolist #%s: %s\\n\",\n\t\t\t\tkey, // Todolist ID\n\t\t\t\ttl.GetTodolistTitle(),\n\t\t\t),\n\t\t)\n\n\t\t// Add Todolist owner\n\t\tb.WriteString(\n\t\t\tufmt.Sprintf(\n\t\t\t\t\"#### Todolist owner : %s\\n\",\n\t\t\t\ttl.GetTodolistOwner(),\n\t\t\t),\n\t\t)\n\n\t\t// List all todos that are currently Todolisted\n\t\tif todos := tl.GetTasks(); len(todos) \u003e 0 {\n\t\t\tb.WriteString(\n\t\t\t\tufmt.Sprintf(\"Currently Todo tasks: %d\\n\\n\", len(todos)),\n\t\t\t)\n\n\t\t\tfor index, todo := range todos {\n\t\t\t\tb.WriteString(\n\t\t\t\t\tufmt.Sprintf(\"#%d - %s \", index, todo.Title),\n\t\t\t\t)\n\t\t\t\t// displays a checked box if task is marked as done, an empty box if not\n\t\t\t\tif todo.Done {\n\t\t\t\t\tb.WriteString(\n\t\t\t\t\t\t\"☑\\n\\n\",\n\t\t\t\t\t)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tb.WriteString(\n\t\t\t\t\t\"☐\\n\\n\",\n\t\t\t\t)\n\t\t\t}\n\t\t} else {\n\t\t\tb.WriteString(\"No tasks in this list currently\\n\")\n\t\t}\n\n\t\tb.WriteString(\"\\n\")\n\t\treturn false\n\t})\n\n\treturn b.String()\n}\n" @@ -4051,10 +3715,6 @@ "name": "types", "path": "gno.land/r/demo/types", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/types\n\nrequire gno.land/p/demo/avl v0.0.0-latest\n" - }, { "name": "types.gno", "body": "// package to test types behavior in various conditions (TXs, imports).\npackage types\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\nvar (\n\tgInt int = -42\n\tgUint uint = 42\n\tgString string = \"a string\"\n\tgStringSlice []string = []string{\"a\", \"string\", \"slice\"}\n\tgError error = errors.New(\"an error\")\n\tgIntSlice []int = []int{-42, 0, 42}\n\tgUintSlice []uint = []uint{0, 42, 84}\n\tgTree avl.Tree\n\t// gInterface = interface{}{-42, \"a string\", uint(42)}\n)\n\nfunc init() {\n\tgTree.Set(\"a\", \"content of A\")\n\tgTree.Set(\"b\", \"content of B\")\n}\n\nfunc Noop() {}\nfunc RetTimeNow() time.Time { return time.Now() }\nfunc RetString() string { return gString }\nfunc RetStringPointer() *string { return \u0026gString }\nfunc RetUint() uint { return gUint }\nfunc RetInt() int { return gInt }\nfunc RetUintPointer() *uint { return \u0026gUint }\nfunc RetIntPointer() *int { return \u0026gInt }\nfunc RetTree() avl.Tree { return gTree }\nfunc RetIntSlice() []int { return gIntSlice }\nfunc RetUintSlice() []uint { return gUintSlice }\nfunc RetStringSlice() []string { return gStringSlice }\nfunc RetError() error { return gError }\nfunc Panic() { panic(\"PANIC!\") }\n\n// TODO: floats\n// TODO: typed errors\n// TODO: ret interface\n// TODO: recover\n// TODO: take types as input\n\nfunc Render(path string) string {\n\treturn \"package to test data types.\"\n}\n" @@ -4089,10 +3749,6 @@ "name": "ui", "path": "gno.land/r/demo/ui", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/ui\n\nrequire gno.land/p/demo/ui v0.0.0-latest\n" - }, { "name": "ui.gno", "body": "package ui\n\nimport \"gno.land/p/demo/ui\"\n\nfunc Render(path string) string {\n\t// TODO: build this realm as a demo one with one page per feature.\n\n\t// TODO: pagination\n\t// TODO: non-standard markdown\n\t// TODO: error, warn\n\t// TODO: header\n\t// TODO: HTML\n\t// TODO: toc\n\t// TODO: forms\n\t// TODO: comments\n\n\tdom := ui.DOM{\n\t\tPrefix: \"r/demo/ui:\",\n\t}\n\n\tdom.Title = \"UI Demo\"\n\n\tdom.Header.Append(ui.Breadcrumb{\n\t\tui.Link{Text: \"foo\", Path: \"foo\"},\n\t\tui.Link{Text: \"bar\", Path: \"foo/bar\"},\n\t})\n\n\tdom.Body.Append(\n\t\tui.Paragraph(\"Simple UI demonstration.\"),\n\t\tui.BulletList{\n\t\t\tui.Text(\"a text\"),\n\t\t\tui.Link{Text: \"a relative link\", Path: \"foobar\"},\n\t\t\tui.Text(\"another text\"),\n\t\t\t// ui.H1(\"a H1 text\"),\n\t\t\tui.Bold(\"a bold text\"),\n\t\t\tui.Italic(\"italic text\"),\n\t\t\tui.Text(\"raw markdown with **bold** text in the middle.\"),\n\t\t\tui.Code(\"some inline code\"),\n\t\t\tui.Link{Text: \"a remote link\", URL: \"https://gno.land\"},\n\t\t},\n\t)\n\n\tdom.Footer.Append(ui.Text(\"I'm the footer.\"))\n\tdom.Body.Append(ui.Text(\"another string.\"))\n\tdom.Body.Append(ui.Paragraph(\"a paragraph.\"), ui.HR{})\n\n\treturn dom.String()\n}\n" @@ -4127,10 +3783,6 @@ "name": "userbook", "path": "gno.land/r/demo/userbook", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/userbook\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/mux v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "userbook.gno", "body": "// This realm demonstrates a small userbook system working with gnoweb\npackage userbook\n\nimport (\n\t\"std\"\n\t\"strconv\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/mux\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype Signup struct {\n\taccount string\n\theight int64\n}\n\n// signups - keep a slice of signed up addresses efficient pagination\nvar signups []Signup\n\n// tracker - keep track of who signed up\nvar (\n\ttracker *avl.Tree\n\trouter *mux.Router\n)\n\nconst (\n\tdefaultPageSize = 20\n\tpathArgument = \"number\"\n\tsubPath = \"page/{\" + pathArgument + \"}\"\n\tsignUpEvent = \"SignUp\"\n)\n\nfunc init() {\n\t// Set up tracker tree\n\ttracker = avl.NewTree()\n\n\t// Set up route handling\n\trouter = mux.NewRouter()\n\trouter.HandleFunc(\"\", renderHelper)\n\trouter.HandleFunc(subPath, renderHelper)\n\n\t// Sign up the deployer\n\tSignUp()\n}\n\nfunc SignUp() string {\n\t// Get transaction caller\n\tcaller := std.PrevRealm().Addr().String()\n\theight := std.GetHeight()\n\n\t// Check if the user is already signed up\n\tif _, exists := tracker.Get(caller); exists {\n\t\tpanic(caller + \" is already signed up!\")\n\t}\n\n\t// Sign up the user\n\ttracker.Set(caller, struct{}{})\n\tsignup := Signup{\n\t\tcaller,\n\t\theight,\n\t}\n\n\tsignups = append(signups, signup)\n\tstd.Emit(signUpEvent, \"SignedUpAccount\", signup.account)\n\n\treturn ufmt.Sprintf(\"%s added to userbook up at block #%d!\", signup.account, signup.height)\n}\n\nfunc GetSignupsInRange(page, pageSize int) ([]Signup, int) {\n\tif page \u003c 1 {\n\t\tpanic(\"page number cannot be less than 1\")\n\t}\n\n\tif pageSize \u003c 1 || pageSize \u003e 50 {\n\t\tpanic(\"page size must be from 1 to 50\")\n\t}\n\n\t// Pagination\n\t// Calculate indexes\n\tstartIndex := (page - 1) * pageSize\n\tendIndex := startIndex + pageSize\n\n\t// If page does not contain any users\n\tif startIndex \u003e= len(signups) {\n\t\treturn nil, -1\n\t}\n\n\t// If page contains fewer users than the page size\n\tif endIndex \u003e len(signups) {\n\t\tendIndex = len(signups)\n\t}\n\n\treturn signups[startIndex:endIndex], endIndex\n}\n\nfunc renderHelper(res *mux.ResponseWriter, req *mux.Request) {\n\ttotalSignups := len(signups)\n\tres.Write(\"# Welcome to UserBook!\\n\\n\")\n\n\t// Get URL parameter\n\tpage, err := strconv.Atoi(req.GetVar(\"number\"))\n\tif err != nil {\n\t\tpage = 1 // render first page on bad input\n\t}\n\n\t// Fetch paginated signups\n\tfetchedSignups, endIndex := GetSignupsInRange(page, defaultPageSize)\n\t// Handle empty page case\n\tif len(fetchedSignups) == 0 {\n\t\tres.Write(\"No users on this page!\\n\\n\")\n\t\tres.Write(\"---\\n\\n\")\n\t\tres.Write(\"[Back to Page #1](/r/demo/userbook:page/1)\\n\\n\")\n\t\treturn\n\t}\n\n\t// Write page title\n\tres.Write(ufmt.Sprintf(\"## UserBook - Page #%d:\\n\\n\", page))\n\n\t// Write signups\n\tpageStartIndex := defaultPageSize * (page - 1)\n\tfor i, signup := range fetchedSignups {\n\t\tout := ufmt.Sprintf(\"#### User #%d - %s - signed up at Block #%d\\n\", pageStartIndex+i, signup.account, signup.height)\n\t\tres.Write(out)\n\t}\n\n\tres.Write(\"---\\n\\n\")\n\n\t// Write UserBook info\n\tlatestSignupIndex := totalSignups - 1\n\tres.Write(ufmt.Sprintf(\"#### Total users: %d\\n\", totalSignups))\n\tres.Write(ufmt.Sprintf(\"#### Latest signup: User #%d at Block #%d\\n\", latestSignupIndex, signups[latestSignupIndex].height))\n\n\tres.Write(\"---\\n\\n\")\n\n\t// Write page number\n\tres.Write(ufmt.Sprintf(\"You're viewing page #%d\", page))\n\n\t// Write navigation buttons\n\tvar prevPage string\n\tvar nextPage string\n\t// If we are on any page that is not the first page\n\tif page \u003e 1 {\n\t\tprevPage = ufmt.Sprintf(\" - [Previous page](/r/demo/userbook:page/%d)\", page-1)\n\t}\n\n\t// If there are more pages after the current one\n\tif endIndex \u003c totalSignups {\n\t\tnextPage = ufmt.Sprintf(\" - [Next page](/r/demo/userbook:page/%d)\\n\\n\", page+1)\n\t}\n\n\tres.Write(prevPage)\n\tres.Write(nextPage)\n}\n\nfunc Render(path string) string {\n\treturn router.Render(path)\n}\n" @@ -4165,10 +3817,6 @@ "name": "wugnot", "path": "gno.land/r/demo/wugnot", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/demo/wugnot\n\nrequire (\n\tgno.land/p/demo/grc/grc20 v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/users v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" - }, { "name": "wugnot.gno", "body": "package wugnot\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/grc/grc20\"\n\t\"gno.land/p/demo/ufmt\"\n\tpusers \"gno.land/p/demo/users\"\n\t\"gno.land/r/demo/users\"\n)\n\nvar (\n\tbanker *grc20.Banker = grc20.NewBanker(\"wrapped GNOT\", \"wugnot\", 0)\n\tToken = banker.Token()\n)\n\nconst (\n\tugnotMinDeposit uint64 = 1000\n\twugnotMinDeposit uint64 = 1\n)\n\nfunc Deposit() {\n\tcaller := std.PrevRealm().Addr()\n\tsent := std.GetOrigSend()\n\tamount := sent.AmountOf(\"ugnot\")\n\n\trequire(uint64(amount) \u003e= ugnotMinDeposit, ufmt.Sprintf(\"Deposit below minimum: %d/%d ugnot.\", amount, ugnotMinDeposit))\n\tcheckErr(banker.Mint(caller, uint64(amount)))\n}\n\nfunc Withdraw(amount uint64) {\n\trequire(amount \u003e= wugnotMinDeposit, ufmt.Sprintf(\"Deposit below minimum: %d/%d wugnot.\", amount, wugnotMinDeposit))\n\n\tcaller := std.PrevRealm().Addr()\n\tpkgaddr := std.CurrentRealm().Addr()\n\tcallerBal := Token.BalanceOf(caller)\n\trequire(amount \u003c= callerBal, ufmt.Sprintf(\"Insufficient balance: %d available, %d needed.\", callerBal, amount))\n\n\t// send swapped ugnots to qcaller\n\tstdBanker := std.GetBanker(std.BankerTypeRealmSend)\n\tsend := std.Coins{{\"ugnot\", int64(amount)}}\n\tstdBanker.SendCoins(pkgaddr, caller, send)\n\tcheckErr(banker.Burn(caller, amount))\n}\n\nfunc Render(path string) string {\n\tparts := strings.Split(path, \"/\")\n\tc := len(parts)\n\n\tswitch {\n\tcase path == \"\":\n\t\treturn banker.RenderHome()\n\tcase c == 2 \u0026\u0026 parts[0] == \"balance\":\n\t\towner := std.Address(parts[1])\n\t\tbalance := Token.BalanceOf(owner)\n\t\treturn ufmt.Sprintf(\"%d\", balance)\n\tdefault:\n\t\treturn \"404\"\n\t}\n}\n\nfunc TotalSupply() uint64 { return Token.TotalSupply() }\n\nfunc BalanceOf(owner pusers.AddressOrName) uint64 {\n\townerAddr := users.Resolve(owner)\n\treturn Token.BalanceOf(ownerAddr)\n}\n\nfunc Allowance(owner, spender pusers.AddressOrName) uint64 {\n\townerAddr := users.Resolve(owner)\n\tspenderAddr := users.Resolve(spender)\n\treturn Token.Allowance(ownerAddr, spenderAddr)\n}\n\nfunc Transfer(to pusers.AddressOrName, amount uint64) {\n\ttoAddr := users.Resolve(to)\n\tcheckErr(Token.Transfer(toAddr, amount))\n}\n\nfunc Approve(spender pusers.AddressOrName, amount uint64) {\n\tspenderAddr := users.Resolve(spender)\n\tcheckErr(Token.Approve(spenderAddr, amount))\n}\n\nfunc TransferFrom(from, to pusers.AddressOrName, amount uint64) {\n\tfromAddr := users.Resolve(from)\n\ttoAddr := users.Resolve(to)\n\tcheckErr(Token.TransferFrom(fromAddr, toAddr, amount))\n}\n\nfunc require(condition bool, msg string) {\n\tif !condition {\n\t\tpanic(msg)\n\t}\n}\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" @@ -4207,10 +3855,6 @@ "name": "admin.gno", "body": "package gnoblog\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n\t\"gno.land/p/demo/context\"\n\t\"gno.land/p/gov/proposal\"\n)\n\nvar (\n\tadminAddr std.Address\n\tmoderatorList avl.Tree\n\tcommenterList avl.Tree\n\tinPause bool\n)\n\nfunc init() {\n\t// adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis.\n\tadminAddr = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\"\n}\n\nfunc AdminSetAdminAddr(addr std.Address) {\n\tassertIsAdmin()\n\tadminAddr = addr\n}\n\nfunc AdminSetInPause(state bool) {\n\tassertIsAdmin()\n\tinPause = state\n}\n\nfunc AdminAddModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), true)\n}\n\nfunc AdminRemoveModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), false) // FIXME: delete instead?\n}\n\nfunc DaoAddPost(ctx context.Context, slug, title, body, publicationDate, authors, tags string) {\n\tproposal.AssertContextApprovedByGovDAO(ctx)\n\tcaller := std.DerivePkgAddr(\"gno.land/r/gov/dao\")\n\taddPost(caller, slug, title, body, publicationDate, authors, tags)\n}\n\nfunc ModAddPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\tcaller := std.GetOrigCaller()\n\taddPost(caller, slug, title, body, publicationDate, authors, tags)\n}\n\nfunc addPost(caller std.Address, slug, title, body, publicationDate, authors, tags string) {\n\tvar tagList []string\n\tif tags != \"\" {\n\t\ttagList = strings.Split(tags, \",\")\n\t}\n\tvar authorList []string\n\tif authors != \"\" {\n\t\tauthorList = strings.Split(authors, \",\")\n\t}\n\n\terr := b.NewPost(caller, slug, title, body, publicationDate, authorList, tagList)\n\n\tcheckErr(err)\n}\n\nfunc ModEditPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.GetPost(slug).Update(title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc ModRemovePost(slug string) {\n\tassertIsModerator()\n\n\tb.RemovePost(slug)\n}\n\nfunc ModAddCommenter(addr std.Address) {\n\tassertIsModerator()\n\tcommenterList.Set(addr.String(), true)\n}\n\nfunc ModDelCommenter(addr std.Address) {\n\tassertIsModerator()\n\tcommenterList.Set(addr.String(), false) // FIXME: delete instead?\n}\n\nfunc ModDelComment(slug string, index int) {\n\tassertIsModerator()\n\n\terr := b.GetPost(slug).DeleteComment(index)\n\tcheckErr(err)\n}\n\nfunc isAdmin(addr std.Address) bool {\n\treturn addr == adminAddr\n}\n\nfunc isModerator(addr std.Address) bool {\n\t_, found := moderatorList.Get(addr.String())\n\treturn found\n}\n\nfunc isCommenter(addr std.Address) bool {\n\t_, found := commenterList.Get(addr.String())\n\treturn found\n}\n\nfunc assertIsAdmin() {\n\tcaller := std.GetOrigCaller()\n\tif !isAdmin(caller) {\n\t\tpanic(\"access restricted.\")\n\t}\n}\n\nfunc assertIsModerator() {\n\tcaller := std.GetOrigCaller()\n\tif isAdmin(caller) || isModerator(caller) {\n\t\treturn\n\t}\n\tpanic(\"access restricted\")\n}\n\nfunc assertIsCommenter() {\n\tcaller := std.GetOrigCaller()\n\tif isAdmin(caller) || isModerator(caller) || isCommenter(caller) {\n\t\treturn\n\t}\n\tpanic(\"access restricted\")\n}\n\nfunc assertNotInPause() {\n\tif inPause {\n\t\tpanic(\"access restricted (pause)\")\n\t}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/gnoland/blog\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/blog v0.0.0-latest\n\tgno.land/p/demo/context v0.0.0-latest\n\tgno.land/p/gov/proposal v0.0.0-latest\n)\n" - }, { "name": "gnoblog.gno", "body": "package gnoblog\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/blog\"\n)\n\nvar b = \u0026blog.Blog{\n\tTitle: \"Gnoland's Blog\",\n\tPrefix: \"/r/gnoland/blog:\",\n}\n\nfunc AddComment(postSlug, comment string) {\n\tassertIsCommenter()\n\tassertNotInPause()\n\n\tcaller := std.GetOrigCaller()\n\terr := b.GetPost(postSlug).AddComment(caller, comment)\n\tcheckErr(err)\n}\n\nfunc Render(path string) string {\n\treturn b.Render(path)\n}\n\nfunc RenderLastPostsWidget(limit int) string {\n\treturn b.RenderLastPostsWidget(limit)\n}\n\nfunc PostExists(slug string) bool {\n\tif b.GetPost(slug) == nil {\n\t\treturn false\n\t}\n\treturn true\n}\n" @@ -4256,10 +3900,6 @@ { "name": "events_filetest.gno", "body": "package main\n\nimport \"gno.land/r/gnoland/events\"\n\nfunc main() {\n\tprintln(events.Render(\"\"))\n}\n\n// Output:\n// \u003cmain class='gno-tmpl-section'\u003e\n//\n// # Gno.land Core Team Attends Industry Events \u0026 Meetups\n//\n//\n// If you’re interested in building web3 with us, catch up with gno.land in person at one of our industry events. We’re looking to connect with developers and like-minded thinkers who can contribute to the growth of our platform.\n//\n//\n// ---\n//\n// ## Upcoming Events\n//\n// \u003cdiv class=\"columns-3\"\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### GopherCon EU\n// - Come Meet Us at our Booth\n// - Berlin, June 17 - 20, 2024\n//\n// [Learn More](https://gophercon.eu/)\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### GopherCon US\n// - Come Meet Us at our Booth\n// - Chicago, July 7 - 10, 2024\n//\n// [Learn More](https://www.gophercon.com/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Nebular Summit\n// - Join our workshop\n// - Brussels, July 12 - 13, 2024\n//\n// [Learn More](https://nebular.builders/)\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003c/div\u003e\u003c!-- end columns-3--\u003e\n//\n// ---\n//\n// ## Past Events\n//\n// \u003cdiv class=\"columns-3\"\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Gno @ Golang Serbia\n//\n// - **Join the meetup**\n// - Belgrade, May 23, 2024\n//\n// [Learn more](https://gno.land/r/gnoland/blog:p/gnomes-in-serbia)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Intro to Gno Tokyo\n//\n// - **Join the meetup**\n// - Tokyo, April 11, 2024\n//\n// [Learn more](https://gno.land/r/gnoland/blog:p/gno-tokyo)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Go to Gno Seoul\n//\n// - **Join the workshop**\n// - Seoul, March 23, 2024\n//\n// [Learn more](https://medium.com/onbloc/go-to-gno-recap-intro-to-the-gno-stack-with-memeland-284a43d7f620)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### GopherCon US\n//\n// - **Come Meet Us at our Booth**\n// - San Diego, September 26 - 29, 2023\n//\n// [Learn more](https://www.gophercon.com/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### GopherCon EU\n//\n// - **Come Meet Us at our Booth**\n// - Berlin, July 26 - 29, 2023\n//\n// [Learn more](https://gophercon.eu/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Nebular Summit Gno.land for Developers\n//\n// - Paris, July 24 - 25, 2023\n// - Manfred Touron\n//\n// [Learn more](https://www.nebular.builders/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### EthCC\n//\n// - **Come Meet Us at our Booth**\n// - Paris, July 17 - 20, 2023\n// - Manfred Touron\n//\n// [Learn more](https://www.ethcc.io/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n//\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Eth Seoul\n//\n// - **The Evolution of Smart Contracts: A Journey into Gno.land**\n// - Seoul, June 3, 2023\n// - Manfred Touron\n//\n// [Learn more](https://2023.ethseoul.org/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### BUIDL Asia\n//\n// - **Proof of Contribution in Gno.land**\n// - Seoul, June 6, 2023\n// - Manfred Touron\n//\n// [Learn more](https://www.buidl.asia/)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Game Developer Conference\n//\n// - **Side Event: Web3 Gaming Apps Powered by Gno**\n// - San Francisco, Mach 23, 2023\n// - Jae Kwon\n//\n// [Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### EthDenver\n//\n// - **Side Event: Discover Gno.land**\n// - Denver, Feb 24 - Mar 5, 2023\n// - Jae Kwon\n//\n// [Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Istanbul Blockchain Week\n//\n// - Istanbul, Nov 14 - 17, 2022\n// - Manfred Touron\n//\n// [Watch the talk](https://www.youtube.com/watch?v=JX0gdWT0Cg4)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Web Summit Buckle Up and Build with Cosmos\n//\n// - Lisbon, Nov 1 - 4, 2022\n// - Manfred Touron\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Cosmoverse\n//\n// - Medallin, Sept 26 - 28, 2022\n// - Manfred Touron\n//\n// [Watch the talk](https://www.youtube.com/watch?v=6s1zG7hgxMk)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003cdiv class=\"column\"\u003e\n//\n// ### Berlin Blockchain Week Buckle Up and Build with Cosmos\n//\n// - Berlin, Sept 11 - 18, 2022\n//\n// [Watch the talk](https://www.youtube.com/watch?v=hCLErPgnavI)\n//\n// \u003c/div\u003e\u003c!-- end column--\u003e\n// \u003c/div\u003e\u003c!-- end columns-3--\u003e\n//\n// \u003c/main\u003e\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/gnoland/events\n\nrequire gno.land/p/demo/ui v0.0.0-latest\n" } ] }, @@ -4299,10 +3939,6 @@ "name": "faucet_test.gno", "body": "package faucet\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/r/gnoland/faucet\"\n)\n\nfunc TestPackage(t *testing.T) {\n\tvar (\n\t\tadminaddr = std.Address(\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\")\n\t\tfaucetaddr = std.DerivePkgAddr(\"gno.land/r/gnoland/faucet\")\n\t\tcontrolleraddr1 = testutils.TestAddress(\"controller1\")\n\t\tcontrolleraddr2 = testutils.TestAddress(\"controller2\")\n\t\tcontrolleraddr3 = testutils.TestAddress(\"controller3\")\n\t\tcontrolleraddr4 = testutils.TestAddress(\"controller4\")\n\t\tcontrolleraddr5 = testutils.TestAddress(\"controller5\")\n\t\tcontrolleraddr6 = testutils.TestAddress(\"controller6\")\n\t\tcontrolleraddr7 = testutils.TestAddress(\"controller7\")\n\t\tcontrolleraddr8 = testutils.TestAddress(\"controller8\")\n\t\tcontrolleraddr9 = testutils.TestAddress(\"controller9\")\n\t\tcontrolleraddr10 = testutils.TestAddress(\"controller10\")\n\t\tcontrolleraddr11 = testutils.TestAddress(\"controller11\")\n\n\t\ttest1addr = testutils.TestAddress(\"test1\")\n\t)\n\t// deposit 1000gnot to faucet contract\n\tstd.TestIssueCoins(faucetaddr, std.Coins{{\"ugnot\", 1000000000}})\n\tassertBalance(t, faucetaddr, 1200000000)\n\n\t// by default, balance is empty, and as a user I cannot call Transfer, or Admin commands.\n\n\tassertBalance(t, test1addr, 0)\n\tstd.TestSetOrigCaller(test1addr)\n\tassertErr(t, faucet.Transfer(test1addr, 1000000))\n\n\tassertErr(t, faucet.AdminAddController(controlleraddr1))\n\tstd.TestSetOrigCaller(controlleraddr1)\n\tassertErr(t, faucet.Transfer(test1addr, 1000000))\n\n\t// as an admin, add the controller to contract and deposit more 2000gnot to contract\n\tstd.TestSetOrigCaller(adminaddr)\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr1))\n\tassertBalance(t, faucetaddr, 1200000000)\n\n\t// now, send some tokens as controller.\n\tstd.TestSetOrigCaller(controlleraddr1)\n\tassertNoErr(t, faucet.Transfer(test1addr, 1000000))\n\tassertBalance(t, test1addr, 1000000)\n\tassertNoErr(t, faucet.Transfer(test1addr, 1000000))\n\tassertBalance(t, test1addr, 2000000)\n\tassertBalance(t, faucetaddr, 1198000000)\n\n\t// remove controller\n\t// as an admin, remove controller\n\tstd.TestSetOrigCaller(adminaddr)\n\tassertNoErr(t, faucet.AdminRemoveController(controlleraddr1))\n\tstd.TestSetOrigCaller(controlleraddr1)\n\tassertErr(t, faucet.Transfer(test1addr, 1000000))\n\n\t// duplicate controller\n\tstd.TestSetOrigCaller(adminaddr)\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr1))\n\tassertErr(t, faucet.AdminAddController(controlleraddr1))\n\t// add more than more than allowed controllers\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr2))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr3))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr4))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr5))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr6))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr7))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr8))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr9))\n\tassertNoErr(t, faucet.AdminAddController(controlleraddr10))\n\tassertErr(t, faucet.AdminAddController(controlleraddr11))\n\n\t// send more than per transfer limit\n\tstd.TestSetOrigCaller(adminaddr)\n\tfaucet.AdminSetTransferLimit(300000000)\n\tstd.TestSetOrigCaller(controlleraddr1)\n\tassertErr(t, faucet.Transfer(test1addr, 301000000))\n\n\t// block transefer from the address not on the controllers list.\n\tstd.TestSetOrigCaller(controlleraddr11)\n\tassertErr(t, faucet.Transfer(test1addr, 1000000))\n}\n\nfunc assertErr(t *testing.T, err string) {\n\tt.Helper()\n\n\tif err == \"\" {\n\t\tt.Logf(\"info: got err: %v\", err)\n\t\tt.Errorf(\"expected an error, got nil.\")\n\t}\n}\n\nfunc assertNoErr(t *testing.T, err string) {\n\tt.Helper()\n\tif err != \"\" {\n\t\tt.Errorf(\"got err: %v.\", err)\n\t}\n}\n\nfunc assertBalance(t *testing.T, addr std.Address, expectedBal int64) {\n\tt.Helper()\n\tbanker := std.GetBanker(std.BankerTypeReadonly)\n\tcoins := banker.GetCoins(addr)\n\tgot := coins.AmountOf(\"ugnot\")\n\n\tif expectedBal != got {\n\t\tt.Errorf(\"invalid balance: expected %d, got %d.\", expectedBal, got)\n\t}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/gnoland/faucet\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n)\n" - }, { "name": "z0_filetest.gno", "body": "package main\n\nimport (\n\t\"std\"\n\n\t\"gno.land/r/gnoland/faucet\"\n)\n\n// mints ugnot to current realm\nfunc init() {\n\tfacuetaddr := std.DerivePkgAddr(\"gno.land/r/gnoland/faucet\")\n\tstd.TestIssueCoins(facuetaddr, std.Coins{{\"ugnot\", 200000000}})\n}\n\n// assert render with empty path and no controllers\nfunc main() {\n\tprintln(faucet.Render(\"\"))\n}\n\n// Output:\n// # Community Faucet.\n//\n// Status: active.\n// Balance: 200000000ugnot.\n// Total transfers: (in 0 times).\n//\n// Package address: g1ttrq7mp4zy6dssnmgyyktnn4hcj3ys8xhju0n7\n//\n// Admin: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\n//\n// Controllers:\n//\n//\n//\n// Per request limit: 350000000ugnot\n" @@ -4357,10 +3993,6 @@ "name": "contract_test.gno", "body": "package ghverify\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n)\n\nfunc TestVerificationLifecycle(t *testing.T) {\n\tdefaultAddress := std.GetOrigCaller()\n\tuserAddress := std.Address(testutils.TestAddress(\"user\"))\n\n\t// Verify request returns no feeds.\n\tresult := GnorkleEntrypoint(\"request\")\n\tif result != \"[]\" {\n\t\tt.Fatalf(\"expected empty request result, got %s\", result)\n\t}\n\n\t// Make a verification request with the created user.\n\tstd.TestSetOrigCaller(userAddress)\n\tRequestVerification(\"deelawn\")\n\n\t// A subsequent request from the same address should panic because there is\n\t// already a feed with an ID of this user's address.\n\tvar errMsg string\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\terrMsg = r.(error).Error()\n\t\t\t}\n\t\t}()\n\t\tRequestVerification(\"deelawn\")\n\t}()\n\tif errMsg != \"feed already exists\" {\n\t\tt.Fatalf(\"expected feed already exists, got %s\", errMsg)\n\t}\n\n\t// Verify the request returns no feeds for this non-whitelisted user.\n\tresult = GnorkleEntrypoint(\"request\")\n\tif result != \"[]\" {\n\t\tt.Fatalf(\"expected empty request result, got %s\", result)\n\t}\n\n\t// Set the caller back to the whitelisted user and verify that the feed data\n\t// returned matches what should have been created by the `RequestVerification`\n\t// invocation.\n\tstd.TestSetOrigCaller(defaultAddress)\n\tresult = GnorkleEntrypoint(\"request\")\n\texpResult := `[{\"id\":\"` + string(userAddress) + `\",\"type\":\"0\",\"value_type\":\"string\",\"tasks\":[{\"gno_address\":\"` +\n\t\tstring(userAddress) + `\",\"github_handle\":\"deelawn\"}]}]`\n\tif result != expResult {\n\t\tt.Fatalf(\"expected request result %s, got %s\", expResult, result)\n\t}\n\n\t// Try to trigger feed ingestion from the non-authorized user.\n\tstd.TestSetOrigCaller(userAddress)\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\terrMsg = r.(error).Error()\n\t\t\t}\n\t\t}()\n\t\tGnorkleEntrypoint(\"ingest,\" + string(userAddress) + \",OK\")\n\t}()\n\tif errMsg != \"caller not whitelisted\" {\n\t\tt.Fatalf(\"expected caller not whitelisted, got %s\", errMsg)\n\t}\n\n\t// Set the caller back to the whitelisted user and transfer contract ownership.\n\tstd.TestSetOrigCaller(defaultAddress)\n\tSetOwner(userAddress)\n\n\t// Now trigger the feed ingestion from the user and new owner and only whitelisted address.\n\tstd.TestSetOrigCaller(userAddress)\n\tGnorkleEntrypoint(\"ingest,\" + string(userAddress) + \",OK\")\n\n\t// Verify the ingestion autocommitted the value and triggered the post handler.\n\tdata := Render(\"\")\n\texpResult = `{\"deelawn\": \"` + string(userAddress) + `\"}`\n\tif data != expResult {\n\t\tt.Fatalf(\"expected render data %s, got %s\", expResult, data)\n\t}\n\n\t// Finally make sure the feed was cleaned up after the data was committed.\n\tresult = GnorkleEntrypoint(\"request\")\n\tif result != \"[]\" {\n\t\tt.Fatalf(\"expected empty request result, got %s\", result)\n\t}\n\n\t// Check that the accessor functions are working as expected.\n\tif handle := GetHandleByAddress(string(userAddress)); handle != \"deelawn\" {\n\t\tt.Fatalf(\"expected deelawn, got %s\", handle)\n\t}\n\tif address := GetAddressByHandle(\"deelawn\"); address != string(userAddress) {\n\t\tt.Fatalf(\"expected %s, got %s\", string(userAddress), address)\n\t}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/gnoland/ghverify\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/gnorkle/feeds/static v0.0.0-latest\n\tgno.land/p/demo/gnorkle/gnorkle v0.0.0-latest\n\tgno.land/p/demo/gnorkle/message v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n)\n" - }, { "name": "task.gno", "body": "package ghverify\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n)\n\ntype verificationTask struct {\n\tgnoAddress string\n\tgithubHandle string\n}\n\n// MarshalJSON marshals the task contents to JSON.\nfunc (t *verificationTask) MarshalJSON() ([]byte, error) {\n\tbuf := new(bytes.Buffer)\n\tw := bufio.NewWriter(buf)\n\n\tw.Write(\n\t\t[]byte(`{\"gno_address\":\"` + t.gnoAddress + `\",\"github_handle\":\"` + t.githubHandle + `\"}`),\n\t)\n\n\tw.Flush()\n\treturn buf.Bytes(), nil\n}\n" @@ -4391,10 +4023,6 @@ "name": "home", "path": "gno.land/r/gnoland/home", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/gnoland/home\n\nrequire (\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/ui v0.0.0-latest\n\tgno.land/r/gnoland/blog v0.0.0-latest\n)\n" - }, { "name": "home.gno", "body": "package home\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/demo/ui\"\n\tblog \"gno.land/r/gnoland/blog\"\n)\n\n// XXX: p/demo/ui API is crappy, we need to make it more idiomatic\n// XXX: use an updatable block system to update content from a DAO\n// XXX: var blocks avl.Tree\n\nfunc Render(_ string) string {\n\tdom := ui.DOM{Prefix: \"r/gnoland/home:\"}\n\tdom.Title = \"Welcome to gno.land\"\n\tdom.Classes = []string{\"gno-tmpl-section\"}\n\n\t// body\n\tdom.Body.Append(introSection()...)\n\n\tdom.Body.Append(ui.Jumbotron(discoverLinks()))\n\n\tdom.Body.Append(\n\t\tui.Columns{3, []ui.Element{\n\t\t\tlastBlogposts(4),\n\t\t\tupcomingEvents(4),\n\t\t\tlastContributions(4),\n\t\t}},\n\t)\n\n\tdom.Body.Append(ui.HR{})\n\tdom.Body.Append(playgroundSection()...)\n\tdom.Body.Append(ui.HR{})\n\tdom.Body.Append(packageStaffPicks()...)\n\tdom.Body.Append(ui.HR{})\n\tdom.Body.Append(worxDAO()...)\n\tdom.Body.Append(ui.HR{})\n\t// footer\n\tdom.Footer.Append(\n\t\tui.Columns{2, []ui.Element{\n\t\t\tsocialLinks(),\n\t\t\tquoteOfTheBlock(),\n\t\t}},\n\t)\n\n\t// Testnet disclaimer\n\tdom.Footer.Append(\n\t\tui.HR{},\n\t\tui.Bold(\"This is a testnet.\"),\n\t\tui.Text(\"Package names are not guaranteed to be available for production.\"),\n\t)\n\n\treturn dom.String()\n}\n\nfunc lastBlogposts(limit int) ui.Element {\n\tposts := blog.RenderLastPostsWidget(limit)\n\treturn ui.Element{\n\t\tui.H3(\"Latest Blogposts\"),\n\t\tui.Text(posts),\n\t}\n}\n\nfunc lastContributions(limit int) ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"Latest Contributions\"),\n\t\t// TODO: import r/gh to\n\t\tui.Link{Text: \"View latest contributions\", URL: \"https://github.com/gnolang/gno/pulls\"},\n\t}\n}\n\nfunc upcomingEvents(limit int) ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"Upcoming Events\"),\n\t\t// TODO: replace with r/gnoland/events\n\t\tui.Text(\"[View upcoming events](/events)\"),\n\t}\n}\n\nfunc introSection() ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"We’re building gno.land, set to become the leading open-source smart contract platform, using Gno, an interpreted and fully deterministic variation of the Go programming language for succinct and composable smart contracts.\"),\n\t\tui.Paragraph(\"With transparent and timeless code, gno.land is the next generation of smart contract platforms, serving as the “GitHub” of the ecosystem, with realms built using fully transparent, auditable code that anyone can inspect and reuse.\"),\n\t\tui.Paragraph(\"Intuitive and easy to use, gno.land lowers the barrier to web3 and makes censorship-resistant platforms accessible to everyone. If you want to help lay the foundations of a fairer and freer world, join us today.\"),\n\t}\n}\n\nfunc worxDAO() ui.Element {\n\t// WorxDAO\n\t// XXX(manfred): please, let me finish a v0, then we can iterate\n\t// highest level == highest responsibility\n\t// teams are responsible for components they don't owne\n\t// flag : realm maintainers VS facilitators\n\t// teams\n\t// committee of trustees to create the directory\n\t// each directory is a name, has a parent and have groups\n\t// homepage team - blocks aggregating events\n\t// XXX: TODO\n\t/*`\n\t# Directory\n\n\t* gno.land (owned by group)\n\t *\n\t* gnovm\n\t * gnolang (language)\n\t * gnovm\n\t - current challenges / concerns / issues\n\t* tm2\n\t * amino\n\t *\n\n\t## Contributors\n\t``*/\n\treturn ui.Element{\n\t\tui.H3(\"Contributions (WorxDAO \u0026 GoR)\"),\n\t\t// TODO: GoR dashboard + WorxDAO topics\n\t\tui.Text(`coming soon`),\n\t}\n}\n\nfunc quoteOfTheBlock() ui.Element {\n\tquotes := []string{\n\t\t\"Gno is for Truth.\",\n\t\t\"Gno is for Social Coordination.\",\n\t\t\"Gno is _not only_ for DeFi.\",\n\t\t\"Now, you Gno.\",\n\t\t\"Come for the Go, Stay for the Gno.\",\n\t}\n\theight := std.GetHeight()\n\tidx := int(height) % len(quotes)\n\tqotb := quotes[idx]\n\n\treturn ui.Element{\n\t\tui.H3(ufmt.Sprintf(\"Quote of the ~Day~ Block#%d\", height)),\n\t\tui.Quote(qotb),\n\t}\n}\n\nfunc socialLinks() ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"Socials\"),\n\t\tui.BulletList{\n\t\t\t// XXX: improve UI to support a nice GO api for such links\n\t\t\tui.Text(\"Check out our [community projects](https://github.com/gnolang/awesome-gno)\"),\n\t\t\tui.Text(\"![Discord](static/img/ico-discord.svg) [Discord](https://discord.gg/S8nKUqwkPn)\"),\n\t\t\tui.Text(\"![Twitter](static/img/ico-twitter.svg) [Twitter](https://twitter.com/_gnoland)\"),\n\t\t\tui.Text(\"![Youtube](static/img/ico-youtube.svg) [Youtube](https://www.youtube.com/@_gnoland)\"),\n\t\t\tui.Text(\"![Telegram](static/img/ico-telegram.svg) [Telegram](https://t.me/gnoland)\"),\n\t\t},\n\t}\n}\n\nfunc playgroundSection() ui.Element {\n\treturn ui.Element{\n\t\tui.H3(\"[Gno Playground](https://play.gno.land)\"),\n\t\tui.Paragraph(`Gno Playground is a web application designed for building, running, testing, and interacting\nwith your Gno code, enhancing your understanding of the Gno language. With Gno Playground, you can share your code,\nexecute tests, deploy your realms and packages to gno.land, and explore a multitude of other features.`),\n\t\tui.Paragraph(\"Experience the convenience of code sharing and rapid experimentation with [Gno Playground](https://play.gno.land).\"),\n\t}\n}\n\nfunc packageStaffPicks() ui.Element {\n\t// XXX: make it modifiable from a DAO\n\treturn ui.Element{\n\t\tui.H3(\"Explore New Packages and Realms\"),\n\t\tui.Columns{\n\t\t\t3,\n\t\t\t[]ui.Element{\n\t\t\t\t{\n\t\t\t\t\tui.H4(\"[r/gnoland](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/gnoland)\"),\n\t\t\t\t\tui.BulletList{\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/blog\"},\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/dao\"},\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/faucet\"},\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/home\"},\n\t\t\t\t\t\tui.Link{URL: \"r/gnoland/pages\"},\n\t\t\t\t\t},\n\t\t\t\t\tui.H4(\"[r/sys](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/sys)\"),\n\t\t\t\t\tui.BulletList{\n\t\t\t\t\t\tui.Link{URL: \"r/sys/names\"},\n\t\t\t\t\t\tui.Link{URL: \"r/sys/rewards\"},\n\t\t\t\t\t\tui.Link{URL: \"r/sys/validators\"},\n\t\t\t\t\t},\n\t\t\t\t}, {\n\t\t\t\t\tui.H4(\"[r/demo](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo)\"),\n\t\t\t\t\tui.BulletList{\n\t\t\t\t\t\tui.Link{URL: \"r/demo/boards\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/users\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/banktest\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/foo20\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/foo721\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/microblog\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/nft\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/types\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/art/gnoface\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/art/millipede\"},\n\t\t\t\t\t\tui.Link{URL: \"r/demo/groups\"},\n\t\t\t\t\t\tui.Text(\"...\"),\n\t\t\t\t\t},\n\t\t\t\t}, {\n\t\t\t\t\tui.H4(\"[p/demo](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo)\"),\n\t\t\t\t\tui.BulletList{\n\t\t\t\t\t\tui.Link{URL: \"p/demo/avl\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/blog\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/ui\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/ufmt\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/merkle\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/bf\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/flow\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/gnode\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/grc/grc20\"},\n\t\t\t\t\t\tui.Link{URL: \"p/demo/grc/grc721\"},\n\t\t\t\t\t\tui.Text(\"...\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc discoverLinks() ui.Element {\n\treturn ui.Element{\n\t\tui.Text(`\u003cdiv class=\"columns-3\"\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Learn about gno.land\n\n- [About](/about)\n- [GitHub](https://github.com/gnolang)\n- [Blog](/blog)\n- [Events](/events)\n- Tokenomics (soon)\n- [Partners, Fund, Grants](/partners)\n- [Explore the Ecosystem](/ecosystem)\n- [Careers](https://jobs.lever.co/allinbits?department=Gno.land)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\n\u003cdiv class=\"column\"\u003e\n\n### Build with Gno\n\n- [Write Gno in the browser](https://play.gno.land)\n- [Read about the Gno Language](/gnolang)\n- [Visit the official documentation](https://docs.gno.land)\n- [Gno by Example](https://gno-by-example.com/)\n- [Efficient local development for Gno](https://docs.gno.land/gno-tooling/cli/gno-tooling-gnodev)\n- [Get testnet GNOTs](https://faucet.gno.land)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003cdiv class=\"column\"\u003e\n\n### Explore the universe\n\n- [Discover demo packages](https://github.com/gnolang/gno/tree/master/examples)\n- [Gnoscan](https://gnoscan.io)\n- [Portal Loop](https://docs.gno.land/concepts/portal-loop)\n- Testnet 4 (upcoming)\n- [Testnet 3](https://test3.gno.land/) (archive)\n- [Testnet 2](https://test2.gno.land/) (archive)\n- Testnet Faucet Hub (soon)\n\n\u003c/div\u003e\u003c!-- end column--\u003e\n\u003c/div\u003e\u003c!-- end columns-3--\u003e`),\n\t}\n}\n" @@ -4429,10 +4057,6 @@ "name": "monit", "path": "gno.land/r/gnoland/monit", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/gnoland/monit\n\nrequire (\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/watchdog v0.0.0-latest\n)\n" - }, { "name": "monit.gno", "body": "// Package monit links a monitoring system with the chain in both directions.\n//\n// The agent will periodically call Incr() and verify that the value is always\n// higher than the previously known one. The contract will store the last update\n// time and use it to detect whether or not the monitoring agent is functioning\n// correctly.\npackage monit\n\nimport (\n\t\"std\"\n\t\"time\"\n\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/p/demo/ufmt\"\n\t\"gno.land/p/demo/watchdog\"\n)\n\nvar (\n\tcounter int\n\tlastUpdate time.Time\n\tlastCaller std.Address\n\twd = watchdog.Watchdog{Duration: 5 * time.Minute}\n\towner = ownable.New() // TODO: replace with -\u003e ownable.NewWithAddress...\n\twatchdogDuration = 5 * time.Minute\n)\n\n// Incr increments the counter and informs the watchdog that we're alive.\n// This function can be called by anyone.\nfunc Incr() int {\n\tcounter++\n\tlastUpdate = time.Now()\n\tlastCaller = std.PrevRealm().Addr()\n\twd.Alive()\n\treturn counter\n}\n\n// Reset resets the realm state.\n// This function can only be called by the admin.\nfunc Reset() {\n\tif owner.CallerIsOwner() != nil { // TODO: replace with owner.AssertCallerIsOwner\n\t\tpanic(\"unauthorized\")\n\t}\n\tcounter = 0\n\tlastCaller = std.PrevRealm().Addr()\n\tlastUpdate = time.Now()\n\twd = watchdog.Watchdog{Duration: 5 * time.Minute}\n}\n\nfunc Render(_ string) string {\n\tstatus := wd.Status()\n\treturn ufmt.Sprintf(\n\t\t\"counter=%d\\nlast update=%s\\nlast caller=%s\\nstatus=%s\",\n\t\tcounter, lastUpdate, lastCaller, status,\n\t)\n}\n\n// TransferOwnership transfers ownership to a new owner. This is a proxy to\n// ownable.Ownable.TransferOwnership.\nfunc TransferOwnership(newOwner std.Address) { owner.TransferOwnership(newOwner) }\n" @@ -4471,10 +4095,6 @@ "name": "admin.gno", "body": "package gnopages\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\nvar (\n\tadminAddr std.Address\n\tmoderatorList avl.Tree\n\tinPause bool\n)\n\nfunc init() {\n\t// adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis.\n\tadminAddr = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\"\n}\n\nfunc AdminSetAdminAddr(addr std.Address) {\n\tassertIsAdmin()\n\tadminAddr = addr\n}\n\nfunc AdminSetInPause(state bool) {\n\tassertIsAdmin()\n\tinPause = state\n}\n\nfunc AdminAddModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), true)\n}\n\nfunc AdminRemoveModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), false) // XXX: delete instead?\n}\n\nfunc ModAddPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\tcaller := std.GetOrigCaller()\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.NewPost(caller, slug, title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc ModEditPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.GetPost(slug).Update(title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc isAdmin(addr std.Address) bool {\n\treturn addr == adminAddr\n}\n\nfunc isModerator(addr std.Address) bool {\n\t_, found := moderatorList.Get(addr.String())\n\treturn found\n}\n\nfunc assertIsAdmin() {\n\tcaller := std.GetOrigCaller()\n\tif !isAdmin(caller) {\n\t\tpanic(\"access restricted.\")\n\t}\n}\n\nfunc assertIsModerator() {\n\tcaller := std.GetOrigCaller()\n\tif isAdmin(caller) || isModerator(caller) {\n\t\treturn\n\t}\n\tpanic(\"access restricted\")\n}\n\nfunc assertNotInPause() {\n\tif inPause {\n\t\tpanic(\"access restricted (pause)\")\n\t}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/gnoland/pages\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/blog v0.0.0-latest\n)\n" - }, { "name": "page_about.gno", "body": "package gnopages\n\nfunc init() {\n\tpath := \"about\"\n\ttitle := \"Gno.land Is A Platform To Write Smart Contracts In Gno\"\n\t// XXX: description := \"On gno.land, developers write smart contracts and other blockchain apps using Gno without learning a language that’s exclusive to a single ecosystem.\"\n\tbody := `\nGno.land is a next-generation smart contract platform using Gno, an interpreted version of the general-purpose Go\nprogramming language. On gno.land, smart contracts can be uploaded on-chain only by publishing their full source code,\nmaking it trivial to verify the contract or fork it into an improved version. With a system to publish reusable code \nlibraries on-chain, gno.land serves as the “GitHub” of the ecosystem, with realms built using fully transparent, \nauditable code that anyone can inspect and reuse.\n\nGno.land addresses many pressing issues in the blockchain space, starting with the ease of use and intuitiveness of\nsmart contract platforms. Developers can write smart contracts without having to learn a new language that’s exclusive \nto a single ecosystem or limited by design. Go developers can easily port their existing web apps to gno.land or build\nnew ones from scratch, making web3 vastly more accessible.\n\nSecured by Proof of Contribution (PoC), a DAO-managed Proof-of-Authority consensus mechanism, gno.land prioritizes \nfairness and merit, rewarding the people most active on the platform. PoC restructures the financial incentives that \noften corrupt blockchain projects, opting instead to reward contributors for their work based on expertise, commitment, and \nalignment. \n\nOne of our inspirations for gno.land is the gospels, which built a system of moral code that lasted thousands of years.\nBy observing a minimal production implementation, gno.land’s design will endure over time and serve as a reference for \nfuture generations with censorship-resistant tools that improve their understanding of the world. \n`\n\t_ = b.NewPost(\"\", path, title, body, \"2022-05-20T13:17:22Z\", nil, nil)\n}\n" @@ -4557,10 +4177,6 @@ "name": "dao_test.gno", "body": "package govdao\n\nimport (\n\t\"std\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/testutils\"\n\t\"gno.land/p/demo/urequire\"\n\tpproposal \"gno.land/p/gov/proposal\"\n)\n\nfunc TestPackage(t *testing.T) {\n\tu1 := testutils.TestAddress(\"u1\")\n\tu2 := testutils.TestAddress(\"u2\")\n\tu3 := testutils.TestAddress(\"u3\")\n\n\tmembers = append(members, u1)\n\tmembers = append(members, u2)\n\tmembers = append(members, u3)\n\n\tnu1 := testutils.TestAddress(\"random1\")\n\n\tout := Render(\"\")\n\n\texpected := \"No proposals found :(\"\n\turequire.Equal(t, expected, out)\n\n\tvar called bool\n\tex := pproposal.NewExecutor(func() error {\n\t\tcalled = true\n\t\treturn nil\n\t})\n\n\tstd.TestSetOrigCaller(u1)\n\tpid := Propose(\"dummy proposal\", ex)\n\n\t// try to vote not being a member\n\tstd.TestSetOrigCaller(nu1)\n\n\turequire.PanicsWithMessage(t, msgCallerNotAMember, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\n\t// try to vote several times\n\tstd.TestSetOrigCaller(u1)\n\turequire.NotPanics(t, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\turequire.PanicsWithMessage(t, msgAlreadyVoted, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: active\n\nVoting status: YES: 1, NO: 0, percent: 33, members: 3\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\tstd.TestSetOrigCaller(u2)\n\turequire.PanicsWithMessage(t, msgWrongVotingValue, func() {\n\t\tVoteOnProposal(pid, \"INCORRECT\")\n\t})\n\turequire.NotPanics(t, func() {\n\t\tVoteOnProposal(pid, \"NO\")\n\t})\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: active\n\nVoting status: YES: 1, NO: 1, percent: 33, members: 3\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\tstd.TestSetOrigCaller(u3)\n\turequire.NotPanics(t, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: accepted\n\nVoting status: YES: 2, NO: 1, percent: 66, members: 3\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\t// Add a new member, so non-executed proposals will change the voting status\n\tu4 := testutils.TestAddress(\"u4\")\n\tmembers = append(members, u4)\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: active\n\nVoting status: YES: 2, NO: 1, percent: 50, members: 4\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\tstd.TestSetOrigCaller(u4)\n\turequire.NotPanics(t, func() {\n\t\tVoteOnProposal(pid, \"YES\")\n\t})\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: accepted\n\nVoting status: YES: 3, NO: 1, percent: 75, members: 4\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\tExecuteProposal(pid)\n\turequire.True(t, called)\n\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: succeeded\n\nVoting status: YES: 3, NO: 1, percent: 75, members: 4\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n\t// Add a new member and try to vote an already executed proposal\n\tu5 := testutils.TestAddress(\"u5\")\n\tmembers = append(members, u5)\n\tstd.TestSetOrigCaller(u5)\n\turequire.PanicsWithMessage(t, msgPropExecuted, func() {\n\t\tExecuteProposal(pid)\n\t})\n\n\t// even if we added a new member the executed proposal is showing correctly the members that voted on it\n\tout = Render(\"0\")\n\texpected = `# Prop #0\n\ndummy proposal\n\nStatus: succeeded\n\nVoting status: YES: 3, NO: 1, percent: 75, members: 4\n\nAuthor: g1w5c47h6lta047h6lta047h6lta047h6ly5kscr\n\n`\n\n\turequire.Equal(t, expected, out)\n\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/gov/dao\n\nrequire (\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/demo/urequire v0.0.0-latest\n\tgno.land/p/gov/proposal v0.0.0-latest\n)\n" - }, { "name": "memberset.gno", "body": "package govdao\n\nimport (\n\t\"std\"\n\n\tpproposal \"gno.land/p/gov/proposal\"\n)\n\nconst daoPkgPath = \"gno.land/r/gov/dao\"\n\nconst (\n\terrNoChangesProposed = \"no set changes proposed\"\n\terrNotGovDAO = \"caller not govdao executor\"\n)\n\nfunc NewPropExecutor(changesFn func() []std.Address) pproposal.Executor {\n\tif changesFn == nil {\n\t\tpanic(errNoChangesProposed)\n\t}\n\n\tcallback := func() error {\n\t\t// Make sure the GovDAO executor runs the valset changes\n\t\tassertGovDAOCaller()\n\n\t\tfor _, addr := range changesFn() {\n\t\t\tmembers = append(members, addr)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\treturn pproposal.NewExecutor(callback)\n}\n\n// assertGovDAOCaller verifies the caller is the GovDAO executor\nfunc assertGovDAOCaller() {\n\tif std.CurrentRealm().PkgPath() != daoPkgPath {\n\t\tpanic(errNotGovDAO)\n\t}\n}\n" @@ -4611,10 +4227,6 @@ "name": "doc.gno", "body": "// Package validators implements the on-chain validator set management through Proof of Contribution.\n// The Realm exposes only a public executor for govdao proposals, that can suggest validator set changes.\npackage validators\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/sys/validators\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/seqid v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/gov/proposal v0.0.0-latest\n\tgno.land/p/nt/poa v0.0.0-latest\n\tgno.land/p/sys/validators v0.0.0-latest\n)\n" - }, { "name": "gnosdk.gno", "body": "package validators\n\nimport (\n\t\"gno.land/p/sys/validators\"\n)\n\n// GetChanges returns the validator changes stored on the realm, since the given block number.\n// This function is intended to be called by gno.land through the GnoSDK\nfunc GetChanges(from int64) []validators.Validator {\n\tvalsetChanges := make([]validators.Validator, 0)\n\n\t// Gather the changes from the specified block\n\tchanges.Iterate(getBlockID(from), \"\", func(_ string, value interface{}) bool {\n\t\tchs := value.([]change)\n\n\t\tfor _, ch := range chs {\n\t\t\tvalsetChanges = append(valsetChanges, ch.validator)\n\t\t}\n\n\t\treturn false\n\t})\n\n\treturn valsetChanges\n}\n" @@ -4661,10 +4273,6 @@ "name": "valopers", "path": "gno.land/r/gnoland/valopers", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/gnoland/valopers\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/testutils v0.0.0-latest\n\tgno.land/p/demo/uassert v0.0.0-latest\n\tgno.land/p/demo/ufmt v0.0.0-latest\n\tgno.land/p/sys/validators v0.0.0-latest\n\tgno.land/r/gov/dao v0.0.0-latest\n\tgno.land/r/sys/validators v0.0.0-latest\n)\n" - }, { "name": "init.gno", "body": "package valopers\n\nimport \"gno.land/p/demo/avl\"\n\nfunc init() {\n\tvalopers = avl.NewTree()\n}\n" @@ -4706,10 +4314,6 @@ { "name": "config.gno", "body": "package config\n\nimport \"std\"\n\nvar addr = std.Address(\"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\")\n\nfunc Addr() std.Address {\n\treturn addr\n}\n\nfunc UpdateAddr(newAddr std.Address) {\n\tAssertIsAdmin()\n\taddr = newAddr\n}\n\nfunc AssertIsAdmin() {\n\tif std.GetOrigCaller() != addr {\n\t\tpanic(\"restricted area\")\n\t}\n}\n" - }, - { - "name": "gno.mod", - "body": "module gno.land/r/manfred/config\n" } ] }, @@ -4737,10 +4341,6 @@ "name": "home", "path": "gno.land/r/manfred/home", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/manfred/home\n\nrequire gno.land/r/manfred/config v0.0.0-latest\n" - }, { "name": "home.gno", "body": "package home\n\nimport \"gno.land/r/manfred/config\"\n\nvar (\n\ttodos []string\n\tstatus string\n\tmemeImgURL string\n)\n\nfunc init() {\n\ttodos = append(todos, \"fill this todo list...\")\n\tstatus = \"Online\" // Initial status set to \"Online\"\n\tmemeImgURL = \"https://i.imgflip.com/7ze8dc.jpg\"\n}\n\nfunc Render(path string) string {\n\tcontent := \"# Manfred's (gn)home Dashboard\\n\\n\"\n\n\tcontent += \"## Meme\\n\"\n\tcontent += \"![](\" + memeImgURL + \")\\n\\n\"\n\n\tcontent += \"## Status\\n\"\n\tcontent += status + \"\\n\\n\"\n\n\tcontent += \"## Personal ToDo List\\n\"\n\tfor _, todo := range todos {\n\t\tcontent += \"- [ ] \" + todo + \"\\n\"\n\t}\n\tcontent += \"\\n\"\n\n\t// TODO: Implement a feature to list replies on r/boards on my posts\n\t// TODO: Maybe integrate a calendar feature for upcoming events?\n\n\treturn content\n}\n\nfunc AddNewTodo(todo string) {\n\tconfig.AssertIsAdmin()\n\ttodos = append(todos, todo)\n}\n\nfunc DeleteTodo(todoIndex int) {\n\tconfig.AssertIsAdmin()\n\tif todoIndex \u003e= 0 \u0026\u0026 todoIndex \u003c len(todos) {\n\t\t// Remove the todo from the list by merging slices from before and after the todo\n\t\ttodos = append(todos[:todoIndex], todos[todoIndex+1:]...)\n\t} else {\n\t\tpanic(\"Invalid todo index\")\n\t}\n}\n\nfunc UpdateStatus(newStatus string) {\n\tconfig.AssertIsAdmin()\n\tstatus = newStatus\n}\n" @@ -4783,10 +4383,6 @@ "name": "admin.gno", "body": "package present\n\nimport (\n\t\"std\"\n\t\"strings\"\n\n\t\"gno.land/p/demo/avl\"\n)\n\nvar (\n\tadminAddr std.Address\n\tmoderatorList avl.Tree\n\tinPause bool\n)\n\nfunc init() {\n\t// adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis.\n\tadminAddr = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\"\n}\n\nfunc AdminSetAdminAddr(addr std.Address) {\n\tassertIsAdmin()\n\tadminAddr = addr\n}\n\nfunc AdminSetInPause(state bool) {\n\tassertIsAdmin()\n\tinPause = state\n}\n\nfunc AdminAddModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), true)\n}\n\nfunc AdminRemoveModerator(addr std.Address) {\n\tassertIsAdmin()\n\tmoderatorList.Set(addr.String(), false) // XXX: delete instead?\n}\n\nfunc ModAddPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\tcaller := std.GetOrigCaller()\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.NewPost(caller, slug, title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc ModEditPost(slug, title, body, publicationDate, authors, tags string) {\n\tassertIsModerator()\n\n\ttagList := strings.Split(tags, \",\")\n\tauthorList := strings.Split(authors, \",\")\n\n\terr := b.GetPost(slug).Update(title, body, publicationDate, authorList, tagList)\n\tcheckErr(err)\n}\n\nfunc isAdmin(addr std.Address) bool {\n\treturn addr == adminAddr\n}\n\nfunc isModerator(addr std.Address) bool {\n\t_, found := moderatorList.Get(addr.String())\n\treturn found\n}\n\nfunc assertIsAdmin() {\n\tcaller := std.GetOrigCaller()\n\tif !isAdmin(caller) {\n\t\tpanic(\"access restricted.\")\n\t}\n}\n\nfunc assertIsModerator() {\n\tcaller := std.GetOrigCaller()\n\tif isAdmin(caller) || isModerator(caller) {\n\t\treturn\n\t}\n\tpanic(\"access restricted\")\n}\n\nfunc assertNotInPause() {\n\tif inPause {\n\t\tpanic(\"access restricted (pause)\")\n\t}\n}\n\nfunc checkErr(err error) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n" }, - { - "name": "gno.mod", - "body": "module gno.land/r/manfred/present\n\nrequire (\n\tgno.land/p/demo/avl v0.0.0-latest\n\tgno.land/p/demo/blog v0.0.0-latest\n)\n" - }, { "name": "present_miami23.gno", "body": "package present\n\nfunc init() {\n\tpath := \"miami23\"\n\ttitle := \"Portal Loop Demo (Miami 2023)\"\n\tbody := `\nRendered by Gno.\n\n[Source (WIP)](https://github.com/gnolang/gno/pull/1176)\n\n## Portal Loop\n\n- DONE: Dynamic homepage, key pages, aliases, and redirects.\n- TODO: Deploy with history, complete worxdao v0.\n- Will replace the static gno.land site.\n- Enhances local development.\n\n[GitHub Issue](https://github.com/gnolang/gno/issues/1108)\n\n## Roadmap\n\n- Crafting the roadmap this week, open to collaboration.\n- Combining onchain (portal loop) and offchain (GitHub).\n- Next week: Unveiling the official v0 roadmap.\n\n## Teams, DAOs, Projects\n\n- Developing worxDAO contracts for directories of projects and teams.\n- GitHub teams and projects align with this structure.\n- CODEOWNER file updates coming.\n- Initial teams announced next week.\n\n## Tech Team Retreat Plan\n\n- Continue Portal Loop.\n- Consider dApp development.\n- Explore new topics [here](https://github.com/orgs/gnolang/projects/15/).\n- Engage in workshops.\n- Connect and have fun with colleagues.\n`\n\t_ = b.NewPost(adminAddr, path, title, body, \"2023-10-15T13:17:24Z\", []string{\"moul\"}, []string{\"demo\", \"portal-loop\", \"miami\"})\n}\n" @@ -4825,10 +4421,6 @@ "name": "rewards", "path": "gno.land/r/sys/rewards", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/sys/rewards\n" - }, { "name": "rewards.gno", "body": "// This package will be used to manage proof-of-contributions on the exposed smart-contract side.\npackage rewards\n\n// TODO: write specs.\n" @@ -4859,10 +4451,6 @@ "name": "users", "path": "gno.land/r/sys/users", "files": [ - { - "name": "gno.mod", - "body": "module gno.land/r/sys/users\n\nrequire (\n\tgno.land/p/demo/ownable v0.0.0-latest\n\tgno.land/r/demo/users v0.0.0-latest\n)\n" - }, { "name": "verify.gno", "body": "package users\n\nimport (\n\t\"std\"\n\n\t\"gno.land/p/demo/ownable\"\n\t\"gno.land/r/demo/users\"\n)\n\nconst admin = \"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\" // @moul\n\ntype VerifyNameFunc func(enabled bool, address std.Address, name string) bool\n\nvar (\n\towner = ownable.NewWithAddress(admin) // Package owner\n\tcheckFunc = VerifyNameByUser // Checking namespace callback\n\tenabled = true // For now this package is disabled by default\n)\n\nfunc IsEnabled() bool { return enabled }\n\n// This method ensures that the given address has ownership of the given name.\nfunc IsAuthorizedAddressForName(address std.Address, name string) bool {\n\treturn checkFunc(enabled, address, name)\n}\n\n// VerifyNameByUser checks from the `users` package that the user has correctly\n// registered the given name.\n// This function considers as valid an `address` that matches the `name`.\nfunc VerifyNameByUser(enable bool, address std.Address, name string) bool {\n\tif !enable {\n\t\treturn true\n\t}\n\n\t// Allow user with their own address as name\n\tif address.String() == name {\n\t\treturn true\n\t}\n\n\tif user := users.GetUserByName(name); user != nil {\n\t\treturn user.Address == address\n\t}\n\n\treturn false\n}\n\n// Admin calls\n\n// Enable this package.\nfunc AdminEnable() {\n\tif err := owner.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tenabled = true\n}\n\n// Disable this package.\nfunc AdminDisable() {\n\tif err := owner.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tenabled = false\n}\n\n// AdminUpdateVerifyCall updates the method that verifies the namespace.\nfunc AdminUpdateVerifyCall(check VerifyNameFunc) {\n\tif err := owner.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tcheckFunc = check\n}\n\n// AdminTransferOwnership transfers the ownership to a new owner.\nfunc AdminTransferOwnership(newOwner std.Address) error {\n\tif err := owner.CallerIsOwner(); err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn owner.TransferOwnership(newOwner)\n}\n" From 5109a2adface3950388a0a66cb1808b48e32f3a9 Mon Sep 17 00:00:00 2001 From: Milos Zivkovic Date: Tue, 9 Jul 2024 23:01:02 +0200 Subject: [PATCH 22/22] Add leftover Teritori address --- misc/deployments/test4.gno.land/genesis.json | 59 ++++++++++--------- .../test4.gno.land/genesis_balances.txt | 4 +- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/misc/deployments/test4.gno.land/genesis.json b/misc/deployments/test4.gno.land/genesis.json index 1523fd3b7c6..0c64b18ed42 100644 --- a/misc/deployments/test4.gno.land/genesis.json +++ b/misc/deployments/test4.gno.land/genesis.json @@ -84,46 +84,47 @@ "app_state": { "@type": "/gno.GenesisState", "balances": [ - "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", - "g1gadp0yq5djelxwv7h8g7wpdf7x5w3vuzwmqrne=9000000000000000000ugnot", - "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot", - "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=103000000ugnot", + "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", + "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", + "g1d7set9rsdw7ggt9elvzerep4jz80a4hu4crmzg=9000000000000000000ugnot", + "g13csus64lj8twrn266837l9e77dusk2zwa794qz=9000000000000000000ugnot", + "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", + "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", "g1cm7u2fqd7drwfcqtn7zsgrfcyvlkkukhhtrxxj=9000000000000000000ugnot", + "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", + "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot", + "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", + "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", + "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", "g1knpjkw7d5ft2830n8rnatllzq9gu85dmn20nrp=9000000000000000000ugnot", - "g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot", - "g1a5kdc6ykfykq9p6088sclnr7e63hj8y4nnr5gn=9000000000000000000ugnot", - "g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot", - "g1778y2yphxs2wpuaflsy5y9qwcd4gttn4g5yjx5=9000000000000000000ugnot", - "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", - "g1s6ujq0r200a0tfqgnjfflz7ddetsnrwvzxxx74=9000000000000000000ugnot", - "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", "g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3=9000000000000000000ugnot", "g1563d3cw0gdv68le6azw2s63xm0jx9xvgpmfatq=9000000000000000000ugnot", - "g13csus64lj8twrn266837l9e77dusk2zwa794qz=9000000000000000000ugnot", + "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", "g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot", + "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot", + "g1a5kdc6ykfykq9p6088sclnr7e63hj8y4nnr5gn=9000000000000000000ugnot", + "g16f5chytu99dmjqtekxf8qzg04vcv7dck6qny6d=9000000000000000000ugnot", + "g1dfr24yhk5ztwtqn2a36m8f6ud8cx5hww4dkjfl=9000000000000000000ugnot", + "g1gadp0yq5djelxwv7h8g7wpdf7x5w3vuzwmqrne=9000000000000000000ugnot", + "g14u5eaheavy0ux4dmpykg2gvxpvqvexm9cyg58a=9000000000000000000ugnot", + "g1cpx59z5r8vzeww2fm4ezpz7yvjs7kptywkm864=9000000000000000000ugnot", + "g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot", + "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", + "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", "g18amm3fc00t43dcxsys6udug0czyvqt9e7p23rd=9000000000000000000ugnot", - "g1jazghxvvgz3egnr2fc8uf72z4g0l03596y9ls7=9000000000000000000ugnot", - "g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=9000000000000000000ugnot", + "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", + "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", + "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", + "g1s6ujq0r200a0tfqgnjfflz7ddetsnrwvzxxx74=9000000000000000000ugnot", + "g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot", "g1xja7p9s3ly3tvkgks9x0n6f6yau2hnzl4x8x3d=9000000000000000000ugnot", - "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=9000000000000000000ugnot", + "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=103000000ugnot", "g1njagaeg7e398hze39ygfgvc4gwsh6lkz7dwnuz=9000000000000000000ugnot", - "g1d7set9rsdw7ggt9elvzerep4jz80a4hu4crmzg=9000000000000000000ugnot", - "g1acn3xssksatydd0fcuslvgmjyw0fzkjdhusddg=9000000000000000000ugnot", "g19p3yzr3cuhzqa02j0ce6kzvyjqfzwemw3vam0x=9000000000000000000ugnot", - "g1mx4pum9976th863jgry4sdjzfwu03qan5w2v9j=9000000000000000000ugnot", - "g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot", - "g125em6arxsnj49vx35f0n0z34putv5ty3376fg5=9000000000000000000ugnot", - "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=9000000000000000000ugnot", - "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5=9000000000000000000ugnot", - "g1e6gxg5tvc55mwsn7t7dymmlasratv7mkv0rap2=9000000000000000000ugnot", - "g1qynsu9dwj9lq0m5fkje7jh6qy3md80ztqnshhm=9000000000000000000ugnot", "g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=9000000000000000000ugnot", - "g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot", - "g1ker4vvggvsyatexxn3hkthp2hu80pkhrwmuczr=9000000000000000000ugnot", - "g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot", + "g17p2kkyy9lp2z3ecw4psssk357vxp20afnyl00d=9000000000000000000ugnot", "g127l4gkhk0emwsx5tmxe96sp86c05h8vg5tufzq=9000000000000000000ugnot", - "g16f5chytu99dmjqtekxf8qzg04vcv7dck6qny6d=9000000000000000000ugnot", - "g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot" + "g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=9000000000000000000ugnot" ], "txs": [ { diff --git a/misc/deployments/test4.gno.land/genesis_balances.txt b/misc/deployments/test4.gno.land/genesis_balances.txt index 3e80f31e925..d9493485c96 100644 --- a/misc/deployments/test4.gno.land/genesis_balances.txt +++ b/misc/deployments/test4.gno.land/genesis_balances.txt @@ -47,7 +47,6 @@ g16tfrrul20g4jzt3z303raqw8vs8s2pqqh5clwu=9000000000000000000ugnot # Ilker g1hy6zry03hg5d8le9s2w4fxme6236hkgd928dun=9000000000000000000ugnot # Jerónimo g15ruzptpql4dpuyzej0wkt5rq6r26kw4nxu9fwd=9000000000000000000ugnot # Denis g1dnllrdzwfhxv3evyk09y48mgn5phfjvtyrlzm7=9000000000000000000ugnot # Danny -# XXX=9000000000000000000ugnot # Alan g197q5e9v00vuz256ly7fq7v3ekaun5cr7wmjgfh=9000000000000000000ugnot # Salvo g1mpkp5lm8lwpm0pym4388836d009zfe4maxlqsq=9000000000000000000ugnot # Alexis @@ -67,4 +66,5 @@ g17ernafy6ctpcz6uepfsq2js8x2vz0wladh5yc3=9000000000000000000ugnot # ZenTasktic f # Teritori -g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot # Teritori Core Team \ No newline at end of file +g16n55jt73sl8s5kl3z5ahrf0qrlxp47n205j9ex=9000000000000000000ugnot # Teritori Core Team +g14u5eaheavy0ux4dmpykg2gvxpvqvexm9cyg58a=9000000000000000000ugnot # Norman