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(gno.land): support setting custom height/timestamp for genesis txs #2751

Open
wants to merge 55 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
14cea4f
wip: issue 2283 txtar
thehowl Jun 10, 2024
dcffca3
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/issue…
thehowl Jul 3, 2024
a132452
current status dump
thehowl Jul 3, 2024
bf9a692
txtar for test4 issue
thehowl Jul 18, 2024
33c580c
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/issue…
thehowl Jul 25, 2024
57f6f47
chain init and restart now works!
thehowl Jul 26, 2024
ed84bf7
testing fixes, etc.
thehowl Jul 26, 2024
4b318a0
fixup gnodev
thehowl Jul 26, 2024
7be7e37
testing
thehowl Jul 26, 2024
48593e7
fixup
thehowl Jul 26, 2024
74df3ea
fix gnodev tests
thehowl Jul 26, 2024
a7db07a
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/issue…
thehowl Aug 2, 2024
dfab57a
fix vm tests
thehowl Aug 2, 2024
4f91956
add back clearobjectcache
thehowl Aug 2, 2024
0cab316
Merge branch 'dev/morgan/cache-types-test4-mess' into dev/morgan/issu…
thehowl Aug 5, 2024
a45d9c5
fixup gnoland restart
thehowl Aug 5, 2024
818558f
some reorg, add comments
thehowl Aug 20, 2024
e360017
refactors; add tests for pkg/gnoland
thehowl Aug 20, 2024
63641a1
add test for `gnoland start`, and docs
thehowl Aug 20, 2024
738ff0b
move restart.txtar to pkg/integration tests
thehowl Aug 20, 2024
7baf0de
unit tests for option setters
thehowl Aug 20, 2024
a38ff80
fix ci
thehowl Aug 20, 2024
b0a67ae
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/issue…
thehowl Aug 20, 2024
3e6bb61
further gnoland app tests
thehowl Aug 20, 2024
1347c5f
benchmark and test out "stackable" maps
thehowl Aug 21, 2024
84ad015
add txLogMap as the default solution
thehowl Aug 21, 2024
2235cb6
clone test for txLogMap
thehowl Aug 21, 2024
9a1ba12
oops
thehowl Aug 21, 2024
40fae48
move panics into methods of transactionStore
thehowl Aug 21, 2024
6e2591a
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/issue…
thehowl Aug 23, 2024
bcf0e0e
codereview changes
thehowl Aug 23, 2024
b859b07
add newGnoTransactionStore
thehowl Aug 23, 2024
78552f8
codereview changes
thehowl Aug 23, 2024
73af9c4
add tests for iterators
thehowl Aug 23, 2024
cba4c0f
refactor: move txLogMap into own internal package
thehowl Aug 23, 2024
cf76081
some consistency
thehowl Aug 23, 2024
00297d1
remove hashMap data type
thehowl Aug 23, 2024
bd3bf21
more tests
thehowl Aug 23, 2024
0909738
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/issue…
thehowl Aug 26, 2024
5b9d8d5
more comments / examples on txlog
thehowl Aug 26, 2024
8eb0ce4
add gnoland start -non-validator; and failing test.
thehowl Aug 26, 2024
7109b8a
fix bug reported by milos
thehowl Aug 26, 2024
b00508f
add test for CopyFromCachedStore
thehowl Aug 26, 2024
d16d560
add a test for NewApp
thehowl Aug 26, 2024
df9d708
split up
thehowl Aug 27, 2024
2b2c7da
correctly use gas stores
thehowl Aug 28, 2024
26afca5
fix more tests
thehowl Aug 28, 2024
c027067
return error in the case of nil AppStates
thehowl Aug 28, 2024
5799ce1
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/issue…
thehowl Sep 2, 2024
7c67a14
feat(gno.land): support setting custom height/timestamp for genesis txs
thehowl Sep 2, 2024
41b441d
fix ci
thehowl Sep 2, 2024
1869855
remove calls from mock
thehowl Sep 2, 2024
6a1a82f
Merge branch 'dev/morgan/issue-2283' into dev/morgan/portal-loop-time…
thehowl Sep 3, 2024
55a8d4a
more coverage
thehowl Sep 3, 2024
ed68afa
Merge branch 'master' of github.com:gnolang/gno into dev/morgan/porta…
thehowl Sep 12, 2024
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
38 changes: 34 additions & 4 deletions gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package gnoland

