Skip to content

Commit

Permalink
feat: add genesis packages with transaction signing
Browse files Browse the repository at this point in the history
  • Loading branch information
Villaquiranm committed Dec 5, 2024
1 parent 7ce29ff commit aa15626
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 37 deletions.
76 changes: 63 additions & 13 deletions contribs/gnogenesis/internal/txs/txs_add_packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"flag"
"fmt"

"github.com/gnolang/gno/gnovm/pkg/gnoenv"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"

"github.com/gnolang/gno/gno.land/pkg/gnoland"
"github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot"
Expand All @@ -27,16 +29,32 @@ var (
)

type addPkgCfg struct {
txsCfg *txsCfg
deployerAddress string
txsCfg *txsCfg
keyName string
gnoHome string
insecurePasswordStdin bool
}

func (c *addPkgCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.deployerAddress,
"deployer-address",
defaultCreator.String(),
"the address that will be used to deploy the package",
&c.keyName,
"key-name",
"",
"The package deployer key name or address",
)

fs.StringVar(
&c.gnoHome,
"gno-home",
gnoenv.HomeDir(),
"the gno home directory",
)

fs.BoolVar(
&c.insecurePasswordStdin,
"insecure-password-stdin",
false,
"the gno home directory",
)
}

Expand Down Expand Up @@ -78,14 +96,21 @@ func execTxsAddPackages(

var (
creator = defaultCreator
err error
pass string
)

// Check if the deployer address is set
if cfg.deployerAddress != defaultCreator.String() {
creator, err = crypto.AddressFromString(cfg.deployerAddress)
kb, err := keys.NewKeyBaseFromDir(cfg.gnoHome)
if err != nil {
return err
}
if cfg.keyName != "" {
info, err := kb.GetByNameOrAddress(cfg.keyName)
if err != nil {
return err
}
creator = info.GetAddress()
pass, err = io.GetPassword("Enter password.", cfg.insecurePasswordStdin)
if err != nil {
return fmt.Errorf("%w, %w", errInvalidDeployerAddr, err)
return fmt.Errorf("cannot read password: %w", err)
}
}

Expand All @@ -96,7 +121,11 @@ func execTxsAddPackages(
if err != nil {
return fmt.Errorf("unable to load txs from directory, %w", err)
}

if creator != defaultCreator {
if err := signTxs(txs, cfg.keyName, genesis.ChainID, kb, pass); err != nil {
return fmt.Errorf("unable to sign txs, %w", err)
}
}
parsedTxs = append(parsedTxs, txs...)
}

Expand All @@ -117,3 +146,24 @@ func execTxsAddPackages(

return nil
}

func signTxs(txs []gnoland.TxWithMetadata, keyName string, chainID string, kb keys.Keybase, pass string) error {
for index, tx := range txs {
signBytes, err := tx.Tx.GetSignBytes(chainID, 0, 0)
if err != nil {
return fmt.Errorf("unable to load txs from directory, %w", err)
}
signature, publicKey, err := kb.Sign(keyName, pass, signBytes)
txs[index].Tx.Signatures = []std.Signature{
{
PubKey: publicKey,
Signature: signature,
},
}
if err != nil {
return fmt.Errorf("unable sign tx %w", err)
}
}

return nil
}
175 changes: 156 additions & 19 deletions contribs/gnogenesis/internal/txs/txs_add_packages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"

"github.com/gnolang/contribs/gnogenesis/internal/common"
"github.com/gnolang/gno/gno.land/pkg/gnoland"
vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/gno/tm2/pkg/bft/types"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
"github.com/gnolang/gno/tm2/pkg/crypto/keys/client"
"github.com/gnolang/gno/tm2/pkg/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -60,8 +63,10 @@ func TestGenesis_Txs_Add_Packages(t *testing.T) {
assert.ErrorContains(t, cmdErr, errInvalidPackageDir.Error())
})

