Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add InMemory gnoland node #1241

Merged
merged 47 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f01ad76
wip: gnoland inmemory
gfanton Oct 10, 2023
676523e
Merge remote-tracking branch 'origin/master' into feat/inmemory-gnoland
gfanton Oct 11, 2023
db5a4c2
Revert "fix(make): disable _test.gnoweb temporarily (#1223)"
gfanton Oct 11, 2023
3b71cc6
feat: add integration node
gfanton Oct 12, 2023
9077ca1
chore: cleanup
gfanton Oct 12, 2023
3a3133c
wip: gnoweb
gfanton Oct 12, 2023
06ecc62
chore: lint
gfanton Oct 12, 2023
84907d1
chore: add some comments
gfanton Oct 12, 2023
8e8bed2
fix: gnoweb tests
gfanton Oct 12, 2023
d510140
fix: enable gnoweb test on the CI
gfanton Oct 12, 2023
d967a5c
Merge branch 'master' into feat/inmemory-gnoland
gfanton Oct 12, 2023
3221a1c
feat: add testing node
gfanton Oct 12, 2023
92de256
chore: remove useless comment
gfanton Oct 12, 2023
2003899
feat: add testing helper
gfanton Oct 12, 2023
e0b59fd
chore: lint
gfanton Oct 12, 2023
6d761d7
wip: testing integration inmemory node
gfanton Oct 13, 2023
19cfdb0
chore: lint
gfanton Oct 16, 2023
7300c4e
fix(integration): waiting for node readiness
gfanton Oct 19, 2023
7ee015b
chore: lint
gfanton Oct 19, 2023
aee3dd9
chore: improve comments
gfanton Oct 20, 2023
a0dd859
Merge remote-tracking branch 'origin/master' into feat/inmemory-gnoland
gfanton Oct 21, 2023
2f8bc50
fix: update InMemoryNodeConfig
gfanton Oct 21, 2023
ff0aef2
chore: fix & lint
gfanton Oct 25, 2023
3584d86
Update gno.land/cmd/gnoland/start.go
gfanton Oct 26, 2023
9fa209b
Merge branch 'master' into feat/inmemory-gnoland
gfanton Oct 26, 2023
a580f54
chore: cleanup
gfanton Oct 27, 2023
d9bcf35
chore: add back previous gnoweb flag
gfanton Oct 27, 2023
daaa7a3
fix: fix gnoweb analytics test
gfanton Oct 27, 2023
81afbf4
chore: lint
gfanton Oct 27, 2023
8df3622
fix: add minimal and full config
gfanton Oct 27, 2023
f6d4b24
fix: creator balances
gfanton Oct 27, 2023
fbe0300
fix: lint
gfanton Oct 27, 2023
e1058ef
Merge branch 'master' into feat/inmemory-gnoland
gfanton Oct 27, 2023
f747694
chore: lint
gfanton Oct 27, 2023
248b083
fix: remove panic & fix potential nil error
gfanton Oct 27, 2023
0200561
Merge branch 'master' into feat/inmemory-gnoland
gfanton Oct 29, 2023
92ce8a8
chore: lint
gfanton Oct 30, 2023
77b416b
fix: use gnoland.Balance in `genesis` command
gfanton Oct 30, 2023
83321a8
Merge branch 'master' into feat/inmemory-gnoland
gfanton Nov 6, 2023
cc3fe3e
fix: use amino marshaler
gfanton Nov 6, 2023
41ef47c
fix: amount can be empty
gfanton Nov 6, 2023
d80231c
fix: rename Balance.Value into Balance.Amount
gfanton Nov 6, 2023
5b23e9f
chore: rename
gfanton Nov 6, 2023
6292f7b
chore: remove gnoroot singleton
gfanton Nov 6, 2023
844995a
chore: lint
gfanton Nov 7, 2023
f700c81
Merge branch 'master' into feat/inmemory-gnoland
gfanton Nov 7, 2023
a86eabc
Merge branch 'master' into feat/inmemory-gnoland
gfanton Nov 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/workflows/gnoland.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ jobs:
- _test.gnoland
- _test.gnokey
- _test.pkgs
# XXX: test broken, should be rewritten to run an inmemory localnode
# Re-add to makefile when fixed. Tracked here: https://github.com/gnolang/gno/issues/1222
#- _test.gnoweb
- _test.gnoweb # this test should be rewritten to run an inmemory localnode
gfanton marked this conversation as resolved.
Show resolved Hide resolved
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand All @@ -78,7 +76,7 @@ jobs:
export LOG_DIR="${{ runner.temp }}/logs/test-${{ matrix.goversion }}-gnoland"
make ${{ matrix.args }}
- name: Upload Test Log
if: always()
if: always()
uses: actions/upload-artifact@v3
with:
name: logs-test-gnoland-go${{ matrix.goversion }}
Expand All @@ -101,7 +99,7 @@ jobs:
uses: codecov/codecov-action@v3
with:
directory: ${{ runner.temp }}/coverage
token: ${{ secrets.CODECOV_TOKEN }}
token: ${{ secrets.CODECOV_TOKEN }}
moul marked this conversation as resolved.
Show resolved Hide resolved
fail_ci_if_error: ${{ github.repository == 'gnolang/gno' }}