import (
"errors"
"fmt"
"log/slog"
"path/filepath"
Expand Down Expand Up @@ -71,6 +72,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) {
if err := cfg.validate(); err != nil {
return nil, err
}
cfg.setDefaults()

// Capabilities keys.
mainKey := store.NewStoreKey("main")
Expand Down Expand Up @@ -121,6 +123,9 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) {
// the tx - in other words, data from a failing transaction won't be persisted
// to the gno store caches.
baseApp.SetBeginTxHook(func(ctx sdk.Context) sdk.Context {
if icc.beginTxHook != nil && ctx.BlockHeight() == 0 {
ctx = icc.beginTxHook(ctx)
}
// Create Gno transaction store.
return vmk.MakeGnoTransactionStore(ctx)
})
Expand Down Expand Up @@ -229,10 +234,16 @@ type InitChainerConfig struct {
vmKpr vm.VMKeeperI
acctKpr auth.AccountKeeperI
bankKpr bank.BankKeeperI

// This is used by InitChainer, to set a different beginTxHook on each
// transaction. This allows the InitChainer to pass additional data down
// to the underlying app, such as custom Time/Height for genesis.
// (See [GnoGenesisState]).
beginTxHook func(sdk.Context) sdk.Context
}

// InitChainer is the function that can be used as a [sdk.InitChainer].
func (cfg InitChainerConfig) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
func (cfg *InitChainerConfig) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
start := time.Now()
ctx.Logger().Debug("InitChainer: started")

Expand Down Expand Up @@ -263,7 +274,7 @@ func (cfg InitChainerConfig) InitChainer(ctx sdk.Context, req abci.RequestInitCh
}
}

