-
Notifications
You must be signed in to change notification settings - Fork 391
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(gnoland): Define NoopMsg to facilitate the creation of sponsor transaction (gasless transaction) #2209
base: master
Are you sure you want to change the base?
Changes from all commits
d76c863
d8c9b12
5a18b42
231ddf5
6310a42
5313061
7cc2d1c
7e970d4
531cd9a
6b86263
71d4dc0
1850d7a
5bdfac7
859f315
c4e3262
378adae
e3f7104
002c3db
45a40ab
58d6e8c
c24dc09
85f2cf6
1bf3ebb
b4f61b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package vm | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/gnolang/gno/tm2/pkg/crypto" | ||
"github.com/gnolang/gno/tm2/pkg/std" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestMsgAddPackage(t *testing.T) { | ||
creator := crypto.AddressFromPreimage([]byte("addr1")) | ||
pkgPath := "gno.land/r/namespace/test" | ||
files := []*std.MemFile{ | ||
{ | ||
Name: "test.gno", | ||
Body: `package test | ||
func Echo() string {return "hello world"}`, | ||
}, | ||
} | ||
|
||
msg := NewMsgAddPackage(creator, pkgPath, files) | ||
|
||
// Validate Basic | ||
err := msg.ValidateBasic() | ||
require.NoError(t, err) | ||
|
||
// Check if package name is set correctly | ||
require.Equal(t, msg.Package.Name, "test") | ||
|
||
// Test invalid address | ||
msg.Creator = crypto.Address{} | ||
err = msg.ValidateBasic() | ||
require.Error(t, err) | ||
|
||
// Test invalid package path | ||
msg.Creator = creator | ||
msg.Package.Path = "" | ||
err = msg.ValidateBasic() | ||
require.Error(t, err) | ||
} | ||
|
||
func TestMsgCall(t *testing.T) { | ||
caller := crypto.AddressFromPreimage([]byte("addr1")) | ||
pkgPath := "gno.land/r/namespace/mypkg" | ||
funcName := "MyFunction" | ||
args := []string{"arg1", "arg2"} | ||
|
||
msg := NewMsgCall(caller, std.Coins{}, pkgPath, funcName, args) | ||
|
||
// Validate Basic | ||
err := msg.ValidateBasic() | ||
require.NoError(t, err) | ||
|
||
// Test invalid caller address | ||
msg.Caller = crypto.Address{} | ||
err = msg.ValidateBasic() | ||
require.Error(t, err) | ||
|
||
// Test invalid package path | ||
msg.Caller = caller | ||
msg.PkgPath = "" | ||
err = msg.ValidateBasic() | ||
require.Error(t, err) | ||
|
||
// Test invalid function name | ||
msg.PkgPath = pkgPath | ||
msg.Func = "" | ||
err = msg.ValidateBasic() | ||
require.Error(t, err) | ||
} | ||
|
||
func TestMsgRun(t *testing.T) { | ||
caller := crypto.AddressFromPreimage([]byte("addr1")) | ||
files := []*std.MemFile{ | ||
{ | ||
Name: "test.gno", | ||
Body: `package main | ||
func Echo() string {return "hello world"}`, | ||
}, | ||
} | ||
|
||
msg := NewMsgRun(caller, std.Coins{}, files) | ||
|
||
// Validate Basic | ||
err := msg.ValidateBasic() | ||
require.NoError(t, err) | ||
|
||
// Test invalid caller address | ||
msg.Caller = crypto.Address{} | ||
err = msg.ValidateBasic() | ||
require.Error(t, err) | ||
|
||
// Test invalid package name | ||
files = []*std.MemFile{ | ||
{ | ||
Name: "test.gno", | ||
Body: `package test | ||
func Echo() string {return "hello world"}`, | ||
}, | ||
} | ||
require.Panics(t, func() { | ||
NewMsgRun(caller, std.Coins{}, files) | ||
}) | ||
} | ||
|
||
func TestMsgNoop(t *testing.T) { | ||
caller := crypto.AddressFromPreimage([]byte("addr1")) | ||
|
||
msg := NewMsgNoop(caller) | ||
|
||
// Validate Basic | ||
err := msg.ValidateBasic() | ||
require.NoError(t, err) | ||
|
||
// Test invalid caller address | ||
msg.Caller = crypto.Address{} | ||
err = msg.ValidateBasic() | ||
require.Error(t, err) | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -139,11 +139,19 @@ func NewAnteHandler(ak AccountKeeper, bank BankKeeperI, sigGasConsumer Signature | |||||||||||||
stdSigs := tx.GetSignatures() | ||||||||||||||
|
||||||||||||||
for i := 0; i < len(stdSigs); i++ { | ||||||||||||||
var isNewAccount bool | ||||||||||||||
// skip the fee payer, account is cached and fees were deducted already | ||||||||||||||
if i != 0 { | ||||||||||||||
signerAccs[i], res = GetSignerAcc(newCtx, ak, signerAddrs[i]) | ||||||||||||||
// only create new account when tx is a sponsor transaction | ||||||||||||||
if !res.IsOK() { | ||||||||||||||
return newCtx, res, true | ||||||||||||||
if tx.IsSponsorTx() { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think it should be a combination of:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. great additions There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, this check Lines 145 to 150 in 5a45e9e
|
||||||||||||||
isNewAccount = true | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sponsored tx is not only for new accounts There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean this condition should be wrapped into a more general function for cases where a new account needs to be created, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @moul Actually, the logic here is: when the signer account is not found in the storage (the account receiving the sponsor has not existed on-chain before), we check if the transaction being processed is a sponsor transaction. Only then do we allow the creation of this new account on-chain instead of returning an error. This way, we can handle cases where the account does not exist but can still perform onchain actions through a sponsor transaction |
||||||||||||||
signerAccs[i] = ak.NewAccountWithAddress(newCtx, signerAddrs[i]) | ||||||||||||||
signerAccs[i].SetPubKey(stdSigs[i].PubKey) | ||||||||||||||
} else { | ||||||||||||||
return newCtx, res, true | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
|
@@ -153,7 +161,7 @@ func NewAnteHandler(ak AccountKeeper, bank BankKeeperI, sigGasConsumer Signature | |||||||||||||
// No signatures are needed for genesis. | ||||||||||||||
} else { | ||||||||||||||
// Check signature | ||||||||||||||
signBytes, err := GetSignBytes(newCtx.ChainID(), tx, sacc, isGenesis) | ||||||||||||||
signBytes, err := GetSignBytes(newCtx.ChainID(), tx, sacc, isGenesis || isNewAccount) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need to check the signatures; maybe with a different algorithm, but we cannot completely skip signature verification There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, the logic here is able to verify all signatures within the transaction regardless of whether the account existed on-chain before or has just been created above |
||||||||||||||
if err != nil { | ||||||||||||||
return newCtx, res, true | ||||||||||||||
} | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,6 +60,34 @@ | |
return nil | ||
} | ||
|
||
// IsSponsorTx checks if the transaction is a valid sponsor transaction | ||
func (tx Tx) IsSponsorTx() bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO, sponsortx is when:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
// At least two message in the transaction | ||
if len(tx.Msgs) <= 1 { | ||
return false | ||
} | ||
|
||
// The first message type is "no_op" | ||
if tx.Msgs[0].Type() != "no_op" { | ||
return false | ||
} | ||
|
||
// More than one signer | ||
signers := tx.GetSigners() | ||
if len(signers) <= 1 { | ||
return false | ||
} | ||
|
||
// The first signer is different from all other signers | ||
for i := 1; i < len(signers); i++ { | ||
if signers[0] == signers[i] { | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} | ||
|
||
// CountSubKeys counts the total number of keys for a multi-sig public key. | ||
func CountSubKeys(pub crypto.PubKey) int { | ||
v, ok := pub.(multisig.PubKeyMultisigThreshold) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried and rolled back because the CI lint does not allow this type of declaration