docker-integration:
Expand Down
4 changes: 1 addition & 3 deletions gno.land/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ fmt:
########################################
# Test suite
.PHONY: test
test: _test.gnoland _test.gnokey _test.pkgs
# XXX: _test.gnoweb is currently disabled. If fixed, re-enable here and in CI.
# https://github.com/gnolang/gno/issues/1222
test: _test.gnoland _test.gnoweb _test.gnokey _test.pkgs

GOTEST_FLAGS ?= -v -p 1 -timeout=30m

Expand Down
11 changes: 11 additions & 0 deletions gno.land/cmd/gnoweb/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"strings"
"testing"

"github.com/gnolang/gno/gno.land/pkg/gnoland"
"github.com/gnolang/gno/gno.land/pkg/integration"
"github.com/gnolang/gno/tm2/pkg/log"
"github.com/gotuna/gotuna/test/assert"
)

Expand Down Expand Up @@ -40,6 +43,14 @@ func TestRoutes(t *testing.T) {
} else {
panic("os.Getwd() -> err: " + err.Error())
}

config := integration.DefaultTestingConfig(t, gnoland.MustGuessGnoRootDir())
node := integration.TestingInMemoryNode(t, log.NewNopLogger(), config)
defer node.Stop()

// XXX: this is ugly :(
gfanton marked this conversation as resolved.
Show resolved Hide resolved
flags.remoteAddr = node.Config().RPC.ListenAddress

app := makeApp()

for _, r := range routes {
Expand Down
61 changes: 49 additions & 12 deletions gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package gnoland

import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"

"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/gno/tm2/pkg/amino"
Expand Down Expand Up @@ -36,7 +39,7 @@ func NewAppOptions() *AppOptions {
return &AppOptions{
Logger: log.NewNopLogger(),
DB: dbm.NewMemDB(),
GnoRootDir: GuessGnoRootDir(),
GnoRootDir: MustGuessGnoRootDir(),
}
}

Expand Down Expand Up @@ -73,6 +76,8 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) {
// Construct keepers.
acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount)
bankKpr := bank.NewBankKeeper(acctKpr)

// XXX: Embed this ?
stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs")
vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles)

Expand Down Expand Up @@ -142,10 +147,9 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank
genState := req.AppState.(GnoGenesisState)
// Parse and set genesis state balances.
for _, bal := range genState.Balances {
addr, coins := parseBalance(bal)
acc := acctKpr.NewAccountWithAddress(ctx, addr)
acc := acctKpr.NewAccountWithAddress(ctx, bal.Address)
acctKpr.SetAccount(ctx, acc)
err := bankKpr.SetCoins(ctx, addr, coins)
err := bankKpr.SetCoins(ctx, bal.Address, bal.Value)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -195,24 +199,57 @@ func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock
}
}