func (cfg InitChainerConfig) loadStdlibs(ctx sdk.Context) {
func (cfg *InitChainerConfig) loadStdlibs(ctx sdk.Context) {
// cache-wrapping is necessary for non-validator nodes; in the tm2 BaseApp,
// this is done using BaseApp.cacheTxContext; so we replicate it here.
ms := ctx.MultiStore()
Expand All @@ -281,7 +292,12 @@ func (cfg InitChainerConfig) loadStdlibs(ctx sdk.Context) {
msCache.MultiWrite()
}

func (cfg InitChainerConfig) loadAppState(ctx sdk.Context, appState any) ([]abci.ResponseDeliverTx, error) {
func (cfg *InitChainerConfig) loadAppState(ctx sdk.Context, appState any) ([]abci.ResponseDeliverTx, error) {
defer func() {
// Ensure to reset beginTxHook to nil when wrapping up.
cfg.beginTxHook = nil
}()

state, ok := appState.(GnoGenesisState)
if !ok {
return nil, fmt.Errorf("invalid AppState of type %T", appState)
Expand All @@ -297,9 +313,23 @@ func (cfg InitChainerConfig) loadAppState(ctx sdk.Context, appState any) ([]abci
}
}

if len(state.TxContexts) != 0 && len(state.Txs) != len(state.TxContexts) {
return nil, errors.New("genesis state tx_contexts, if given, should be of same length as txs")
}

txResponses := make([]abci.ResponseDeliverTx, 0, len(state.Txs))
// Run genesis txs
for _, tx := range state.Txs {
for idx, tx := range state.Txs {
if len(state.TxContexts) > 0 {
customExecCtx := state.TxContexts[idx]
if customExecCtx.Timestamp == 0 {
customExecCtx.Timestamp = ctx.BlockTime().Unix()
}
cfg.beginTxHook = func(ctx sdk.Context) sdk.Context {
return vm.InjectExecContextCustom(ctx, customExecCtx)
}
}

res := cfg.baseApp.Deliver(tx)
if res.IsErr() {
ctx.Logger().Error(
Expand Down
164 changes: 139 additions & 25 deletions gno.land/pkg/gnoland/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"testing"
"time"
Expand All @@ -26,6 +27,18 @@ import (
"github.com/stretchr/testify/require"
)

func testRequestInitChain(genState GnoGenesisState) abci.RequestInitChain {
return abci.RequestInitChain{
Time: time.Now(),
ChainID: "dev",
ConsensusParams: &abci.ConsensusParams{
Block: defaultBlockParams(),
},
Validators: []abci.ValidatorUpdate{},
AppState: genState,
}
}

// Tests that NewAppWithOptions works even when only providing a simple DB.
func TestNewAppWithOptions(t *testing.T) {
t.Parallel()
Expand All @@ -37,34 +50,26 @@ func TestNewAppWithOptions(t *testing.T) {
assert.Equal(t, "gnoland", bapp.Name())

addr := crypto.AddressFromPreimage([]byte("test1"))
resp := bapp.InitChain(abci.RequestInitChain{
Time: time.Now(),
ChainID: "dev",
ConsensusParams: &abci.ConsensusParams{
Block: defaultBlockParams(),
},
Validators: []abci.ValidatorUpdate{},
AppState: GnoGenesisState{
Balances: []Balance{
{
Address: addr,
Amount: []std.Coin{{Amount: 1e15, Denom: "ugnot"}},
},
resp := bapp.InitChain(testRequestInitChain(GnoGenesisState{
Balances: []Balance{
{
Address: addr,
Amount: []std.Coin{{Amount: 1e15, Denom: "ugnot"}},
},
Txs: []std.Tx{
{
Msgs: []std.Msg{vm.NewMsgAddPackage(addr, "gno.land/r/demo", []*std.MemFile{
{
Name: "demo.gno",
Body: "package demo; func Hello() string { return `hello`; }",
},
})},
Fee: std.Fee{GasWanted: 1e6, GasFee: std.Coin{Amount: 1e6, Denom: "ugnot"}},
Signatures: []std.Signature{{}}, // one empty signature
},
},
Txs: []std.Tx{
{
Msgs: []std.Msg{vm.NewMsgAddPackage(addr, "gno.land/r/demo", []*std.MemFile{
{
Name: "demo.gno",
Body: "package demo; func Hello() string { return `hello`; }",
},
})},
Fee: std.Fee{GasWanted: 1e6, GasFee: std.Coin{Amount: 1e6, Denom: "ugnot"}},
Signatures: []std.Signature{{}}, // one empty signature
},
},
})
}))
require.True(t, resp.IsOK(), "InitChain response: %v", resp)

tx := amino.MustMarshal(std.Tx{
Expand Down Expand Up @@ -116,6 +121,115 @@ func TestNewApp(t *testing.T) {
assert.True(t, resp.IsOK(), "resp is not OK: %v", resp)
}

func TestNewAppWithOptions_WithTxContexts(t *testing.T) {
t.Parallel()

appOpts := TestAppOptions(memdb.NewMemDB())
called := 0
appOpts.GenesisTxResultHandler = func(ctx sdk.Context, _ std.Tx, res sdk.Result) {
if !res.IsOK() {
t.Fatal(res)
}
called++
switch called {
case 1:
assert.Equal(t, string(res.Data), "time.Now() 7331\nstd.GetHeight() -1337\n")
case 2:
assert.Equal(t, string(res.Data), "time.Now() "+strconv.FormatInt(ctx.BlockTime().Unix(), 10)+"\nstd.GetHeight() 13377331\n")
}
}
app, err := NewAppWithOptions(appOpts)
require.NoError(t, err)
bapp := app.(*sdk.BaseApp)

const pkgFile = `package main

import (
"std"
"time"
)

func main() {
println("time.Now()", time.Now().Unix())
println("std.GetHeight()", std.GetHeight())
}
`

addr := crypto.AddressFromPreimage([]byte("test1"))
resp := bapp.InitChain(testRequestInitChain(GnoGenesisState{
Balances: []Balance{
{
Address: addr,
Amount: []std.Coin{{Amount: 1e15, Denom: "ugnot"}},
},
},
Txs: []std.Tx{
{
Msgs: []std.Msg{vm.NewMsgRun(addr, std.Coins{}, []*std.MemFile{
{
Name: "demo.gno",
Body: pkgFile,
},
})},
Fee: std.Fee{GasWanted: 1e6, GasFee: std.Coin{Amount: 1e6, Denom: "ugnot"}},
Signatures: []std.Signature{{}}, // one empty signature
},
{
// same as previous, but will have different TxContext.
Msgs: []std.Msg{vm.NewMsgRun(addr, std.Coins{}, []*std.MemFile{
{
Name: "demo.gno",
Body: pkgFile,
},
})},
Fee: std.Fee{GasWanted: 1e6, GasFee: std.Coin{Amount: 1e6, Denom: "ugnot"}},
Signatures: []std.Signature{{}}, // one empty signature
},
},
TxContexts: []vm.ExecContextCustom{
{
Height: -1337,
Timestamp: 7331,
},
{
// different height; timestamp=0 will mean that InitChain will auto-set it
// to the block time.
Height: 13377331,
Timestamp: 0,
},
},
}))
require.True(t, resp.IsOK(), "InitChain response: %v", resp)
}

func TestNewAppWithOptions_InvalidTxContexts(t *testing.T) {
t.Parallel()

app, err := NewAppWithOptions(testAppOptions())
require.NoError(t, err)
bapp := app.(*sdk.BaseApp)

addr := crypto.AddressFromPreimage([]byte("test1"))
resp := bapp.InitChain(testRequestInitChain(GnoGenesisState{
Balances: []Balance{
{
Address: addr,
Amount: []std.Coin{{Amount: 1e15, Denom: "ugnot"}},
},
},
Txs: []std.Tx{
// one tx
{},
},
TxContexts: []vm.ExecContextCustom{
// two contexts
{}, {},
},
}))
assert.True(t, resp.IsErr())
assert.ErrorContains(t, resp.Error, "genesis state tx_contexts, if given, should be of same length as txs")
}

// Test whether InitChainer calls to load the stdlibs correctly.
func TestInitChainer_LoadStdlib(t *testing.T) {
t.Parallel()
Expand Down
3 changes: 3 additions & 0 deletions gno.land/pkg/gnoland/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gnoland
import (
"errors"

"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/gno/tm2/pkg/std"
)

Expand All @@ -22,4 +23,6 @@ func ProtoGnoAccount() std.Account {
type GnoGenesisState struct {
Balances []Balance `json:"balances"`
Txs []std.Tx `json:"txs"`
// Should match len(Txs), or be null
TxContexts []vm.ExecContextCustom `json:"tx_contexts"`
}
Loading
Loading