Skip to content

Commit

Permalink
docs: standard library, banker, coins, address (gnolang#1468)
Browse files Browse the repository at this point in the history
## Description

This PR introduces the `std` package concept & reference pages. It
covers concept pages for the banker, coins, testing etc, as well as the
reference for them. It also includes the reference for chain-related
functions in `std`, ie. `GetOrigCaller`, `GetChainID`, etc.

EDIT: Feel free to review & leave suggestions.

<details><summary>Contributors' checklist...</summary>

- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
- [x] Provided any useful hints for running manual tests
- [x] 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).
</details>

---------

Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com>
Co-authored-by: jon roethke <waymobetta@users.noreply.github.com>
  • Loading branch information
3 people authored and gfanton committed Jan 18, 2024
1 parent a0b142b commit c0491ef
Show file tree
Hide file tree
Showing 23 changed files with 1,716 additions and 1,151 deletions.
20 changes: 17 additions & 3 deletions docs/concepts/gno-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ id: gno-test
There are two methods for testing a realm or package during the development phase:

1. Calling the realm/package after deploying it on a local network (or testnet).
2. Using the `test` option within the [`gno`](./gno-tooling/cli/gno.md) CLI.
2. Using the `test` option within the [`gno`](../gno-tooling/cli/gno.md) CLI.

While the first method is recommended for its accuracy and similarity to the actual deployment environment, it is more efficient to initially utilize the second method for composing test cases and then proceed to the first method if no errors are detected.

Expand Down Expand Up @@ -44,7 +44,7 @@ package demo

import "testing"

