diff --git a/gno.land/pkg/integration/testdata/addpkg_private_basic.txtar b/gno.land/pkg/integration/testdata/addpkg_private_basic.txtar new file mode 100644 index 00000000000..5000eb35697 --- /dev/null +++ b/gno.land/pkg/integration/testdata/addpkg_private_basic.txtar @@ -0,0 +1,81 @@ +# Test private realm basic functionality + +# start a new node +gnoland start + +# add a public realm +gnokey maketx addpkg -pkgdir $WORK/publicrealm -pkgpath gno.land/r/foobar/publicrealm -gas-fee 10000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test $test1_user_addr +stdout OK! + +# update the public realm +! gnokey maketx addpkg -pkgdir $WORK/publicrealm -pkgpath gno.land/r/foobar/publicrealm -gas-fee 10000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test $test1_user_addr +stderr 'package already exists' + +# add a private realm +gnokey maketx addpkg -pkgdir $WORK/privaterealm -pkgpath gno.land/r/foobar/privaterealm -gas-fee 10000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test $test1_user_addr +stdout OK! + +# update the same private realm +gnokey maketx addpkg -pkgdir $WORK/privaterealm -pkgpath gno.land/r/foobar/privaterealm -gas-fee 10000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test $test1_user_addr +stdout OK! + +# ensure A is present +gnokey query vm/qeval --data "gno.land/r/foobar/privaterealm.A" +stdout '123' + +# update the edited private realm over the original +gnokey maketx addpkg -pkgdir $WORK/privaterealmedited -pkgpath gno.land/r/foobar/privaterealm -gas-fee 10000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test $test1_user_addr +stdout OK! + +# ensure A is deleted +! gnokey query vm/qeval --data "gno.land/r/foobar/privaterealm.A" +stderr 'name A not declared' + +# ensure B is present +gnokey query vm/qeval --data "gno.land/r/foobar/privaterealm.B" +stdout '456' + +# overwrite public realm with private realm +! gnokey maketx addpkg -pkgdir $WORK/privaterealm -pkgpath gno.land/r/foobar/publicrealm -gas-fee 10000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test $test1_user_addr +stderr 'package already exists' + +# overwrite private realm with public realm +! gnokey maketx addpkg -pkgdir $WORK/publicrealm -pkgpath gno.land/r/foobar/privaterealm -gas-fee 10000000ugnot -gas-wanted 20000000 -broadcast -chainid=tendermint_test $test1_user_addr +stderr 'a private package cannot be overridden by a public package' + +-- privaterealm/gnomod.toml -- +module = "gno.land/r/foobar/privaterealm" +gno = "0.9" +private = true + +-- privaterealm/privaterealm.gno -- +package test +var A = "123" + +func Echo(cur realm) string { + return "hello private world" +} + +-- privaterealmedited/gnomod.toml -- +module = "gno.land/r/foobar/privaterealm" +gno = "0.9" +private = true + +-- privaterealmedited/privaterealm.gno -- +package test +var B = "456" + +func Echo(cur realm) string { + return "hello private edited world" +} + +-- publicrealm/gnomod.toml -- +module = "gno.land/r/foobar/publicrealm" +gno = "0.9" + +-- publicrealm/publicrealm.gno -- +package test + +func Echo(cur realm) string { + return "hello public world" +} diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index c644ee6b1b9..564cb45cb22 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -429,9 +429,12 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { if !strings.HasPrefix(pkgPath, chainDomain+"/") { return ErrInvalidPkgPath("invalid domain: " + pkgPath) } - if pv := gnostore.GetPackage(pkgPath, false); pv != nil { + + pv := gnostore.GetPackage(pkgPath, false) + if pv != nil && !pv.Private { return ErrPkgAlreadyExists("package already exists: " + pkgPath) } + if !gno.IsRealmPath(pkgPath) && !gno.IsPPackagePath(pkgPath) { return ErrInvalidPkgPath("package path must be valid realm or p package path") } @@ -465,6 +468,9 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { if gm.HasReplaces() { return ErrInvalidPackage("development packages are not allowed") } + if pv != nil && pv.Private && !gm.Private { + return ErrInvalidPackage("a private package cannot be overridden by a public package") + } if gm.Private && !gno.IsRealmPath(pkgPath) { return ErrInvalidPackage("private packages must be realm packages") } diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go index 464c68df971..381a84bf5e9 100644 --- a/gno.land/pkg/sdk/vm/keeper_test.go +++ b/gno.land/pkg/sdk/vm/keeper_test.go @@ -260,6 +260,75 @@ func Echo(cur realm) string { assert.NoError(t, err) } +func TestVMKeeperAddPackage_UpdatePrivatePackage(t *testing.T) { + env := setupTestEnv() + ctx := env.vmk.MakeGnoTransactionStore(env.ctx) + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bankk.SetCoins(ctx, addr, initialBalance) + assert.True(t, env.bankk.GetCoins(ctx, addr).IsEqual(initialBalance)) + + // Create private test package. + const pkgPath = "gno.land/r/test" + files := []*std.MemFile{ + { + Name: "gnomod.toml", + Body: `module = "gno.land/r/test" +gno = "0.9" +private = true`, + }, + { + Name: "test.gno", + Body: `package test + +func Echo(cur realm) string { + return "hello world" +}`, + }, + } + + msg1 := NewMsgAddPackage(addr, pkgPath, files) + assert.Nil(t, env.vmk.getGnoTransactionStore(ctx).GetPackage(pkgPath, false)) + err := env.vmk.AddPackage(ctx, msg1) + assert.NoError(t, err) + + // Re-upload the same private package with updated content. + files2 := []*std.MemFile{ + { + Name: "gnomod.toml", + Body: `module = "gno.land/r/test" +gno = "0.9" +private = true`, + }, + { + Name: "test.gno", + Body: `package test + +func Echo(cur realm) string { + return "hello updated world" +}`, + }, + } + + msg2 := NewMsgAddPackage(addr, pkgPath, files2) + err = env.vmk.AddPackage(ctx, msg2) + assert.NoError(t, err) + + // Verify the package was updated with the new content. + store := env.vmk.getGnoTransactionStore(ctx) + memFile := store.GetMemFile(pkgPath, "test.gno") + assert.NotNil(t, memFile) + expected := `package test + +func Echo(cur realm) string { + return "hello updated world" +}` + assert.Equal(t, expected, memFile.Body) +} + func TestVMKeeperAddPackage_ImportPrivate(t *testing.T) { env := setupTestEnv() ctx := env.vmk.MakeGnoTransactionStore(env.ctx) @@ -315,6 +384,111 @@ func Echo(cur realm) string { assert.Error(t, err, ErrTypeCheck(gnolang.ImportPrivateError{PkgPath: pkgPath})) } +func TestVMKeeperAddPackage_ChangePublicToPrivate(t *testing.T) { + env := setupTestEnv() + ctx := env.vmk.MakeGnoTransactionStore(env.ctx) + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bankk.SetCoins(ctx, addr, initialBalance) + assert.True(t, env.bankk.GetCoins(ctx, addr).IsEqual(initialBalance)) + + const pkgPath = "gno.land/r/test" + files := []*std.MemFile{ + {Name: "gnomod.toml", Body: gnolang.GenGnoModLatest(pkgPath)}, + { + Name: "test.gno", + Body: `package test + +func Echo(cur realm) string { + return "hello world" +}`, + }, + } + + msg1 := NewMsgAddPackage(addr, pkgPath, files) + assert.Nil(t, env.vmk.getGnoTransactionStore(ctx).GetPackage(pkgPath, false)) + err := env.vmk.AddPackage(ctx, msg1) + assert.NoError(t, err) + + // Try to upload a private version of the same package. + files2 := []*std.MemFile{ + { + Name: "gnomod.toml", + Body: `module = "gno.land/r/test" +gno = "0.9" +private = true`, + }, + { + Name: "test.gno", + Body: `package test + +func Echo(cur realm) string { + return "hello private world" +}`, + }, + } + + msg2 := NewMsgAddPackage(addr, pkgPath, files2) + err = env.vmk.AddPackage(ctx, msg2) + assert.Error(t, err, ErrInvalidPackage("")) +} + +func TestVMKeeperAddPackage_ChangePrivateToPublic(t *testing.T) { + env := setupTestEnv() + ctx := env.vmk.MakeGnoTransactionStore(env.ctx) + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bankk.SetCoins(ctx, addr, initialBalance) + assert.True(t, env.bankk.GetCoins(ctx, addr).IsEqual(initialBalance)) + + // Create a private test package first. + const pkgPath = "gno.land/r/test" + files := []*std.MemFile{ + { + Name: "gnomod.toml", + Body: `module = "gno.land/r/test" +gno = "0.9" +private = true`, + }, + { + Name: "test.gno", + Body: `package test + +func Echo(cur realm) string { + return "hello private world" +}`, + }, + } + + msg1 := NewMsgAddPackage(addr, pkgPath, files) + assert.Nil(t, env.vmk.getGnoTransactionStore(ctx).GetPackage(pkgPath, false)) + err := env.vmk.AddPackage(ctx, msg1) + assert.NoError(t, err) + + // Try to upload a public version of the same package. + files2 := []*std.MemFile{ + {Name: "gnomod.toml", Body: gnolang.GenGnoModLatest(pkgPath)}, + { + Name: "test.gno", + Body: `package test + +func Echo(cur realm) string { + return "hello public world" +}`, + }, + } + + msg2 := NewMsgAddPackage(addr, pkgPath, files2) + err = env.vmk.AddPackage(ctx, msg2) + assert.Error(t, err, ErrInvalidPackage("")) +} + // Sending total send amount succeeds. func TestVMKeeperOriginSend1(t *testing.T) { env := setupTestEnv()