func GuessGnoRootDir() string {
var rootdir string
// XXX: should be removed in favor of https://github.com/gnolang/gno/pull/1233
var (
guessOnce sync.Once
gnoroot string
gfanton marked this conversation as resolved.
Show resolved Hide resolved
)

func MustGuessGnoRootDir() string {
root, err := guessGnoRootDir()
if err != nil {
panic(err)
}

return root
}

func GuessGnoRootDir() (string, error) {
var err error
guessOnce.Do(func() {
gnoroot, err = guessGnoRootDir()
})

return gnoroot, err
}

func guessGnoRootDir() (string, error) {
// First try to get the root directory from the GNOROOT environment variable.
if rootdir = os.Getenv("GNOROOT"); rootdir != "" {
return filepath.Clean(rootdir)
if rootdir := os.Getenv("GNOROOT"); rootdir != "" {
return filepath.Clean(rootdir), nil
}

// Try to guess GNOROOT using the nearest go.mod.
if gobin, err := exec.LookPath("go"); err == nil {
// If GNOROOT is not set, try to guess the root directory using the `go list` command.
cmd := exec.Command(gobin, "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno")
out, err := cmd.CombinedOutput()
if err != nil {
panic(fmt.Errorf("invalid gno directory %q: %w", rootdir, err))
if err == nil {
return strings.TrimSpace(string(out)), nil
}
}

return strings.TrimSpace(string(out))
// Try to guess GNOROOT using caller stack.
if _, filename, _, ok := runtime.Caller(1); ok && filepath.IsAbs(filename) {
if currentDir := filepath.Dir(filename); currentDir != "" {
// Gno root directory relative from `app.go` path:
// gno/ .. /gno.land/ .. /pkg/ .. /gnoland/app.go
rootdir, err := filepath.Abs(filepath.Join(currentDir, "..", "..", ".."))
if err == nil {
return rootdir, nil
}
}
}

panic("no go binary available, unable to determine gno root-dir path")
return "", errors.New("unable to guess gno's root-directory")
}
79 changes: 79 additions & 0 deletions gno.land/pkg/gnoland/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package gnoland

import (
"errors"
"fmt"
"strings"

"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/crypto"
osm "github.com/gnolang/gno/tm2/pkg/os"
"github.com/gnolang/gno/tm2/pkg/std"
)

func LoadGenesisBalancesFile(path string) ([]Balance, error) {
// each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot
content := osm.MustReadFile(path)
lines := strings.Split(string(content), "\n")

balances := make([]Balance, 0, len(lines))
for _, line := range lines {
line = strings.TrimSpace(line)

// remove comments.
line = strings.Split(line, "#")[0]
line = strings.TrimSpace(line)

// skip empty lines.
if line == "" {
continue
}

parts := strings.Split(line, "=") // <address>=<coin>
if len(parts) != 2 {
return nil, errors.New("invalid genesis_balance line: " + line)
}

addr, err := crypto.AddressFromBech32(parts[0])
if err != nil {
return nil, fmt.Errorf("invalid balance addr %s: %w", parts[0], err)
}

coins, err := std.ParseCoins(parts[1])
if err != nil {
return nil, fmt.Errorf("invalid balance coins %s: %w", parts[1], err)
}

balances = append(balances, Balance{
Address: addr,
Value: coins,
})
}

return balances, nil
}

// XXX: we can do something better here
func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]std.Tx, error) {
txs := []std.Tx{}
txsBz := osm.MustReadFile(path)
txsLines := strings.Split(string(txsBz), "\n")
for _, txLine := range txsLines {
if txLine == "" {
continue // Skip empty line.
}

// Patch the TX.
txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID)
txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote)

var tx std.Tx
if err := amino.UnmarshalJSON([]byte(txLine), &tx); err != nil {
return nil, fmt.Errorf("unable to Unmarshall txs file: %w", err)
}

txs = append(txs, tx)
}

return txs, nil
}
57 changes: 55 additions & 2 deletions gno.land/pkg/gnoland/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package gnoland

import (
"errors"
"fmt"
"strings"

bft "github.com/gnolang/gno/tm2/pkg/bft/types"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/std"
)

Expand All @@ -13,6 +19,53 @@ func ProtoGnoAccount() std.Account {
}

type GnoGenesisState struct {
Balances []string `json:"balances"`
Txs []std.Tx `json:"txs"`
Balances []Balance `json:"balances"`
Txs []std.Tx `json:"txs"`
}

type Balance struct {
Address bft.Address
Value std.Coins
}

func (b *Balance) Parse(line string) error {
parts := strings.Split(strings.TrimSpace(line), "=") // <address>=<coin>
if len(parts) != 2 {
return errors.New("invalid genesis_balance line: " + line)
}

var err error

b.Address, err = crypto.AddressFromBech32(parts[0])
if err != nil {
return fmt.Errorf("invalid balance addr %s: %w", parts[0], err)
}

b.Value, err = std.ParseCoins(parts[1])
if err != nil {
return fmt.Errorf("invalid balance coins %s: %w", parts[1], err)
}

return nil
}

func (b *Balance) UnmarshalJSON(data []byte) error {
gfanton marked this conversation as resolved.
Show resolved Hide resolved
return b.Parse(string(data))
}
func (b *Balance) Marshaljson() ([]byte, error) {
gfanton marked this conversation as resolved.
Show resolved Hide resolved
return []byte(b.String()), nil
}

func (b Balance) String() string {
return fmt.Sprintf("%s=%s", b.Address.String(), b.Value.String())
}

// type Balances []Balance

// func (bs Balances) Strings() []string {
// bss := make([]string, len(bs))
// for i, balance := range bs {
// bss[i] = balance.String()
// }
// return bss
// }
Loading
Loading