func Test(t *testing.T) {
func TestHello(t *testing.T) {
{
got := Hello("People")
expected := "Hello People!"
Expand All @@ -67,7 +67,8 @@ Two conditions exist in the test case above.
1. "Hello People!" should be returned when calling `Hello("People")`.
2. "Hello People!" should be returned when calling `Hello("")`.

Upon examination of our realm code and the associated test results, the initial condition exhibited the desired behavior; however, an error was identified in the second condition. Despite the expected outcome of "Hello" being returned, the test case incorrectly specified that the expected output should be "Hello People!" instead.
Upon examination of our realm code and the associated test results, the initial condition exhibited the desired behavior; however, an error was identified in the second condition.
Despite the expected outcome of "Hello" being returned, the test case incorrectly specified that the expected output should be "Hello People!" instead.

Replacing the second test case with the following will successfully fix the issue and allow the test to pass.

Expand All @@ -80,3 +81,16 @@ Replacing the second test case with the following will successfully fix the issu
}
}
```

## Blockchain context in tests
Running `gno test` executes files within the directory that end with `_test.gno` and `_filetest.gno`.
Internally, a GnoVM instance is initialized to run the test, and, at that moment,
a blockchain-related context is injected into the GnoVM. Utilizing this context, the transaction sender,
coins, block height, etc. can be mocked.

## Manipulating blockchain context in tests
Some functions in the `std` package are exclusively accessible in `_test.gno`
and `_filetest.gno` files. These are located in the `std` package, and their primary role
is to alter the blockchain context for test runs.

For detailed information on these functions, refer to their [reference page](../reference/standard-library/std/testing.md).
6 changes: 3 additions & 3 deletions docs/concepts/packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ id: packages

# Packages

Packages encompass functionalities that are more closely aligned with the characteristics and capabilities of realms, as opposed to standard libraries.
Packages aim to encompass functionalities that are more closely aligned with the characteristics and capabilities of realms, as opposed to standard libraries. As opposed to realms, they are stateless.

The full list of available packages can be found in [the demo package](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo). Below are some of the most commonly used packages.
The full list of pre-deployed available packages can be found under the [demo package](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo). Below are some of the most commonly used packages.

## `avl`

In Golang, the classic key/value data type is represented by the `map` construct. However, while Gnolang also supports the use of `map`, it is not a viable option as it lacks determinism due to its non-sequential nature.

To address this issue, Gnolang implements the [AVL Tree](https://en.wikipedia.org/wiki/AVL\_tree) (Adelson-Velsky-Landis Tree) as a solution. The AVL Tree is a self-balancing binary search tree.

The `avl` package comprises a set of functions that can manipulate the leaves and nodes of the AVL Tree.
Expand Down
18 changes: 18 additions & 0 deletions docs/concepts/standard-library/banker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
id: banker
---

# Banker

The Banker's main purpose is to handle balance changes of [native coins](./coin.md) within Gno chains. This includes issuance, transfers, and burning of coins.

The Banker module can be cast into 4 subtypes of bankers that expose different functionalities and safety features within your packages and realms.

### Banker Types

1. `BankerTypeReadOnly` - read-only access to coin balances
2. `BankerTypeOrigSend` - full access to coins sent with the transaction that called the banker
3. `BankerTypeRealmSend` - full access to coins that the realm itself owns, including the ones sent with the transaciton
4. `BankerTypeRealmIssue` - able to issue new coins

The Banker API can be found in under the `std` package [reference](../../reference/standard-library/std/banker.md).
36 changes: 36 additions & 0 deletions docs/concepts/standard-library/coin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
id: coin
---

# Coin

A Coin is a native Gno type that has a denomination and an amount. Coins can be issued by the [Banker](banker.md).

A coin is defined by the following:

```go
type Coin struct {
Denom string `json:"denom"`
Amount int64 `json:"amount"`
}
```

`Denom` is the denomination of the coin, i.e. `ugnot`, and `Amount` is a non-negative
amount of the coin.

Multiple coins can be bundled together into a `Coins` slice:

```go
type Coins []Coin
```

This slice behaves like a mathematical set - it cannot contain duplicate `Coin` instances.

The `Coins` slice can be included in a transaction made by a user addresses or a realm.
Coins in this set are then available for access by specific types of Bankers,
which can manipulate them depending on access rights.

[//]: # (TODO ADD LINK TO Effective GNO)
Read more about coins in the [Effective Gno] section.

The Coin(s) API can be found in under the `std` package [reference](../../reference/standard-library/std/coin.md).
106 changes: 106 additions & 0 deletions docs/concepts/standard-library/gnopher-hole.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
id: gnopher-hole-stdlib
---

# Gnopher Hole

## Native bindings

Gno has support for "natively-defined" functions exclusively within the standard
libraries. These are functions which are _declared_ in Gno code, but only _defined_
in Go. There are generally three reasons why a function should be natively
defined:

1. It relies on inspecting the Gno Virtual Machine itself, i.e. `std.AssertOriginCall`
or `std.CurrentRealmPath`.
2. It relies on `unsafe`, or other features which are not planned to be
available in the GnoVM, i.e. `math.Float64frombits`.
3. Its native Go performance significantly outperforms the Gno counterpart by
several orders of magnitude, and it is used in crucial code or hot paths in
many programs, i.e. `sha256.Sum256`.

Native bindings are made to be a special feature which can be
help overcome pure Gno limitations, but it is not a substitute for writing
standard libraries in Gno.

There are three components to a natively bound function in Gno:

1. The Gno function declaration, which must be a top-level function with no body
(and no brackets), i.e. `crypto/sha256/sha256.gno`.
2. The Go function definition, which must be a top-level function with the same
name and signature, i.e. `crypto/sha256/sha256.go`.
3. When the two above are present and valid, the native binding can be created
by executing the code generator: either by executing `go generate` from the
`stdlibs` directory, or run `make generate` from the `gnovm` directory.
This generates the `native.go` file available in the `stdlibs` directory,
which provides the binding itself to then be used by the GnoVM.

The code generator in question is available in the `misc/genstd` directory.
There are some quirks and features that must be kept in mind when writing native
bindings, which are the following:

- Unexported functions (i.e. `func sum256(b []byte)`) must have their
Go counterpart prefixed with `X_` in order to make the functions exported (i.e.
`func X_sum256(b []byte)`).
- The Go function declaration may specify as the first argument
`m *gno.Machine`, where `gno` is an import for
`github.com/gnolang/gno/gnovm/pkg/gnolang`. This gives the function access to
the Virtual Machine state, and is used by functions like `std.AssertOriginCall()`.
- The Go function may change the type of any parameter or result to
`gno.TypedValue`, where `gno` is an import for the above import path. This
means that the `native.go` generated code will not attempt to automatically
convert the Gno value into the Go value, and can be useful for unsupported
conversions like interface values.
- A small set of named types are "linked" between their Gno version and Go
counterpart. For instance, `std.Address` in Gno is
`(".../tm2/pkg/crypto").Bech32Address` in Go. A list of these can be found in
`misc/genstd/mapping.go`.
- Not all type literals are currently supported when converting from their Gno
version to their Go counterpart, i.e. `struct` and `map` literals. If you intend to use these,
modify the code generator to support them.
- The code generator does not inspect any imported packages from the Go native code
to determine the default package identifier (i.e. the `package` clause).
For example, if a package is in `foo/bar`, but declares `package xyz`, when importing
foo/bar the generator will assume the name to be `bar` instead of `xyz`.
You can add an identifier to the import to fix this and use the identifier
you want/need, such as `import gno "github.com/gnolang/gno/gnovm/pkg/gnolang"`.

## Adding new standard libraries

New standard libraries may be added by simply creating a new directory (whose
path relative to the `stdlibs` directory will be the import path used in Gno
programs). Following that, the suggested approach for adding a Go standard
library is to copy the original files from the Go source tree, and renaming their
extensions from `.go` to `.gno`.

:::note
As a small aid, this bash one-liner can be useful to convert all the file
extensions:
```sh
for i in *.go; do mv $i "$(echo $i | sed 's/\.go$/.gno/')"; done
```
:::

Following that, the suggested approach is to iteratively try running `gno test .`,
while fixing any errors that may come out of trying to test the package.

Some things to keep in mind:

- Gno doesn't support assembly functions and build tags. Some Go packages may
contain assembly versions for different architecture and a `generic.go` file
containing the architecture-independent version. The general approach is that
of removing everything architecture/os-specific except for the `generic.go` file.
- Gno doesn't support reflection at the time of writing, which means that for
now many packages which rely heavily on reflection have to be delayed or
reduced while we figure out the details on how to implement reflection.
Aside from the `reflect` package itself, this also translates to very common
packages still not available in Gno, such as `fmt` or `encoding/json`.
- In the package documentation, specify the Go version from which the library
was taken.
- All changes from the Go standard libraries must be explicitly marked, possibly
with `// XXX` comments as needed.

If you intend to create a PR to add a new standard library, remember to update
[Go<\>Gno compatibility](../../reference/go-gno-compatibility.md) accordingly.


75 changes: 75 additions & 0 deletions docs/concepts/standard-library/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
id: overview
---

# Overview

Gno comes with a set of standard libraries which are included to ease development and provide extended functionality to the language. These include:
- standard libraries as we know them in classic Golang, i.e. `encoding/binary`, `strings`, `testing`, etc.
- a special `std` package, which contains types, interfaces, and APIs created to handle blockchain-related functionality.


Standard libraries differ from on-chain packages in terms of their import path structure.
Unlike on-chain [packages](../packages.md), standard libraries do not incorporate a domain-like format at the beginning
of their import path. For example:
- `import "encoding/binary"` refers to a standard library
- `import "gno.land/p/demo/avl"` refers to an on-chain package.

To see concrete implementation details & API references, see the [reference](../../reference/standard-library/overview.md) section.

## Accessing documentation

Apart from the official documentation you are currently reading, you can also access documentation for the standard
libraries in several other different ways. You can obtain a list of all the available standard libraries with the following commands:

```console
$ cd gnovm/stdlibs # go to correct directory

$ find -type d
./testing
./math
./crypto
./crypto/chacha20
./crypto/chacha20/chacha
./crypto/chacha20/rand
./crypto/sha256
./crypto/cipher
...
```

All the packages have automatically generated documentation through the use of the
`gno doc` command, which has similar functionality and features to `go doc`:

```console
$ gno doc encoding/binary
package binary // import "encoding/binary"

Package binary implements simple translation between numbers and byte sequences
and encoding and decoding of varints.

[...]

var BigEndian bigEndian
var LittleEndian littleEndian
type AppendByteOrder interface{ ... }
type ByteOrder interface{ ... }
$ gno doc -u -src encoding/binary littleEndian.AppendUint16
package binary // import "encoding/binary"

func (littleEndian) AppendUint16(b []byte, v uint16) []byte {
return append(b,
byte(v),
byte(v>>8),
)
}
```

`gno doc` will work automatically when used within the Gno repository or any
repository which has a `go.mod` dependency on `github.com/gnolang/gno`.

Another alternative is setting your environment variable `GNOROOT` to point to
where you cloned the Gno repository.

```sh
export GNOROOT=$HOME/gno
```
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion docs/how-to-guides/testing-gno.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ Luckily, the Gno standard library provides ample support for functionality such
time, such as the request caller address, or the calling package address.

You can learn more about these methods, that are importable using the `std` import declaration,
in the [Standard Library](../reference/standard-library.md) reference section.
in the [Standard Library](../concepts/standard-library/overview.md) reference section.

## Conclusion

Expand Down
8 changes: 4 additions & 4 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ In comparison to Ethereum, Gno.land offers distinct advantages:
usability beyond the context of blockchain. In contrast, Solidity is designed specifically for Smart Contracts on the
Ethereum platform.

## Gno.land Documentation Overview
## Using the Gno.land Documentation

Gno.land's documentation adopts the [Diataxis](https://diataxis.fr/) framework, ensuring structured and predictable content. It includes:

- A [Getting Started](./getting-started/local-setup.md) section, covering simple instructions on how to begin your journey into Gno.land.
- Concise how-to guides for specific technical tasks.
- Conceptual explanations, offering context and usage insights.
- Detailed reference sections with implementation specifics.
- Tutorials aimed at beginners to build fundamental skills in using Gno.land.
- Concise how-to guides for specific technical tasks.
- Tutorials aimed at beginners to build fundamental skills for developing in Gno.land.
Loading

0 comments on commit c0491ef

Please sign in to comment.