t.Run("invalid deployer address", func(t *testing.T) {
t.Run("nonExistentKeyName", func(t *testing.T) {
t.Parallel()
keybaseDir := t.TempDir()
keyname := "beep-boop"

tempGenesis, cleanup := testutils.NewTestFile(t)
t.Cleanup(cleanup)
Expand All @@ -77,49 +82,159 @@ func TestGenesis_Txs_Add_Packages(t *testing.T) {
"--genesis-path",
tempGenesis.Name(),
t.TempDir(), // package dir
"--deployer-address",
"beep-boop", // invalid address
"--key-name",
keyname, // non-existent key name
"--gno-home",
keybaseDir, // temporaryDir for keybase
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
assert.ErrorIs(t, cmdErr, errInvalidDeployerAddr)
fmt.Println(cmdErr.Error())
assert.ErrorContains(t, cmdErr, "Key "+keyname+" not found")
})

t.Run("valid package", func(t *testing.T) {
t.Run("existentKeyBadPassword", func(t *testing.T) {
t.Parallel()

tempGenesis, cleanup := testutils.NewTestFile(t)
t.Cleanup(cleanup)

genesis := common.GetDefaultGenesis()
require.NoError(t, genesis.SaveAs(tempGenesis.Name()))

// Prepare the package
var (
packagePath = "gno.land/p/demo/cuttlas"
dir = t.TempDir()
keybaseDir = t.TempDir()
keyname = "beep-boop"
password = "somepass"
)
createValidFile(t, dir, packagePath)
// Create key
kb, err := keys.NewKeyBaseFromDir(keybaseDir)
require.NoError(t, err)
mnemonic, err := client.GenerateMnemonic(256)
require.NoError(t, err)
_, err = kb.CreateAccount(keyname, mnemonic, "", password+"wrong", 0, 0)
require.NoError(t, err)

createFile := func(path, data string) {
file, err := os.Create(path)
require.NoError(t, err)
io := commands.NewTestIO()
io.SetIn(
strings.NewReader(
fmt.Sprintf(
"%s\n",
password,
),
),
)

_, err = file.WriteString(data)
require.NoError(t, err)
// Create the command
cmd := NewTxsCmd(io)
args := []string{
"add",
"packages",
"--genesis-path",
tempGenesis.Name(),
"--key-name",
keyname, // non-existent key name
"--gno-home",
keybaseDir, // temporaryDir for keybase
"--insecure-password-stdin",
dir,
}

// Create the gno.mod file
createFile(
filepath.Join(dir, "gno.mod"),
fmt.Sprintf("module %s\n", packagePath),
// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
fmt.Println(cmdErr.Error())
assert.ErrorContains(t, cmdErr, "unable to sign txs, unable sign tx invalid account password")
})

t.Run("existentKeyOKPassword", func(t *testing.T) {
t.Parallel()

tempGenesis, cleanup := testutils.NewTestFile(t)
t.Cleanup(cleanup)

genesis := common.GetDefaultGenesis()
require.NoError(t, genesis.SaveAs(tempGenesis.Name()))
// Prepare the package
var (
packagePath = "gno.land/p/demo/cuttlas"
dir = t.TempDir()
keybaseDir = t.TempDir()
keyname = "beep-boop"
password = "somepass"
)
createValidFile(t, dir, packagePath)
// Create key
kb, err := keys.NewKeyBaseFromDir(keybaseDir)
require.NoError(t, err)
mnemonic, err := client.GenerateMnemonic(256)
require.NoError(t, err)
_, err = kb.CreateAccount(keyname, mnemonic, "", password, 0, 0)
require.NoError(t, err)

io := commands.NewTestIO()
io.SetIn(
strings.NewReader(
fmt.Sprintf(
"%s\n",
password,
),
),
)

// Create a simple main.gno
createFile(
filepath.Join(dir, "main.gno"),
"package cuttlas\n\nfunc Example() string {\nreturn \"Manos arriba!\"\n}",
// Create the command
cmd := NewTxsCmd(io)
args := []string{
"add",
"packages",
"--genesis-path",
tempGenesis.Name(),
"--key-name",
keyname, // non-existent key name
"--gno-home",
keybaseDir, // temporaryDir for keybase
"--insecure-password-stdin",
dir,
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
require.NoError(t, cmdErr)

// Validate the transactions were written down
updatedGenesis, err := types.GenesisDocFromFile(tempGenesis.Name())
require.NoError(t, err)
require.NotNil(t, updatedGenesis.AppState)

// Fetch the state
state := updatedGenesis.AppState.(gnoland.GnoGenesisState)

require.Equal(t, 1, len(state.Txs))
require.Equal(t, 1, len(state.Txs[0].Tx.Msgs))

msgAddPkg, ok := state.Txs[0].Tx.Msgs[0].(vmm.MsgAddPackage)
require.True(t, ok)

assert.Equal(t, packagePath, msgAddPkg.Package.Path)
})

t.Run("valid package", func(t *testing.T) {
t.Parallel()

tempGenesis, cleanup := testutils.NewTestFile(t)
t.Cleanup(cleanup)

genesis := common.GetDefaultGenesis()
require.NoError(t, genesis.SaveAs(tempGenesis.Name()))
// Prepare the package
var (
packagePath = "gno.land/p/demo/cuttlas"
dir = t.TempDir()
)
createValidFile(t, dir, packagePath)

// Create the command
cmd := NewTxsCmd(commands.NewTestIO())
Expand Down Expand Up @@ -152,3 +267,25 @@ func TestGenesis_Txs_Add_Packages(t *testing.T) {
assert.Equal(t, packagePath, msgAddPkg.Package.Path)
})
}

func createValidFile(t *testing.T, dir string, packagePath string) {

Check failure on line 271 in contribs/gnogenesis/internal/txs/txs_add_packages_test.go

View workflow job for this annotation

GitHub Actions / Run Main (gnogenesis) / Go Linter / lint

test helper function should start from t.Helper() (thelper)
createFile := func(path, data string) {
file, err := os.Create(path)
require.NoError(t, err)

_, err = file.WriteString(data)
require.NoError(t, err)
}

// Create the gno.mod file
createFile(
filepath.Join(dir, "gno.mod"),
fmt.Sprintf("module %s\n", packagePath),
)

// Create a simple main.gno
createFile(
filepath.Join(dir, "main.gno"),
"package cuttlas\n\nfunc Example() string {\nreturn \"Manos arriba!\"\n}",
)
}
2 changes: 1 addition & 1 deletion gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) {

// Set AnteHandler
authOptions := auth.AnteOptions{
VerifyGenesisSignatures: false, // for development
VerifyGenesisSignatures: true,
}
authAnteHandler := auth.NewAnteHandler(
acctKpr, bankKpr, auth.DefaultSigVerificationGasConsumer, authOptions)
Expand Down
4 changes: 3 additions & 1 deletion tm2/pkg/sdk/auth/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,15 +394,17 @@ func SetGasMeter(simulate bool, ctx sdk.Context, gasLimit int64) sdk.Context {
// and an account.
func GetSignBytes(chainID string, tx std.Tx, acc std.Account, genesis bool) ([]byte, error) {
var accNum uint64
var accSequence uint64
if !genesis {
accNum = acc.GetAccountNumber()
accSequence = acc.GetSequence()
}

return std.GetSignaturePayload(
std.SignDoc{
ChainID: chainID,
AccountNumber: accNum,
Sequence: acc.GetSequence(),
Sequence: accSequence,
Fee: tx.Fee,
Msgs: tx.Msgs,
Memo: tx.Memo,
Expand Down
6 changes: 3 additions & 3 deletions tm2/pkg/sdk/auth/ante_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) {
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, []uint64{1}, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, std.UnauthorizedError{})

// from correct account number
seqs = []uint64{1}
// At genesis account number is zero
seqs = []uint64{0}
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, []uint64{0}, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false)

Expand All @@ -222,7 +222,7 @@ func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) {
checkInvalidTx(t, anteHandler, ctx, tx, false, std.UnauthorizedError{})

// correct account numbers
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 0}, []uint64{2, 0}
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 0}, []uint64{0, 0}
tx = tu.NewTestTx(t, ctx.ChainID(), msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false)
}
Expand Down

0 comments on commit aa15626

Please sign in to comment.