This is a short tutorial on how to get started with Web3 programming using Gnolang and Gnoland.
You only need some basic Go programming language knowledge.
Gnolang an interpreted variant of the Golang language that is built for Web3 programming.
This means that you can directly transfer your Go skills to Web3 development.
Install the latest version of Go if you haven't already.
On Linux:
go version
>> go version go1.21.1 linux/amd64
And on macOS:
go version
>> go version go1.21.1 darwin/arm64
For editing the code of this project I will be using Visual Studio Code (VSCode).
The following two VSCode extensions are highly recommended:
However, please feel free to use any other editing environment that you prefer.
Gnoland is a new smart contract platform based on Gnolang, a fork of Golang. Gnolang inherits the concurrency and developer-friendliness of Golang while fully leveraging the rich libraries and packages that are already available. This approach greatly accelerates application development by allowing dapp developers to reuse and re-assemble existing code, similar to how Cosmos simplified blockchain deployment.
Gnoland introduces multiple concepts that together form a scalable, transparent, and sustainable blockchain ecosystem that suggest a viable solution to challenges that existing blockchains are faced with.
Packages encompass functionalities that are more closely aligned with the characteristics and capabilities of realms, as opposed to standard libraries.
- A unit that contains functionalities and utilities that can be used in realms.
- Gnolang Packages are stateless.
- The default import path is
gno.land/p/~~~
. - Can be imported to other realms or packages.
A Gnolang Realm is where the state of an application lives.
- Realms are Smart Contracts in Gnolang.
- Realms are stateful.
- The default import path is
gno.land/r/~~~
. - Each realm has the capability to publicly export the function Render(path string) string, which performs rendering when passed a valid markdown as a parameter for the specified path.
The example project for this tutorial is a simple number guessing game.
In the Go programming language, the essential logic for a command line guessing game program looks as follows:
// Set up the module:
// go mod init github.com/pietergreyling/web3_programming_with_gno/m/v2
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
// Main holds the global state in this program - - - -
func main() {
min, max := 1, 100
tries := 0
correct := false
secret := rand.Intn(max-min) + min
fmt.Println("-- Please guess a number between 1 and 100.")
reader := bufio.NewReader(os.Stdin)
for {
tries++
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("-- Error when reading input!", err)
continue
}
input = strings.TrimSuffix(input, "\n")
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("-- Invalid input - that is not a number!")
fmt.Println("-- Please enter a number.")
continue
}
correct = TestGuess(guess, secret, min, max)
if correct {
break
}
}
fmt.Println("-- You tried", tries, "times!")
}
// No global state is held here - - - -
func TestGuess(guess int, secret int, min int, max int) bool {
correct := false
fmt.Println("-- Your guess is: ", guess)
if guess > secret {
correct = false
fmt.Println("-- That is higher than the secret number.")
fmt.Println("-- Please try again...")
} else if guess < secret {
correct = false
fmt.Println("-- That is lower than the secret number.")
fmt.Println("-- Please try again...")
} else {
correct = true
fmt.Println("-- That is the correct number, congratulations!")
}
return correct
}
Please notice that we have made sure to separate the stateful main function from the stateless number guessing code. This is in order to be in line with the Gnolang concepts of Realms and Packages:
- The stateful main function will become a Realm
- The stateless number guessing code will become a Package
As preparation for porting this to a Gnolang realm and package architecture we run a quick test by executing the following:
cd src_go
go run .
-- Please guess a number between 1 and 100.
50
-- Your guess is: 50
-- That is lower than the secret number.
-- Please try again...
75
-- Your guess is: 75
-- That is higher than the secret number.
-- Please try again...
62
-- Your guess is: 62
-- That is lower than the secret number.
-- Please try again...
68
-- Your guess is: 68
-- That is higher than the secret number.
-- Please try again...
65
-- Your guess is: 65
-- That is lower than the secret number.
-- Please try again...
67
-- Your guess is: 67
-- That is the correct number, congratulations!
-- You tried 6 times!
We have now verified that the program is working as expected.
The dependencies below are required to develop applications for the Gno platform.
-
Install gofumpt.
go install mvdan.cc/gofumpt@latest
-
Install Gno.
I will use this method:
cd [YOUR_PROJECT_ROOT_DIRECTORY] git clone https://github.com/gnolang/gno.git cd ./gno make install_gno
But one can also install gno like this:
go install github.com/gnolang/gno/gnovm/cmd/gno
-
Install Gnokey.
cd ./gno make install_gnokey
-
Install Gnoland.
cd ./gno/gno.land make install.gnoland
-
Install Gnoweb (install and run a local gnoland instance first).
cd ./gno/gno.land make install.gnoweb
For verification, our installation output should look similar to the following:
go install mvdan.cc/gofumpt@latest
>> go: downloading mvdan.cc/gofumpt v0.5.0
>> go: downloading golang.org/x/sync v0.1.0
>> go: downloading golang.org/x/mod v0.10.0
>> go: downloading golang.org/x/tools v0.8.0
make install_gno
>> make --no-print-directory -C ./gnovm install
>> go install ./cmd/gno
>> [+] 'gno' is installed. more info in ./gnovm/.
make install_gnokey
>> make --no-print-directory -C ./gno.land install.gnokey
>> go install ./cmd/gnokey
>> [+] 'gnokey' is installed. more info in ./gno.land/.
make install.gnoland
>> go install ./cmd/gnoland
make install.gnoweb
>> go install ./cmd/gnoweb
>> go: downloading github.com/gorilla/mux v1.8.0
>> go: downloading github.com/gotuna/gotuna v0.6.0
>> go: downloading github.com/gorilla/sessions v1.2.1
>> go: downloading github.com/gorilla/securecookie v1.1.1
We can now ask for help from the Gno tools:
gno help
gnokey help
gnoland help
Starting Gnoland:
cd ./gno/gno.land
gnoland start
>> .level 1 .msg Starting multiAppConn [impl multiAppConn [module proxy]]
>> .level 1 .msg Starting localClient [[impl localClient [module abci-client connection query]] [module proxy]]
.....
Starting Gnoweb:
gnoweb
>> Running on http://127.0.0.1:8888
Navigating to Gnoweb at http://127.0.0.1:8888 with a browser should present the following:
Currently, in order to create our Gnolang package and realm .gno
code files, we have to be aware of the following folders:
# the root
gno/examples/gno.land
# the folder for packages
gno/examples/gno.land/p/demo
# the folder for realms
gno/examples/gno.land/r/demo
# the folder tree
├── gno
│ ├── examples
│ │ └── gno.land
│ │ ├── p
│ │ │ └── demo
│ │ │ ├── ...
│ │ │
│ │ └── r
│ │ ├── demo
│ │ │ ├── ...
This is where our gnolang source code will be:
# the folder for our packages
gno/examples/gno.land/p/demo/guess
# the folder for our realm and tests
gno/examples/gno.land/r/demo/guess
# the folder tree
├── gno
│ ├── examples
│ │ └── gno.land
│ │ ├── p
│ │ │ └── demo
│ │ │ └── guess
│ │ └── r
│ │ └── demo
│ │ └── guess
I have duplicated the directory tree and code files as follows in this repository. This is for convenience and in order to not push any changes to the gno repository since it is a sub-repository/submodule of the tutorial repository. However, our /demo/guess
code still needs to be run from within the cloned /gno
repository.
# the root
src_gno/gno/examples/gno.land
# the folder for our packages
src_gno/gno/examples/gno.land/p/demo/guess
# the folder for our realm and tests
src_gno/gno/examples/gno.land/r/demo/guess
# the folder tree
├── src_gno
│ ├── gno
│ │ ├── examples
│ │ │ └── gno.land
│ │ │ ├── p
│ │ │ │ └── demo
│ │ │ │ └── guess
│ │ │ └── r
│ │ │ └── demo
│ │ │ └── guess