From a03eeb3ea27732c437f2d1e3eb1155b8e5dabe8a Mon Sep 17 00:00:00 2001
From: sunspirit99 <167175638+linhpn99@users.noreply.github.com>
Date: Fri, 10 May 2024 21:51:36 +0700
Subject: [PATCH 01/83] feat(examples): define metadata & royalty info for
GRC721 realm (#1962)
Contributors' checklist...
- [x] Added new tests, or not needed, or not feasible
- [ ] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [ ] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [ ] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
---
.../gno.land/p/demo/grc/grc721/errors.gno | 5 +
.../p/demo/grc/grc721/grc721_metadata.gno | 95 +++++++++++++
.../demo/grc/grc721/grc721_metadata_test.gno | 133 ++++++++++++++++++
.../p/demo/grc/grc721/grc721_royalty.gno | 78 ++++++++++
.../p/demo/grc/grc721/grc721_royalty_test.gno | 93 ++++++++++++
.../p/demo/grc/grc721/igrc721_metadata.gno | 38 +++++
.../p/demo/grc/grc721/igrc721_royalty.gno | 18 +++
7 files changed, 460 insertions(+)
create mode 100644 examples/gno.land/p/demo/grc/grc721/grc721_metadata.gno
create mode 100644 examples/gno.land/p/demo/grc/grc721/grc721_metadata_test.gno
create mode 100644 examples/gno.land/p/demo/grc/grc721/grc721_royalty.gno
create mode 100644 examples/gno.land/p/demo/grc/grc721/grc721_royalty_test.gno
create mode 100644 examples/gno.land/p/demo/grc/grc721/igrc721_metadata.gno
create mode 100644 examples/gno.land/p/demo/grc/grc721/igrc721_royalty.gno
diff --git a/examples/gno.land/p/demo/grc/grc721/errors.gno b/examples/gno.land/p/demo/grc/grc721/errors.gno
index 08fb26b0cb5..2d512db350d 100644
--- a/examples/gno.land/p/demo/grc/grc721/errors.gno
+++ b/examples/gno.land/p/demo/grc/grc721/errors.gno
@@ -14,4 +14,9 @@ var (
ErrTransferToNonGRC721Receiver = errors.New("transfer to non GRC721Receiver implementer")
ErrCallerIsNotOwnerOrApproved = errors.New("caller is not token owner or approved")
ErrTokenIdAlreadyExists = errors.New("token id already exists")
+
+ // ERC721Royalty
+ ErrInvalidRoyaltyPercentage = errors.New("invalid royalty percentage")
+ ErrInvalidRoyaltyPaymentAddress = errors.New("invalid royalty paymentAddress")
+ ErrCannotCalculateRoyaltyAmount = errors.New("cannot calculate royalty amount")
)
diff --git a/examples/gno.land/p/demo/grc/grc721/grc721_metadata.gno b/examples/gno.land/p/demo/grc/grc721/grc721_metadata.gno
new file mode 100644
index 00000000000..360f73ed106
--- /dev/null
+++ b/examples/gno.land/p/demo/grc/grc721/grc721_metadata.gno
@@ -0,0 +1,95 @@
+package grc721
+
+import (
+ "std"
+
+ "gno.land/p/demo/avl"
+)
+
+// metadataNFT represents an NFT with metadata extensions.
+type metadataNFT struct {
+ *basicNFT // Embedded basicNFT struct for basic NFT functionality
+ extensions *avl.Tree // AVL tree for storing metadata extensions
+}
+
+// Ensure that metadataNFT implements the IGRC721MetadataOnchain interface.
+var _ IGRC721MetadataOnchain = (*metadataNFT)(nil)
+
+// NewNFTWithMetadata creates a new basic NFT with metadata extensions.
+func NewNFTWithMetadata(name string, symbol string) *metadataNFT {
+ // Create a new basic NFT
+ nft := NewBasicNFT(name, symbol)
+
+ // Return a metadataNFT with basicNFT embedded and an empty AVL tree for extensions
+ return &metadataNFT{
+ basicNFT: nft,
+ extensions: avl.NewTree(),
+ }
+}
+
+// SetTokenMetadata sets metadata for a given token ID.
+func (s *metadataNFT) SetTokenMetadata(tid TokenID, metadata Metadata) error {
+ // Check if the caller is the owner of the token
+ owner, err := s.basicNFT.OwnerOf(tid)
+ if err != nil {
+ return err
+ }
+ caller := std.PrevRealm().Addr()
+ if caller != owner {
+ return ErrCallerIsNotOwner
+ }
+
+ // Set the metadata for the token ID in the extensions AVL tree
+ s.extensions.Set(string(tid), metadata)
+ return nil
+}
+
+// TokenMetadata retrieves metadata for a given token ID.
+func (s *metadataNFT) TokenMetadata(tid TokenID) (Metadata, error) {
+ // Retrieve metadata from the extensions AVL tree
+ metadata, found := s.extensions.Get(string(tid))
+ if !found {
+ return Metadata{}, ErrInvalidTokenId
+ }
+
+ return metadata.(Metadata), nil
+}
+
+// mint mints a new token and assigns it to the specified address.
+func (s *metadataNFT) mint(to std.Address, tid TokenID) error {
+ // Check if the address is valid
+ if err := isValidAddress(to); err != nil {
+ return err
+ }
+
+ // Check if the token ID already exists
+ if s.basicNFT.exists(tid) {
+ return ErrTokenIdAlreadyExists
+ }
+
+ s.basicNFT.beforeTokenTransfer(zeroAddress, to, tid, 1)
+
+ // Check if the token ID was minted by beforeTokenTransfer
+ if s.basicNFT.exists(tid) {
+ return ErrTokenIdAlreadyExists
+ }
+
+ // Increment balance of the recipient address
+ toBalance, err := s.basicNFT.BalanceOf(to)
+ if err != nil {
+ return err
+ }
+ toBalance += 1
+ s.basicNFT.balances.Set(to.String(), toBalance)
+
+ // Set owner of the token ID to the recipient address
+ s.basicNFT.owners.Set(string(tid), to)
+
+ // Emit transfer event
+ event := TransferEvent{zeroAddress, to, tid}
+ emit(&event)
+
+ s.basicNFT.afterTokenTransfer(zeroAddress, to, tid, 1)
+
+ return nil
+}
diff --git a/examples/gno.land/p/demo/grc/grc721/grc721_metadata_test.gno b/examples/gno.land/p/demo/grc/grc721/grc721_metadata_test.gno
new file mode 100644
index 00000000000..b7ca6932fe1
--- /dev/null
+++ b/examples/gno.land/p/demo/grc/grc721/grc721_metadata_test.gno
@@ -0,0 +1,133 @@
+package grc721
+
+import (
+ "std"
+ "testing"
+
+ "gno.land/p/demo/testutils"
+ "gno.land/p/demo/users"
+)
+
+func TestSetMetadata(t *testing.T) {
+ // Create a new dummy NFT with metadata
+ dummy := NewNFTWithMetadata(dummyNFTName, dummyNFTSymbol)
+ if dummy == nil {
+ t.Errorf("should not be nil")
+ }
+
+ // Define addresses for testing purposes
+ addr1 := testutils.TestAddress("alice")
+ addr2 := testutils.TestAddress("bob")
+
+ // Define metadata attributes
+ name := "test"
+ description := "test"
+ image := "test"
+ imageData := "test"
+ externalURL := "test"
+ attributes := []Trait{}
+ backgroundColor := "test"
+ animationURL := "test"
+ youtubeURL := "test"
+
+ // Set the original caller to addr1
+ std.TestSetOrigCaller(addr1) // addr1
+
+ // Mint a new token for addr1
+ dummy.mint(addr1, TokenID("1"))
+
+ // Set metadata for token 1
+ derr := dummy.SetTokenMetadata(TokenID("1"), Metadata{
+ Name: name,
+ Description: description,
+ Image: image,
+ ImageData: imageData,
+ ExternalURL: externalURL,
+ Attributes: attributes,
+ BackgroundColor: backgroundColor,
+ AnimationURL: animationURL,
+ YoutubeURL: youtubeURL,
+ })
+
+ // Check if there was an error setting metadata
+ if derr != nil {
+ t.Errorf("Should not result in error : %s", derr.Error())
+ }
+
+ // Test case: Invalid token ID
+ err := dummy.SetTokenMetadata(TokenID("3"), Metadata{
+ Name: name,
+ Description: description,
+ Image: image,
+ ImageData: imageData,
+ ExternalURL: externalURL,
+ Attributes: attributes,
+ BackgroundColor: backgroundColor,
+ AnimationURL: animationURL,
+ YoutubeURL: youtubeURL,
+ })
+
+ // Check if the error returned matches the expected error
+ if err != ErrInvalidTokenId {
+ t.Errorf("Expected error %s, got %s", ErrInvalidTokenId, err)
+ }
+
+ // Set the original caller to addr2
+ std.TestSetOrigCaller(addr2) // addr2
+
+ // Try to set metadata for token 1 from addr2 (should fail)
+ cerr := dummy.SetTokenMetadata(TokenID("1"), Metadata{
+ Name: name,
+ Description: description,
+ Image: image,
+ ImageData: imageData,
+ ExternalURL: externalURL,
+ Attributes: attributes,
+ BackgroundColor: backgroundColor,
+ AnimationURL: animationURL,
+ YoutubeURL: youtubeURL,
+ })
+
+ // Check if the error returned matches the expected error
+ if cerr != ErrCallerIsNotOwner {
+ t.Errorf("Expected error %s, got %s", ErrCallerIsNotOwner, cerr)
+ }
+
+ // Set the original caller back to addr1
+ std.TestSetOrigCaller(addr1) // addr1
+
+ // Retrieve metadata for token 1
+ dummyMetadata, err := dummy.TokenMetadata(TokenID("1"))
+ if err != nil {
+ t.Errorf("Metadata error: %s", err.Error())
+ }
+
+ // Check if metadata attributes match expected values
+ if dummyMetadata.Image != image {
+ t.Errorf("Expected Metadata's image %s, got %s", image, dummyMetadata.Image)
+ }
+ if dummyMetadata.ImageData != imageData {
+ t.Errorf("Expected Metadata's imageData %s, got %s", imageData, dummyMetadata.ImageData)
+ }
+ if dummyMetadata.ExternalURL != externalURL {
+ t.Errorf("Expected Metadata's externalURL %s, got %s", externalURL, dummyMetadata.ExternalURL)
+ }
+ if dummyMetadata.Description != description {
+ t.Errorf("Expected Metadata's description %s, got %s", description, dummyMetadata.Description)
+ }
+ if dummyMetadata.Name != name {
+ t.Errorf("Expected Metadata's name %s, got %s", name, dummyMetadata.Name)
+ }
+ if len(dummyMetadata.Attributes) != len(attributes) {
+ t.Errorf("Expected %d Metadata's attributes, got %d", len(attributes), len(dummyMetadata.Attributes))
+ }
+ if dummyMetadata.BackgroundColor != backgroundColor {
+ t.Errorf("Expected Metadata's backgroundColor %s, got %s", backgroundColor, dummyMetadata.BackgroundColor)
+ }
+ if dummyMetadata.AnimationURL != animationURL {
+ t.Errorf("Expected Metadata's animationURL %s, got %s", animationURL, dummyMetadata.AnimationURL)
+ }
+ if dummyMetadata.YoutubeURL != youtubeURL {
+ t.Errorf("Expected Metadata's youtubeURL %s, got %s", youtubeURL, dummyMetadata.YoutubeURL)
+ }
+}
diff --git a/examples/gno.land/p/demo/grc/grc721/grc721_royalty.gno b/examples/gno.land/p/demo/grc/grc721/grc721_royalty.gno
new file mode 100644
index 00000000000..9831c709121
--- /dev/null
+++ b/examples/gno.land/p/demo/grc/grc721/grc721_royalty.gno
@@ -0,0 +1,78 @@
+package grc721
+
+import (
+ "std"
+
+ "gno.land/p/demo/avl"
+)
+
+// royaltyNFT represents a non-fungible token (NFT) with royalty functionality.
+type royaltyNFT struct {
+ *metadataNFT // Embedding metadataNFT for NFT functionality
+ tokenRoyaltyInfo *avl.Tree // AVL tree to store royalty information for each token
+ maxRoyaltyPercentage uint64 // maxRoyaltyPercentage represents the maximum royalty percentage that can be charged every sale
+}
+
+// Ensure that royaltyNFT implements the IGRC2981 interface.
+var _ IGRC2981 = (*royaltyNFT)(nil)
+
+// NewNFTWithRoyalty creates a new royalty NFT with the specified name, symbol, and royalty calculator.
+func NewNFTWithRoyalty(name string, symbol string) *royaltyNFT {
+ // Create a new NFT with metadata
+ nft := NewNFTWithMetadata(name, symbol)
+
+ return &royaltyNFT{
+ metadataNFT: nft,
+ tokenRoyaltyInfo: avl.NewTree(),
+ maxRoyaltyPercentage: 100,
+ }
+}
+
+// SetTokenRoyalty sets the royalty information for a specific token ID.
+func (r *royaltyNFT) SetTokenRoyalty(tid TokenID, royaltyInfo RoyaltyInfo) error {
+ // Validate the payment address
+ if err := isValidAddress(royaltyInfo.PaymentAddress); err != nil {
+ return ErrInvalidRoyaltyPaymentAddress
+ }
+
+ // Check if royalty percentage exceeds maxRoyaltyPercentage
+ if royaltyInfo.Percentage > r.maxRoyaltyPercentage {
+ return ErrInvalidRoyaltyPercentage
+ }
+
+ // Check if the caller is the owner of the token
+ owner, err := r.metadataNFT.OwnerOf(tid)
+ if err != nil {
+ return err
+ }
+ caller := std.PrevRealm().Addr()
+ if caller != owner {
+ return ErrCallerIsNotOwner
+ }
+
+ // Set royalty information for the token
+ r.tokenRoyaltyInfo.Set(string(tid), royaltyInfo)
+
+ return nil
+}
+
+// RoyaltyInfo returns the royalty information for the given token ID and sale price.
+func (r *royaltyNFT) RoyaltyInfo(tid TokenID, salePrice uint64) (std.Address, uint64, error) {
+ // Retrieve royalty information for the token
+ val, found := r.tokenRoyaltyInfo.Get(string(tid))
+ if !found {
+ return "", 0, ErrInvalidTokenId
+ }
+
+ royaltyInfo := val.(RoyaltyInfo)
+
+ // Calculate royalty amount
+ royaltyAmount, _ := r.calculateRoyaltyAmount(salePrice, royaltyInfo.Percentage)
+
+ return royaltyInfo.PaymentAddress, royaltyAmount, nil
+}
+
+func (r *royaltyNFT) calculateRoyaltyAmount(salePrice, percentage uint64) (uint64, error) {
+ royaltyAmount := (salePrice * percentage) / 100
+ return royaltyAmount, nil
+}
diff --git a/examples/gno.land/p/demo/grc/grc721/grc721_royalty_test.gno b/examples/gno.land/p/demo/grc/grc721/grc721_royalty_test.gno
new file mode 100644
index 00000000000..8c7bb33caa5
--- /dev/null
+++ b/examples/gno.land/p/demo/grc/grc721/grc721_royalty_test.gno
@@ -0,0 +1,93 @@
+package grc721
+
+import (
+ "std"
+ "testing"
+
+ "gno.land/p/demo/testutils"
+ "gno.land/p/demo/ufmt"
+ "gno.land/p/demo/users"
+)
+
+func TestSetTokenRoyalty(t *testing.T) {
+ dummy := NewNFTWithRoyalty(dummyNFTName, dummyNFTSymbol)
+ if dummy == nil {
+ t.Errorf("should not be nil")
+ }
+
+ addr1 := testutils.TestAddress("alice")
+ addr2 := testutils.TestAddress("bob")
+
+ paymentAddress := testutils.TestAddress("john")
+ percentage := uint64(10) // 10%
+
+ salePrice := uint64(1000)
+ expectRoyaltyAmount := uint64(100)
+
+ std.TestSetOrigCaller(addr1) // addr1
+
+ dummy.mint(addr1, TokenID("1"))
+
+ derr := dummy.SetTokenRoyalty(TokenID("1"), RoyaltyInfo{
+ PaymentAddress: paymentAddress,
+ Percentage: percentage,
+ })
+
+ if derr != nil {
+ t.Errorf("Should not result in error : %s", derr.Error())
+ }
+
+ // Test case: Invalid token ID
+ err := dummy.SetTokenRoyalty(TokenID("3"), RoyaltyInfo{
+ PaymentAddress: paymentAddress,
+ Percentage: percentage,
+ })
+ if err != ErrInvalidTokenId {
+ t.Errorf("Expected error %s, got %s", ErrInvalidTokenId, err)
+ }
+
+ std.TestSetOrigCaller(addr2) // addr2
+
+ cerr := dummy.SetTokenRoyalty(TokenID("1"), RoyaltyInfo{
+ PaymentAddress: paymentAddress,
+ Percentage: percentage,
+ })
+ if cerr != ErrCallerIsNotOwner {
+ t.Errorf("Expected error %s, got %s", ErrCallerIsNotOwner, cerr)
+ }
+
+ // Test case: Invalid payment address
+ aerr := dummy.SetTokenRoyalty(TokenID("4"), RoyaltyInfo{
+ PaymentAddress: std.Address("###"), // invalid address
+ Percentage: percentage,
+ })
+ if aerr != ErrInvalidRoyaltyPaymentAddress {
+ t.Errorf("Expected error %s, got %s", ErrInvalidRoyaltyPaymentAddress, aerr)
+ }
+
+ // Test case: Invalid percentage
+ perr := dummy.SetTokenRoyalty(TokenID("5"), RoyaltyInfo{
+ PaymentAddress: paymentAddress,
+ Percentage: uint64(200), // over maxRoyaltyPercentage
+ })
+
+ if perr != ErrInvalidRoyaltyPercentage {
+ t.Errorf("Expected error %s, got %s", ErrInvalidRoyaltyPercentage, perr)
+ }
+
+ // Test case: Retrieving Royalty Info
+ std.TestSetOrigCaller(addr1) // addr1
+
+ dummyPaymentAddress, dummyRoyaltyAmount, rerr := dummy.RoyaltyInfo(TokenID("1"), salePrice)
+ if rerr != nil {
+ t.Errorf("RoyaltyInfo error: %s", rerr.Error())
+ }
+
+ if dummyPaymentAddress != paymentAddress {
+ t.Errorf("Expected RoyaltyPaymentAddress %s, got %s", paymentAddress, dummyPaymentAddress)
+ }
+
+ if dummyRoyaltyAmount != expectRoyaltyAmount {
+ t.Errorf("Expected RoyaltyAmount %d, got %d", expectRoyaltyAmount, dummyRoyaltyAmount)
+ }
+}
diff --git a/examples/gno.land/p/demo/grc/grc721/igrc721_metadata.gno b/examples/gno.land/p/demo/grc/grc721/igrc721_metadata.gno
new file mode 100644
index 00000000000..8a2be1ff75d
--- /dev/null
+++ b/examples/gno.land/p/demo/grc/grc721/igrc721_metadata.gno
@@ -0,0 +1,38 @@
+package grc721
+
+// IGRC721CollectionMetadata describes basic information about an NFT collection.
+type IGRC721CollectionMetadata interface {
+ Name() string // Name returns the name of the collection.
+ Symbol() string // Symbol returns the symbol of the collection.
+}
+
+// IGRC721Metadata follows the Ethereum standard
+type IGRC721Metadata interface {
+ IGRC721CollectionMetadata
+ TokenURI(tid TokenID) (string, error) // TokenURI returns the URI of a specific token.
+}
+
+// IGRC721Metadata follows the OpenSea metadata standard
+type IGRC721MetadataOnchain interface {
+ IGRC721CollectionMetadata
+ TokenMetadata(tid TokenID) (Metadata, error)
+}
+
+type Trait struct {
+ DisplayType string
+ TraitType string
+ Value string
+}
+
+// see: https://docs.opensea.io/docs/metadata-standards
+type Metadata struct {
+ Image string // URL to the image of the item. Can be any type of image (including SVGs, which will be cached into PNGs by OpenSea), IPFS or Arweave URLs or paths. We recommend using a minimum 3000 x 3000 image.
+ ImageData string // Raw SVG image data, if you want to generate images on the fly (not recommended). Only use this if you're not including the image parameter.
+ ExternalURL string // URL that will appear below the asset's image on OpenSea and will allow users to leave OpenSea and view the item on your site.
+ Description string // Human-readable description of the item. Markdown is supported.
+ Name string // Name of the item.
+ Attributes []Trait // Attributes for the item, which will show up on the OpenSea page for the item.
+ BackgroundColor string // Background color of the item on OpenSea. Must be a six-character hexadecimal without a pre-pended #
+ AnimationURL string // URL to a multimedia attachment for the item. Supported file extensions: GLTF, GLB, WEBM, MP4, M4V, OGV, OGG, MP3, WAV, OGA, HTML (for rich experiences and interactive NFTs using JavaScript canvas, WebGL, etc.). Scripts and relative paths within the HTML page are now supported. Access to browser extensions is not supported.
+ YoutubeURL string // URL to a YouTube video (only used if animation_url is not provided).
+}
diff --git a/examples/gno.land/p/demo/grc/grc721/igrc721_royalty.gno b/examples/gno.land/p/demo/grc/grc721/igrc721_royalty.gno
new file mode 100644
index 00000000000..a8a74ea15cc
--- /dev/null
+++ b/examples/gno.land/p/demo/grc/grc721/igrc721_royalty.gno
@@ -0,0 +1,18 @@
+package grc721
+
+import (
+ "std"
+)
+
+// IGRC2981 follows the Ethereum standard
+type IGRC2981 interface {
+ // RoyaltyInfo retrieves royalty information for a tokenID and salePrice.
+ // It returns the payment address, royalty amount, and an error if any.
+ RoyaltyInfo(tokenID TokenID, salePrice uint64) (std.Address, uint64, error)
+}
+
+// RoyaltyInfo represents royalty information for a token.
+type RoyaltyInfo struct {
+ PaymentAddress std.Address // PaymentAddress is the address where royalty payment should be sent.
+ Percentage uint64 // Percentage is the royalty percentage. It indicates the percentage of royalty to be paid for each sale. For example : Percentage = 10 => 10%
+}
From dc6eb7d1d7d22304b14d1bdec8e89693e0345f00 Mon Sep 17 00:00:00 2001
From: Morgan
Date: Sun, 12 May 2024 14:57:46 +0200
Subject: [PATCH 02/83] chore: kebab-case to snake_case all source and test
files (#2057)
Contributors' checklist...
- [ ] Added new tests, or not needed, or not feasible
- [ ] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [ ] Updated the official documentation or not needed
- [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [ ] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
---
.../integration_test.gno | 4 +-
.../{upgrade-a => upgrade_a}/v1/v1.gno | 0
.../{upgrade-a => upgrade_a}/v2/v2.gno | 2 +-
.../{upgrade-b => upgrade_b}/v1/v1.gno | 0
.../{upgrade-b => upgrade_b}/v2/v2.gno | 2 +-
.../{upgrade-c => upgrade_c}/root/root.gno | 2 +-
.../{upgrade-c => upgrade_c}/v1/v1.gno | 2 +-
.../{upgrade-c => upgrade_c}/v2/v2.gno | 2 +-
.../{float-arg.txtar => float_arg.txtar} | 0
...ress.txtar => grc20_invalid_address.txtar} | 0
...20-registry.txtar => grc20_registry.txtar} | 0
.../{issue-1167.txtar => issue_1167.txtar} | 0
.../{issue-1588.txtar => issue_1588.txtar} | 0
.../{issue-1786.txtar => issue_1786.txtar} | 0
...chess-97.txtar => issue_gnochess_97.txtar} | 0
.../{map-storage.txtar => map_storage.txtar} | 0
gnovm/cmd/gno/clean_test.go | 4 +-
gnovm/cmd/gno/lint_test.go | 16 ++---
gnovm/cmd/gno/mod_test.go | 72 +++++++++----------
gnovm/cmd/gno/run_test.go | 28 ++++----
.../backup/{issue-558.gno => issue_558.gno} | 0
.../backup/{issue-770.gno => issue_770.gno} | 0
.../backup/{issue-772.gno => issue_772.gno} | 0
.../backup/{issue-775.gno => issue_775.gno} | 0
...-global2.gno => redeclaration_global2.gno} | 3 +-
...-global3.gno => redeclaration_global3.gno} | 2 +-
...-global4.gno => redeclaration_global4.gno} | 2 +-
...-global6.gno => redeclaration_global6.gno} | 2 +-
...-global7.gno => redeclaration_global7.gno} | 2 +-
...elector-scope0.gno => selector_scope0.gno} | 0
gnovm/tests/files/extern/baz-bat/baz-bat.gno | 3 -
gnovm/tests/files/extern/baz_bat/baz_bat.gno | 3 +
gnovm/tests/files/extern/foo-bar/foo-bar.gno | 3 -
gnovm/tests/files/extern/foo_bar/foo_bar.gno | 3 +
.../tree/avl/avltree.gno | 6 +-
.../tree/utils.gno | 4 +-
.../types/map_entry.gno | 0
.../types/string.gno | 0
.../types/types.gno | 2 +-
.../types/util.gno | 0
gnovm/tests/files/import7.gno | 4 +-
gnovm/tests/files/import9.gno | 4 +-
.../files/{issue-1085.gno => issue_1085.gno} | 0
.../files/{issue-1096.gno => issue_1096.gno} | 0
.../files/{issue-558b.gno => issue_558b.gno} | 0
.../files/{issue-735.gno => issue_735.gno} | 0
.../files/{issue-776.gno => issue_776.gno} | 0
.../files/{issue-782.gno => issue_782.gno} | 0
.../files/{issue-784.gno => issue_784.gno} | 0
.../files/{issue-880.gno => issue_880.gno} | 0
gnovm/tests/files/redeclaration-global0.gno | 14 ----
gnovm/tests/files/redeclaration-global1.gno | 14 ----
gnovm/tests/files/redeclaration-global5.gno | 16 -----
gnovm/tests/files/redeclaration_global0.gno | 14 ++++
gnovm/tests/files/redeclaration_global1.gno | 14 ++++
gnovm/tests/files/redeclaration_global5.gno | 16 +++++
gnovm/tests/files/zavltree.gno | 4 +-
gnovm/tests/files/zavltree0.gno | 4 +-
gnovm/tests/files/zavltree1.gno | 4 +-
gnovm/tests/files/zrealm4.gno | 16 ++---
gnovm/tests/files/zrealm5.gno | 26 +++----
gnovm/tests/files/zrealm6.gno | 36 +++++-----
gnovm/tests/files/zrealm7.gno | 46 ++++++------
.../integ/{empty-dir => empty_dir}/.gitkeep | 0
.../{empty-gno1 => empty_gno1}/empty.gno | 0
.../{empty-gno2 => empty_gno2}/empty.gno | 0
.../{empty-gno2 => empty_gno2}/empty_test.gno | 0
.../{empty-gno3 => empty_gno3}/empty.gno | 0
.../empty_filetest.gno | 0
.../{empty-gnomod => empty_gnomod}/gno.mod | 0
.../gno.mod | 0
.../invalid.gno | 0
.../gno.mod | 0
.../gno.mod | 0
.../gno.mod | 0
.../gno.mod | 0
.../gno.mod | 0
.../main.gno | 0
.../gno.mod | 0
.../gno.mod | 0
.../gno.mod | 0
.../gno.mod | 0
.../gno.mod | 0
.../import_avl.gno | 0
.../integ/{run-main => run_main}/main.gno | 0
.../{run-namedpkg => run_namedpkg}/main.gno | 0
.../{run-package => run_package}/package.gno | 0
.../{run-package => run_package}/package2.gno | 0
.../gno.mod | 0
.../undefined_variables_test.gno | 0
90 files changed, 200 insertions(+), 201 deletions(-)
rename examples/gno.land/r/x/manfred_upgrade_patterns/{upgrade-a => upgrade_a}/integration_test.gno (81%)
rename examples/gno.land/r/x/manfred_upgrade_patterns/{upgrade-a => upgrade_a}/v1/v1.gno (100%)
rename examples/gno.land/r/x/manfred_upgrade_patterns/{upgrade-a => upgrade_a}/v2/v2.gno (82%)
rename examples/gno.land/r/x/manfred_upgrade_patterns/{upgrade-b => upgrade_b}/v1/v1.gno (100%)
rename examples/gno.land/r/x/manfred_upgrade_patterns/{upgrade-b => upgrade_b}/v2/v2.gno (91%)
rename examples/gno.land/r/x/manfred_upgrade_patterns/{upgrade-c => upgrade_c}/root/root.gno (96%)
rename examples/gno.land/r/x/manfred_upgrade_patterns/{upgrade-c => upgrade_c}/v1/v1.gno (57%)
rename examples/gno.land/r/x/manfred_upgrade_patterns/{upgrade-c => upgrade_c}/v2/v2.gno (57%)
rename gno.land/cmd/gnoland/testdata/{float-arg.txtar => float_arg.txtar} (100%)
rename gno.land/cmd/gnoland/testdata/{grc20-invalid-address.txtar => grc20_invalid_address.txtar} (100%)
rename gno.land/cmd/gnoland/testdata/{grc20-registry.txtar => grc20_registry.txtar} (100%)
rename gno.land/cmd/gnoland/testdata/{issue-1167.txtar => issue_1167.txtar} (100%)
rename gno.land/cmd/gnoland/testdata/{issue-1588.txtar => issue_1588.txtar} (100%)
rename gno.land/cmd/gnoland/testdata/{issue-1786.txtar => issue_1786.txtar} (100%)
rename gno.land/cmd/gnoland/testdata/{issue-gnochess-97.txtar => issue_gnochess_97.txtar} (100%)
rename gno.land/cmd/gnoland/testdata/{map-storage.txtar => map_storage.txtar} (100%)
rename gnovm/tests/backup/{issue-558.gno => issue_558.gno} (100%)
rename gnovm/tests/backup/{issue-770.gno => issue_770.gno} (100%)
rename gnovm/tests/backup/{issue-772.gno => issue_772.gno} (100%)
rename gnovm/tests/backup/{issue-775.gno => issue_775.gno} (100%)
rename gnovm/tests/backup/{redeclaration-global2.gno => redeclaration_global2.gno} (65%)
rename gnovm/tests/backup/{redeclaration-global3.gno => redeclaration_global3.gno} (64%)
rename gnovm/tests/backup/{redeclaration-global4.gno => redeclaration_global4.gno} (65%)
rename gnovm/tests/backup/{redeclaration-global6.gno => redeclaration_global6.gno} (67%)
rename gnovm/tests/backup/{redeclaration-global7.gno => redeclaration_global7.gno} (80%)
rename gnovm/tests/backup/{selector-scope0.gno => selector_scope0.gno} (100%)
delete mode 100644 gnovm/tests/files/extern/baz-bat/baz-bat.gno
create mode 100644 gnovm/tests/files/extern/baz_bat/baz_bat.gno
delete mode 100644 gnovm/tests/files/extern/foo-bar/foo-bar.gno
create mode 100644 gnovm/tests/files/extern/foo_bar/foo_bar.gno
rename gnovm/tests/files/extern/timtadh/{data-structures => data_structures}/tree/avl/avltree.gno (97%)
rename gnovm/tests/files/extern/timtadh/{data-structures => data_structures}/tree/utils.gno (95%)
rename gnovm/tests/files/extern/timtadh/{data-structures => data_structures}/types/map_entry.gno (100%)
rename gnovm/tests/files/extern/timtadh/{data-structures => data_structures}/types/string.gno (100%)
rename gnovm/tests/files/extern/timtadh/{data-structures => data_structures}/types/types.gno (98%)
rename gnovm/tests/files/extern/timtadh/{data-structures => data_structures}/types/util.gno (100%)
rename gnovm/tests/files/{issue-1085.gno => issue_1085.gno} (100%)
rename gnovm/tests/files/{issue-1096.gno => issue_1096.gno} (100%)
rename gnovm/tests/files/{issue-558b.gno => issue_558b.gno} (100%)
rename gnovm/tests/files/{issue-735.gno => issue_735.gno} (100%)
rename gnovm/tests/files/{issue-776.gno => issue_776.gno} (100%)
rename gnovm/tests/files/{issue-782.gno => issue_782.gno} (100%)
rename gnovm/tests/files/{issue-784.gno => issue_784.gno} (100%)
rename gnovm/tests/files/{issue-880.gno => issue_880.gno} (100%)
delete mode 100644 gnovm/tests/files/redeclaration-global0.gno
delete mode 100644 gnovm/tests/files/redeclaration-global1.gno
delete mode 100644 gnovm/tests/files/redeclaration-global5.gno
create mode 100644 gnovm/tests/files/redeclaration_global0.gno
create mode 100644 gnovm/tests/files/redeclaration_global1.gno
create mode 100644 gnovm/tests/files/redeclaration_global5.gno
rename gnovm/tests/integ/{empty-dir => empty_dir}/.gitkeep (100%)
rename gnovm/tests/integ/{empty-gno1 => empty_gno1}/empty.gno (100%)
rename gnovm/tests/integ/{empty-gno2 => empty_gno2}/empty.gno (100%)
rename gnovm/tests/integ/{empty-gno2 => empty_gno2}/empty_test.gno (100%)
rename gnovm/tests/integ/{empty-gno3 => empty_gno3}/empty.gno (100%)
rename gnovm/tests/integ/{empty-gno3 => empty_gno3}/empty_filetest.gno (100%)
rename gnovm/tests/integ/{empty-gnomod => empty_gnomod}/gno.mod (100%)
rename gnovm/tests/integ/{invalid-gno-file => invalid_gno_file}/gno.mod (100%)
rename gnovm/tests/integ/{invalid-gno-file => invalid_gno_file}/invalid.gno (100%)
rename gnovm/tests/integ/{invalid-module-name => invalid_module_name}/gno.mod (100%)
rename gnovm/tests/integ/{invalid-module-version1 => invalid_module_version1}/gno.mod (100%)
rename gnovm/tests/integ/{invalid-module-version2 => invalid_module_version2}/gno.mod (100%)
rename gnovm/tests/integ/{minimalist-gnomod => minimalist_gnomod}/gno.mod (100%)
rename gnovm/tests/integ/{package-not-declared => package_not_declared}/gno.mod (100%)
rename gnovm/tests/integ/{package-not-declared => package_not_declared}/main.gno (100%)
rename gnovm/tests/integ/{replace-with-dir => replace_with_dir}/gno.mod (100%)
rename gnovm/tests/integ/{replace-with-invalid-module => replace_with_invalid_module}/gno.mod (100%)
rename gnovm/tests/integ/{replace-with-module => replace_with_module}/gno.mod (100%)
rename gnovm/tests/integ/{require-invalid-module => require_invalid_module}/gno.mod (100%)
rename gnovm/tests/integ/{require-remote-module => require_remote_module}/gno.mod (100%)
rename gnovm/tests/integ/{require-remote-module => require_remote_module}/import_avl.gno (100%)
rename gnovm/tests/integ/{run-main => run_main}/main.gno (100%)
rename gnovm/tests/integ/{run-namedpkg => run_namedpkg}/main.gno (100%)
rename gnovm/tests/integ/{run-package => run_package}/package.gno (100%)
rename gnovm/tests/integ/{run-package => run_package}/package2.gno (100%)
rename gnovm/tests/integ/{undefined-variable-test => undefined_variable_test}/gno.mod (100%)
rename gnovm/tests/integ/{undefined-variable-test => undefined_variable_test}/undefined_variables_test.gno (100%)
diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-a/integration_test.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/integration_test.gno
similarity index 81%
rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-a/integration_test.gno
rename to examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/integration_test.gno
index ea585cc344d..491bc6575bf 100644
--- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-a/integration_test.gno
+++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/integration_test.gno
@@ -1,8 +1,8 @@
package upgradea
import (
- v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v1"
- v2 "gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v2"
+ v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v1"
+ v2 "gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v2"
)
func main() {
diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v1/v1.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v1/v1.gno
similarity index 100%
rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v1/v1.gno
rename to examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v1/v1.gno
diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v2/v2.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v2/v2.gno
similarity index 82%
rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v2/v2.gno
rename to examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v2/v2.gno
index f5a598b2849..2b3e5e4350c 100644
--- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v2/v2.gno
+++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v2/v2.gno
@@ -3,7 +3,7 @@ package upgradea
import (
"strconv"
- v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v1"
+ v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade_a/v1"
)
var counter int
diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-b/v1/v1.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_b/v1/v1.gno
similarity index 100%
rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-b/v1/v1.gno
rename to examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_b/v1/v1.gno
diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-b/v2/v2.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_b/v2/v2.gno
similarity index 91%
rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-b/v2/v2.gno
rename to examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_b/v2/v2.gno
index bc5862903a7..bf30ee1acab 100644
--- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-b/v2/v2.gno
+++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_b/v2/v2.gno
@@ -3,7 +3,7 @@ package upgradeb
import (
"std"
- v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade-b/v1"
+ v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade_b/v1"
)
const admin = "blahblah"
diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/root/root.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/root/root.gno
similarity index 96%
rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/root/root.gno
rename to examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/root/root.gno
index 6a51fe8001f..926b347c1bf 100644
--- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/root/root.gno
+++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/root/root.gno
@@ -2,7 +2,7 @@ package root
var (
counter int
- currentImplementation = "gno.land/r/x/manfred_upgrade_patterns/upgrade-c/v1"
+ currentImplementation = "gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v1"
)
func Inc() int {
diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/v1/v1.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v1/v1.gno
similarity index 57%
rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/v1/v1.gno
rename to examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v1/v1.gno
index cdacd4ac004..498217dfba0 100644
--- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/v1/v1.gno
+++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v1/v1.gno
@@ -1,6 +1,6 @@
package v1
-import "gno.land/r/x/manfred_upgrade_patterns/upgrade-c/root"
+import "gno.land/r/x/manfred_upgrade_patterns/upgrade_c/root"
func Inc() {
root.Inc()
diff --git a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/v2/v2.gno b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno
similarity index 57%
rename from examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/v2/v2.gno
rename to examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno
index 995b5e3568d..7d07337a17d 100644
--- a/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade-c/v2/v2.gno
+++ b/examples/gno.land/r/x/manfred_upgrade_patterns/upgrade_c/v2/v2.gno
@@ -1,6 +1,6 @@
package v1
-import "gno.land/r/x/manfred_upgrade_patterns/upgrade-c/root"
+import "gno.land/r/x/manfred_upgrade_patterns/upgrade_c/root"
func Inc() {
root.Inc()
diff --git a/gno.land/cmd/gnoland/testdata/float-arg.txtar b/gno.land/cmd/gnoland/testdata/float_arg.txtar
similarity index 100%
rename from gno.land/cmd/gnoland/testdata/float-arg.txtar
rename to gno.land/cmd/gnoland/testdata/float_arg.txtar
diff --git a/gno.land/cmd/gnoland/testdata/grc20-invalid-address.txtar b/gno.land/cmd/gnoland/testdata/grc20_invalid_address.txtar
similarity index 100%
rename from gno.land/cmd/gnoland/testdata/grc20-invalid-address.txtar
rename to gno.land/cmd/gnoland/testdata/grc20_invalid_address.txtar
diff --git a/gno.land/cmd/gnoland/testdata/grc20-registry.txtar b/gno.land/cmd/gnoland/testdata/grc20_registry.txtar
similarity index 100%
rename from gno.land/cmd/gnoland/testdata/grc20-registry.txtar
rename to gno.land/cmd/gnoland/testdata/grc20_registry.txtar
diff --git a/gno.land/cmd/gnoland/testdata/issue-1167.txtar b/gno.land/cmd/gnoland/testdata/issue_1167.txtar
similarity index 100%
rename from gno.land/cmd/gnoland/testdata/issue-1167.txtar
rename to gno.land/cmd/gnoland/testdata/issue_1167.txtar
diff --git a/gno.land/cmd/gnoland/testdata/issue-1588.txtar b/gno.land/cmd/gnoland/testdata/issue_1588.txtar
similarity index 100%
rename from gno.land/cmd/gnoland/testdata/issue-1588.txtar
rename to gno.land/cmd/gnoland/testdata/issue_1588.txtar
diff --git a/gno.land/cmd/gnoland/testdata/issue-1786.txtar b/gno.land/cmd/gnoland/testdata/issue_1786.txtar
similarity index 100%
rename from gno.land/cmd/gnoland/testdata/issue-1786.txtar
rename to gno.land/cmd/gnoland/testdata/issue_1786.txtar
diff --git a/gno.land/cmd/gnoland/testdata/issue-gnochess-97.txtar b/gno.land/cmd/gnoland/testdata/issue_gnochess_97.txtar
similarity index 100%
rename from gno.land/cmd/gnoland/testdata/issue-gnochess-97.txtar
rename to gno.land/cmd/gnoland/testdata/issue_gnochess_97.txtar
diff --git a/gno.land/cmd/gnoland/testdata/map-storage.txtar b/gno.land/cmd/gnoland/testdata/map_storage.txtar
similarity index 100%
rename from gno.land/cmd/gnoland/testdata/map-storage.txtar
rename to gno.land/cmd/gnoland/testdata/map_storage.txtar
diff --git a/gnovm/cmd/gno/clean_test.go b/gnovm/cmd/gno/clean_test.go
index f2694b66f4c..cfca2655031 100644
--- a/gnovm/cmd/gno/clean_test.go
+++ b/gnovm/cmd/gno/clean_test.go
@@ -23,13 +23,13 @@ func TestCleanApp(t *testing.T) {
},
{
args: []string{"clean"},
- testDir: "../../tests/integ/empty-dir",
+ testDir: "../../tests/integ/empty_dir",
simulateExternalRepo: true,
errShouldBe: "not a gno module: gno.mod file not found in current or any parent directory",
},
{
args: []string{"clean"},
- testDir: "../../tests/integ/minimalist-gnomod",
+ testDir: "../../tests/integ/minimalist_gnomod",
simulateExternalRepo: true,
},
}
diff --git a/gnovm/cmd/gno/lint_test.go b/gnovm/cmd/gno/lint_test.go
index 1966eab7c9e..5773ae0a06f 100644
--- a/gnovm/cmd/gno/lint_test.go
+++ b/gnovm/cmd/gno/lint_test.go
@@ -8,22 +8,22 @@ func TestLintApp(t *testing.T) {
args: []string{"lint"},
errShouldBe: "flag: help requested",
}, {
- args: []string{"lint", "--set-exit-status=0", "../../tests/integ/run-main/"},
- stderrShouldContain: "./../../tests/integ/run-main: missing 'gno.mod' file (code=1).",
+ args: []string{"lint", "--set-exit-status=0", "../../tests/integ/run_main/"},
+ stderrShouldContain: "./../../tests/integ/run_main: missing 'gno.mod' file (code=1).",
}, {
- args: []string{"lint", "--set-exit-status=0", "../../tests/integ/undefined-variable-test/undefined_variables_test.gno"},
+ args: []string{"lint", "--set-exit-status=0", "../../tests/integ/undefined_variable_test/undefined_variables_test.gno"},
stderrShouldContain: "undefined_variables_test.gno:6: name toto not declared (code=2)",
}, {
- args: []string{"lint", "--set-exit-status=0", "../../tests/integ/package-not-declared/main.gno"},
+ args: []string{"lint", "--set-exit-status=0", "../../tests/integ/package_not_declared/main.gno"},
stderrShouldContain: "main.gno:4: name fmt not declared (code=2).",
}, {
- args: []string{"lint", "--set-exit-status=0", "../../tests/integ/run-main/"},
- stderrShouldContain: "./../../tests/integ/run-main: missing 'gno.mod' file (code=1).",
+ args: []string{"lint", "--set-exit-status=0", "../../tests/integ/run_main/"},
+ stderrShouldContain: "./../../tests/integ/run_main: missing 'gno.mod' file (code=1).",
}, {
- args: []string{"lint", "--set-exit-status=0", "../../tests/integ/minimalist-gnomod/"},
+ args: []string{"lint", "--set-exit-status=0", "../../tests/integ/minimalist_gnomod/"},
// TODO: raise an error because there is a gno.mod, but no .gno files
}, {
- args: []string{"lint", "--set-exit-status=0", "../../tests/integ/invalid-module-name/"},
+ args: []string{"lint", "--set-exit-status=0", "../../tests/integ/invalid_module_name/"},
// TODO: raise an error because gno.mod is invalid
},
diff --git a/gnovm/cmd/gno/mod_test.go b/gnovm/cmd/gno/mod_test.go
index 789bba61146..d35ab311b6c 100644
--- a/gnovm/cmd/gno/mod_test.go
+++ b/gnovm/cmd/gno/mod_test.go
@@ -19,63 +19,63 @@ func TestModApp(t *testing.T) {
// test `gno mod download`
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/empty-dir",
+ testDir: "../../tests/integ/empty_dir",
simulateExternalRepo: true,
errShouldBe: "gno.mod not found",
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/empty-gnomod",
+ testDir: "../../tests/integ/empty_gnomod",
simulateExternalRepo: true,
errShouldBe: "validate: requires module",
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/invalid-module-name",
+ testDir: "../../tests/integ/invalid_module_name",
simulateExternalRepo: true,
errShouldContain: "usage: module module/path",
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/minimalist-gnomod",
+ testDir: "../../tests/integ/minimalist_gnomod",
simulateExternalRepo: true,
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/require-remote-module",
+ testDir: "../../tests/integ/require_remote_module",
simulateExternalRepo: true,
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/require-invalid-module",
+ testDir: "../../tests/integ/require_invalid_module",
simulateExternalRepo: true,
errShouldContain: "fetch: writepackage: querychain",
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/invalid-module-version1",
+ testDir: "../../tests/integ/invalid_module_version1",
simulateExternalRepo: true,
errShouldContain: "usage: require module/path v1.2.3",
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/invalid-module-version2",
+ testDir: "../../tests/integ/invalid_module_version2",
simulateExternalRepo: true,
errShouldContain: "invalid: must be of the form v1.2.3",
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/replace-with-dir",
+ testDir: "../../tests/integ/replace_with_dir",
simulateExternalRepo: true,
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/replace-with-module",
+ testDir: "../../tests/integ/replace_with_module",
simulateExternalRepo: true,
},
{
args: []string{"mod", "download"},
- testDir: "../../tests/integ/replace-with-invalid-module",
+ testDir: "../../tests/integ/replace_with_invalid_module",
simulateExternalRepo: true,
errShouldContain: "fetch: writepackage: querychain",
},
@@ -88,31 +88,31 @@ func TestModApp(t *testing.T) {
},
{
args: []string{"mod", "init"},
- testDir: "../../tests/integ/empty-dir",
+ testDir: "../../tests/integ/empty_dir",
simulateExternalRepo: true,
errShouldBe: "create gno.mod file: cannot determine package name",
},
{
args: []string{"mod", "init"},
- testDir: "../../tests/integ/empty-gno1",
+ testDir: "../../tests/integ/empty_gno1",
simulateExternalRepo: true,
recoverShouldContain: "expected 'package', found 'EOF'",
},
{
args: []string{"mod", "init"},
- testDir: "../../tests/integ/empty-gno2",
+ testDir: "../../tests/integ/empty_gno2",
simulateExternalRepo: true,
recoverShouldContain: "expected 'package', found 'EOF'",
},
{
args: []string{"mod", "init"},
- testDir: "../../tests/integ/empty-gno3",
+ testDir: "../../tests/integ/empty_gno3",
simulateExternalRepo: true,
recoverShouldContain: "expected 'package', found 'EOF'",
},
{
args: []string{"mod", "init"},
- testDir: "../../tests/integ/empty-gnomod",
+ testDir: "../../tests/integ/empty_gnomod",
simulateExternalRepo: true,
errShouldBe: "create gno.mod file: gno.mod file already exists",
},
@@ -120,27 +120,27 @@ func TestModApp(t *testing.T) {
// test `gno mod init` with module name
{
args: []string{"mod", "init", "gno.land/p/demo/foo"},
- testDir: "../../tests/integ/empty-dir",
+ testDir: "../../tests/integ/empty_dir",
simulateExternalRepo: true,
},
{
args: []string{"mod", "init", "gno.land/p/demo/foo"},
- testDir: "../../tests/integ/empty-gno1",
+ testDir: "../../tests/integ/empty_gno1",
simulateExternalRepo: true,
},
{
args: []string{"mod", "init", "gno.land/p/demo/foo"},
- testDir: "../../tests/integ/empty-gno2",
+ testDir: "../../tests/integ/empty_gno2",
simulateExternalRepo: true,
},
{
args: []string{"mod", "init", "gno.land/p/demo/foo"},
- testDir: "../../tests/integ/empty-gno3",
+ testDir: "../../tests/integ/empty_gno3",
simulateExternalRepo: true,
},
{
args: []string{"mod", "init", "gno.land/p/demo/foo"},
- testDir: "../../tests/integ/empty-gnomod",
+ testDir: "../../tests/integ/empty_gnomod",
simulateExternalRepo: true,
errShouldBe: "create gno.mod file: gno.mod file already exists",
},
@@ -148,30 +148,30 @@ func TestModApp(t *testing.T) {
// test `gno mod tidy`
{
args: []string{"mod", "tidy", "arg1"},
- testDir: "../../tests/integ/minimalist-gnomod",
+ testDir: "../../tests/integ/minimalist_gnomod",
simulateExternalRepo: true,
errShouldContain: "flag: help requested",
},
{
args: []string{"mod", "tidy"},
- testDir: "../../tests/integ/empty-dir",
+ testDir: "../../tests/integ/empty_dir",
simulateExternalRepo: true,
errShouldContain: "could not read gno.mod file",
},
{
args: []string{"mod", "tidy"},
- testDir: "../../tests/integ/invalid-module-version1",
+ testDir: "../../tests/integ/invalid_module_version1",
simulateExternalRepo: true,
errShouldContain: "error parsing gno.mod file at",
},
{
args: []string{"mod", "tidy"},
- testDir: "../../tests/integ/minimalist-gnomod",
+ testDir: "../../tests/integ/minimalist_gnomod",
simulateExternalRepo: true,
},
{
args: []string{"mod", "tidy"},
- testDir: "../../tests/integ/require-remote-module",
+ testDir: "../../tests/integ/require_remote_module",
simulateExternalRepo: true,
},
{
@@ -181,7 +181,7 @@ func TestModApp(t *testing.T) {
},
{
args: []string{"mod", "tidy"},
- testDir: "../../tests/integ/invalid-gno-file",
+ testDir: "../../tests/integ/invalid_gno_file",
simulateExternalRepo: true,
errShouldContain: "expected 'package', found packag",
},
@@ -189,31 +189,31 @@ func TestModApp(t *testing.T) {
// test `gno mod why`
{
args: []string{"mod", "why"},
- testDir: "../../tests/integ/minimalist-gnomod",
+ testDir: "../../tests/integ/minimalist_gnomod",
simulateExternalRepo: true,
errShouldContain: "flag: help requested",
},
{
args: []string{"mod", "why", "std"},
- testDir: "../../tests/integ/empty-dir",
+ testDir: "../../tests/integ/empty_dir",
simulateExternalRepo: true,
errShouldContain: "could not read gno.mod file",
},
{
args: []string{"mod", "why", "std"},
- testDir: "../../tests/integ/invalid-module-version1",
+ testDir: "../../tests/integ/invalid_module_version1",
simulateExternalRepo: true,
errShouldContain: "error parsing gno.mod file at",
},
{
args: []string{"mod", "why", "std"},
- testDir: "../../tests/integ/invalid-gno-file",
+ testDir: "../../tests/integ/invalid_gno_file",
simulateExternalRepo: true,
errShouldContain: "expected 'package', found packag",
},
{
args: []string{"mod", "why", "std"},
- testDir: "../../tests/integ/minimalist-gnomod",
+ testDir: "../../tests/integ/minimalist_gnomod",
simulateExternalRepo: true,
stdoutShouldBe: `# std
(module minim does not need package std)
@@ -221,7 +221,7 @@ func TestModApp(t *testing.T) {
},
{
args: []string{"mod", "why", "std"},
- testDir: "../../tests/integ/require-remote-module",
+ testDir: "../../tests/integ/require_remote_module",
simulateExternalRepo: true,
stdoutShouldBe: `# std
(module gno.land/tests/importavl does not need package std)
@@ -308,10 +308,10 @@ func TestGetGnoImports(t *testing.T) {
name: filepath.Join("subtmp", "file1.gno"),
data: `
package subtmp
-
+
import (
"std"
-
+
"gno.land/p/demo/subpkg1"
)
`,
@@ -320,7 +320,7 @@ func TestGetGnoImports(t *testing.T) {
name: filepath.Join("subtmp", "file2.gno"),
data: `
package subtmp
-
+
import (
"gno.land/p/demo/subpkg1"
"gno.land/p/demo/subpkg2"
diff --git a/gnovm/cmd/gno/run_test.go b/gnovm/cmd/gno/run_test.go
index 575798a78dc..b0cc1514bcb 100644
--- a/gnovm/cmd/gno/run_test.go
+++ b/gnovm/cmd/gno/run_test.go
@@ -9,62 +9,62 @@ func TestRunApp(t *testing.T) {
errShouldBe: "flag: help requested",
},
{
- args: []string{"run", "../../tests/integ/run-main/main.gno"},
+ args: []string{"run", "../../tests/integ/run_main/main.gno"},
stdoutShouldContain: "hello world!",
},
{
- args: []string{"run", "../../tests/integ/run-main/"},
+ args: []string{"run", "../../tests/integ/run_main/"},
stdoutShouldContain: "hello world!",
},
{
- args: []string{"run", "../../tests/integ/does-not-exist"},
+ args: []string{"run", "../../tests/integ/does_not_exist"},
errShouldContain: "no such file or directory",
},
{
- args: []string{"run", "../../tests/integ/run-namedpkg/main.gno"},
+ args: []string{"run", "../../tests/integ/run_namedpkg/main.gno"},
stdoutShouldContain: "hello, other world!",
},
{
- args: []string{"run", "../../tests/integ/run-package"},
+ args: []string{"run", "../../tests/integ/run_package"},
recoverShouldContain: "name main not declared",
},
{
- args: []string{"run", "-expr", "Hello()", "../../tests/integ/run-package"},
+ args: []string{"run", "-expr", "Hello()", "../../tests/integ/run_package"},
stdoutShouldContain: "called Hello",
},
{
- args: []string{"run", "-expr", "World()", "../../tests/integ/run-package"},
+ args: []string{"run", "-expr", "World()", "../../tests/integ/run_package"},
stdoutShouldContain: "called World",
},
{
- args: []string{"run", "-expr", "otherFile()", "../../tests/integ/run-package"},
+ args: []string{"run", "-expr", "otherFile()", "../../tests/integ/run_package"},
stdoutShouldContain: "hello from package2.gno",
},
{
args: []string{
"run", "-expr", "otherFile()",
- "../../tests/integ/run-package/package.gno",
+ "../../tests/integ/run_package/package.gno",
},
recoverShouldContain: "name otherFile not declared",
},
{
args: []string{
"run", "-expr", "otherFile()",
- "../../tests/integ/run-package/package.gno",
- "../../tests/integ/run-package/package2.gno",
+ "../../tests/integ/run_package/package.gno",
+ "../../tests/integ/run_package/package2.gno",
},
stdoutShouldContain: "hello from package2.gno",
},
{
- args: []string{"run", "-expr", "WithArg(1)", "../../tests/integ/run-package"},
+ args: []string{"run", "-expr", "WithArg(1)", "../../tests/integ/run_package"},
stdoutShouldContain: "one",
},
{
- args: []string{"run", "-expr", "WithArg(-255)", "../../tests/integ/run-package"},
+ args: []string{"run", "-expr", "WithArg(-255)", "../../tests/integ/run_package"},
stdoutShouldContain: "out of range!",
},
{
- args: []string{"run", "../../tests/integ/undefined-variable-test/undefined_variables_test.gno"},
+ args: []string{"run", "../../tests/integ/undefined_variable_test/undefined_variables_test.gno"},
recoverShouldContain: "--- preprocess stack ---", // should contain preprocess debug stack trace
},
// TODO: a test file
diff --git a/gnovm/tests/backup/issue-558.gno b/gnovm/tests/backup/issue_558.gno
similarity index 100%
rename from gnovm/tests/backup/issue-558.gno
rename to gnovm/tests/backup/issue_558.gno
diff --git a/gnovm/tests/backup/issue-770.gno b/gnovm/tests/backup/issue_770.gno
similarity index 100%
rename from gnovm/tests/backup/issue-770.gno
rename to gnovm/tests/backup/issue_770.gno
diff --git a/gnovm/tests/backup/issue-772.gno b/gnovm/tests/backup/issue_772.gno
similarity index 100%
rename from gnovm/tests/backup/issue-772.gno
rename to gnovm/tests/backup/issue_772.gno
diff --git a/gnovm/tests/backup/issue-775.gno b/gnovm/tests/backup/issue_775.gno
similarity index 100%
rename from gnovm/tests/backup/issue-775.gno
rename to gnovm/tests/backup/issue_775.gno
diff --git a/gnovm/tests/backup/redeclaration-global2.gno b/gnovm/tests/backup/redeclaration_global2.gno
similarity index 65%
rename from gnovm/tests/backup/redeclaration-global2.gno
rename to gnovm/tests/backup/redeclaration_global2.gno
index de9f3e6078d..0c3cda101b1 100644
--- a/gnovm/tests/backup/redeclaration-global2.gno
+++ b/gnovm/tests/backup/redeclaration_global2.gno
@@ -2,7 +2,6 @@ package main
import (
"time"
- "time"
)
func main() {
@@ -11,4 +10,4 @@ func main() {
}
// Error:
-// ../_test/redeclaration-global2.gno:5:2: time/redeclaration-global2.gno redeclared in this block
+// ../_test/redeclaration_global2.gno:5:2: time/redeclaration_global2.gno redeclared in this block
diff --git a/gnovm/tests/backup/redeclaration-global3.gno b/gnovm/tests/backup/redeclaration_global3.gno
similarity index 64%
rename from gnovm/tests/backup/redeclaration-global3.gno
rename to gnovm/tests/backup/redeclaration_global3.gno
index 49ad1da3f82..f55e54891b8 100644
--- a/gnovm/tests/backup/redeclaration-global3.gno
+++ b/gnovm/tests/backup/redeclaration_global3.gno
@@ -12,4 +12,4 @@ func main() {
}
// Error:
-// ../_test/redeclaration-global3.gno:7:5: time redeclared in this block
+// ../_test/redeclaration_global3.gno:7:5: time redeclared in this block
diff --git a/gnovm/tests/backup/redeclaration-global4.gno b/gnovm/tests/backup/redeclaration_global4.gno
similarity index 65%
rename from gnovm/tests/backup/redeclaration-global4.gno
rename to gnovm/tests/backup/redeclaration_global4.gno
index 74114c0d95c..3df141d50b6 100644
--- a/gnovm/tests/backup/redeclaration-global4.gno
+++ b/gnovm/tests/backup/redeclaration_global4.gno
@@ -12,4 +12,4 @@ func main() {
}
// Error:
-// ../_test/redeclaration-global4.gno:7:6: time redeclared in this block
+// ../_test/redeclaration_global4.gno:7:6: time redeclared in this block
diff --git a/gnovm/tests/backup/redeclaration-global6.gno b/gnovm/tests/backup/redeclaration_global6.gno
similarity index 67%
rename from gnovm/tests/backup/redeclaration-global6.gno
rename to gnovm/tests/backup/redeclaration_global6.gno
index f685969515e..b87cbcb3949 100644
--- a/gnovm/tests/backup/redeclaration-global6.gno
+++ b/gnovm/tests/backup/redeclaration_global6.gno
@@ -14,4 +14,4 @@ func main() {
}
// Error:
-// ../_test/redeclaration-global6.gno:7:1: time redeclared in this block
+// ../_test/redeclaration_global6.gno:7:1: time redeclared in this block
diff --git a/gnovm/tests/backup/redeclaration-global7.gno b/gnovm/tests/backup/redeclaration_global7.gno
similarity index 80%
rename from gnovm/tests/backup/redeclaration-global7.gno
rename to gnovm/tests/backup/redeclaration_global7.gno
index e968ab32363..3e63b7d50e9 100644
--- a/gnovm/tests/backup/redeclaration-global7.gno
+++ b/gnovm/tests/backup/redeclaration_global7.gno
@@ -10,4 +10,4 @@ func main() {
}
// Error:
-// ../_test/redeclaration-global7.gno:5:2: quux/redeclaration-global7.gno redeclared as imported package name
+// ../_test/redeclaration_global7.gno:5:2: quux/redeclaration_global7.gno redeclared as imported package name
diff --git a/gnovm/tests/backup/selector-scope0.gno b/gnovm/tests/backup/selector_scope0.gno
similarity index 100%
rename from gnovm/tests/backup/selector-scope0.gno
rename to gnovm/tests/backup/selector_scope0.gno
diff --git a/gnovm/tests/files/extern/baz-bat/baz-bat.gno b/gnovm/tests/files/extern/baz-bat/baz-bat.gno
deleted file mode 100644
index ff3d8976560..00000000000
--- a/gnovm/tests/files/extern/baz-bat/baz-bat.gno
+++ /dev/null
@@ -1,3 +0,0 @@
-package baz
-
-var Name = "baz-bat"
diff --git a/gnovm/tests/files/extern/baz_bat/baz_bat.gno b/gnovm/tests/files/extern/baz_bat/baz_bat.gno
new file mode 100644
index 00000000000..3835339a2ce
--- /dev/null
+++ b/gnovm/tests/files/extern/baz_bat/baz_bat.gno
@@ -0,0 +1,3 @@
+package baz
+
+var Name = "baz_bat"
diff --git a/gnovm/tests/files/extern/foo-bar/foo-bar.gno b/gnovm/tests/files/extern/foo-bar/foo-bar.gno
deleted file mode 100644
index 15206ce8e32..00000000000
--- a/gnovm/tests/files/extern/foo-bar/foo-bar.gno
+++ /dev/null
@@ -1,3 +0,0 @@
-package bar
-
-var Name = "foo-bar"
diff --git a/gnovm/tests/files/extern/foo_bar/foo_bar.gno b/gnovm/tests/files/extern/foo_bar/foo_bar.gno
new file mode 100644
index 00000000000..aefa7c81bec
--- /dev/null
+++ b/gnovm/tests/files/extern/foo_bar/foo_bar.gno
@@ -0,0 +1,3 @@
+package bar
+
+var Name = "foo_bar"
diff --git a/gnovm/tests/files/extern/timtadh/data-structures/tree/avl/avltree.gno b/gnovm/tests/files/extern/timtadh/data_structures/tree/avl/avltree.gno
similarity index 97%
rename from gnovm/tests/files/extern/timtadh/data-structures/tree/avl/avltree.gno
rename to gnovm/tests/files/extern/timtadh/data_structures/tree/avl/avltree.gno
index 7b468179113..7f7161063c9 100644
--- a/gnovm/tests/files/extern/timtadh/data-structures/tree/avl/avltree.gno
+++ b/gnovm/tests/files/extern/timtadh/data_structures/tree/avl/avltree.gno
@@ -1,12 +1,12 @@
-// modified from https://github.com/timtadh/data-structures
+// modified from https://github.com/timtadh/data_structures
package avl
import (
"fmt"
- "github.com/gnolang/gno/_test/timtadh/data-structures/tree"
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/tree"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
func abs(i int) int {
diff --git a/gnovm/tests/files/extern/timtadh/data-structures/tree/utils.gno b/gnovm/tests/files/extern/timtadh/data_structures/tree/utils.gno
similarity index 95%
rename from gnovm/tests/files/extern/timtadh/data-structures/tree/utils.gno
rename to gnovm/tests/files/extern/timtadh/data_structures/tree/utils.gno
index 1cba11f6753..d26baa93b88 100644
--- a/gnovm/tests/files/extern/timtadh/data-structures/tree/utils.gno
+++ b/gnovm/tests/files/extern/timtadh/data_structures/tree/utils.gno
@@ -1,9 +1,9 @@
-// from https://raw.githubusercontent.com/timtadh/data-structures/master/tree/util.gno
+// from https://raw.githubusercontent.com/timtadh/data_structures/master/tree/util.gno
package tree
import (
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
func pop(stack []types.TreeNode) ([]types.TreeNode, types.TreeNode) {
diff --git a/gnovm/tests/files/extern/timtadh/data-structures/types/map_entry.gno b/gnovm/tests/files/extern/timtadh/data_structures/types/map_entry.gno
similarity index 100%
rename from gnovm/tests/files/extern/timtadh/data-structures/types/map_entry.gno
rename to gnovm/tests/files/extern/timtadh/data_structures/types/map_entry.gno
diff --git a/gnovm/tests/files/extern/timtadh/data-structures/types/string.gno b/gnovm/tests/files/extern/timtadh/data_structures/types/string.gno
similarity index 100%
rename from gnovm/tests/files/extern/timtadh/data-structures/types/string.gno
rename to gnovm/tests/files/extern/timtadh/data_structures/types/string.gno
diff --git a/gnovm/tests/files/extern/timtadh/data-structures/types/types.gno b/gnovm/tests/files/extern/timtadh/data_structures/types/types.gno
similarity index 98%
rename from gnovm/tests/files/extern/timtadh/data-structures/types/types.gno
rename to gnovm/tests/files/extern/timtadh/data_structures/types/types.gno
index 81af016c094..daa6e57e24e 100644
--- a/gnovm/tests/files/extern/timtadh/data-structures/types/types.gno
+++ b/gnovm/tests/files/extern/timtadh/data_structures/types/types.gno
@@ -1,4 +1,4 @@
-// from https://raw.githubusercontent.com/timtadh/data-structures/master/types/types.gno
+// from https://raw.githubusercontent.com/timtadh/data_structures/master/types/types.gno
package types
diff --git a/gnovm/tests/files/extern/timtadh/data-structures/types/util.gno b/gnovm/tests/files/extern/timtadh/data_structures/types/util.gno
similarity index 100%
rename from gnovm/tests/files/extern/timtadh/data-structures/types/util.gno
rename to gnovm/tests/files/extern/timtadh/data_structures/types/util.gno
diff --git a/gnovm/tests/files/import7.gno b/gnovm/tests/files/import7.gno
index 1347e752474..7b66397301a 100644
--- a/gnovm/tests/files/import7.gno
+++ b/gnovm/tests/files/import7.gno
@@ -1,10 +1,10 @@
package main
-import bar "github.com/gnolang/gno/_test/foo-bar"
+import bar "github.com/gnolang/gno/_test/foo_bar"
func main() {
println(bar.Name)
}
// Output:
-// foo-bar
+// foo_bar
diff --git a/gnovm/tests/files/import9.gno b/gnovm/tests/files/import9.gno
index abf3a158ee0..e5bc6d2213d 100644
--- a/gnovm/tests/files/import9.gno
+++ b/gnovm/tests/files/import9.gno
@@ -1,10 +1,10 @@
package main
-import "github.com/gnolang/gno/_test/baz-bat"
+import "github.com/gnolang/gno/_test/baz_bat"
func main() {
println(baz.Name)
}
// Output:
-// baz-bat
+// baz_bat
diff --git a/gnovm/tests/files/issue-1085.gno b/gnovm/tests/files/issue_1085.gno
similarity index 100%
rename from gnovm/tests/files/issue-1085.gno
rename to gnovm/tests/files/issue_1085.gno
diff --git a/gnovm/tests/files/issue-1096.gno b/gnovm/tests/files/issue_1096.gno
similarity index 100%
rename from gnovm/tests/files/issue-1096.gno
rename to gnovm/tests/files/issue_1096.gno
diff --git a/gnovm/tests/files/issue-558b.gno b/gnovm/tests/files/issue_558b.gno
similarity index 100%
rename from gnovm/tests/files/issue-558b.gno
rename to gnovm/tests/files/issue_558b.gno
diff --git a/gnovm/tests/files/issue-735.gno b/gnovm/tests/files/issue_735.gno
similarity index 100%
rename from gnovm/tests/files/issue-735.gno
rename to gnovm/tests/files/issue_735.gno
diff --git a/gnovm/tests/files/issue-776.gno b/gnovm/tests/files/issue_776.gno
similarity index 100%
rename from gnovm/tests/files/issue-776.gno
rename to gnovm/tests/files/issue_776.gno
diff --git a/gnovm/tests/files/issue-782.gno b/gnovm/tests/files/issue_782.gno
similarity index 100%
rename from gnovm/tests/files/issue-782.gno
rename to gnovm/tests/files/issue_782.gno
diff --git a/gnovm/tests/files/issue-784.gno b/gnovm/tests/files/issue_784.gno
similarity index 100%
rename from gnovm/tests/files/issue-784.gno
rename to gnovm/tests/files/issue_784.gno
diff --git a/gnovm/tests/files/issue-880.gno b/gnovm/tests/files/issue_880.gno
similarity index 100%
rename from gnovm/tests/files/issue-880.gno
rename to gnovm/tests/files/issue_880.gno
diff --git a/gnovm/tests/files/redeclaration-global0.gno b/gnovm/tests/files/redeclaration-global0.gno
deleted file mode 100644
index 86d4c234340..00000000000
--- a/gnovm/tests/files/redeclaration-global0.gno
+++ /dev/null
@@ -1,14 +0,0 @@
-package main
-
-type time int
-
-var time string
-
-func main() {
- time = "hello"
- println(time)
-}
-
-// Error:
-// files/redeclaration-global0.gno:5:5: time redeclared in this block
-// previous declaration at files/redeclaration-global0.gno:3:6
diff --git a/gnovm/tests/files/redeclaration-global1.gno b/gnovm/tests/files/redeclaration-global1.gno
deleted file mode 100644
index b48364f06e0..00000000000
--- a/gnovm/tests/files/redeclaration-global1.gno
+++ /dev/null
@@ -1,14 +0,0 @@
-package main
-
-var time int
-
-type time string
-
-func main() {
- var t time = "hello"
- println(t)
-}
-
-// Error:
-// files/redeclaration-global1.gno:5:6: time redeclared in this block
-// previous declaration at files/redeclaration-global1.gno:3:5
diff --git a/gnovm/tests/files/redeclaration-global5.gno b/gnovm/tests/files/redeclaration-global5.gno
deleted file mode 100644
index c8eded62acc..00000000000
--- a/gnovm/tests/files/redeclaration-global5.gno
+++ /dev/null
@@ -1,16 +0,0 @@
-package main
-
-var time int
-
-func time() string {
- return "hello"
-}
-
-func main() {
- t := time()
- println(t)
-}
-
-// Error:
-// files/redeclaration-global5.gno:5:6: time redeclared in this block
-// previous declaration at files/redeclaration-global5.gno:3:5
diff --git a/gnovm/tests/files/redeclaration_global0.gno b/gnovm/tests/files/redeclaration_global0.gno
new file mode 100644
index 00000000000..19138744f1d
--- /dev/null
+++ b/gnovm/tests/files/redeclaration_global0.gno
@@ -0,0 +1,14 @@
+package main
+
+type time int
+
+var time string
+
+func main() {
+ time = "hello"
+ println(time)
+}
+
+// Error:
+// files/redeclaration_global0.gno:5:5: time redeclared in this block
+// previous declaration at files/redeclaration_global0.gno:3:6
diff --git a/gnovm/tests/files/redeclaration_global1.gno b/gnovm/tests/files/redeclaration_global1.gno
new file mode 100644
index 00000000000..8b05245d41d
--- /dev/null
+++ b/gnovm/tests/files/redeclaration_global1.gno
@@ -0,0 +1,14 @@
+package main
+
+var time int
+
+type time string
+
+func main() {
+ var t time = "hello"
+ println(t)
+}
+
+// Error:
+// files/redeclaration_global1.gno:5:6: time redeclared in this block
+// previous declaration at files/redeclaration_global1.gno:3:5
diff --git a/gnovm/tests/files/redeclaration_global5.gno b/gnovm/tests/files/redeclaration_global5.gno
new file mode 100644
index 00000000000..e7b868a03cf
--- /dev/null
+++ b/gnovm/tests/files/redeclaration_global5.gno
@@ -0,0 +1,16 @@
+package main
+
+var time int
+
+func time() string {
+ return "hello"
+}
+
+func main() {
+ t := time()
+ println(t)
+}
+
+// Error:
+// files/redeclaration_global5.gno:5:6: time redeclared in this block
+// previous declaration at files/redeclaration_global5.gno:3:5
diff --git a/gnovm/tests/files/zavltree.gno b/gnovm/tests/files/zavltree.gno
index 40d29886480..02e8bfef27e 100644
--- a/gnovm/tests/files/zavltree.gno
+++ b/gnovm/tests/files/zavltree.gno
@@ -1,8 +1,8 @@
package main
import (
- "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl"
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
func main() {
diff --git a/gnovm/tests/files/zavltree0.gno b/gnovm/tests/files/zavltree0.gno
index 6aa355da2b5..8a8e305e0d0 100644
--- a/gnovm/tests/files/zavltree0.gno
+++ b/gnovm/tests/files/zavltree0.gno
@@ -1,8 +1,8 @@
package main
import (
- "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl"
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
func main() {
diff --git a/gnovm/tests/files/zavltree1.gno b/gnovm/tests/files/zavltree1.gno
index 256231230dd..73536c2732e 100644
--- a/gnovm/tests/files/zavltree1.gno
+++ b/gnovm/tests/files/zavltree1.gno
@@ -1,8 +1,8 @@
package main
import (
- "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl"
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
func main() {
diff --git a/gnovm/tests/files/zrealm4.gno b/gnovm/tests/files/zrealm4.gno
index dc3c48c774b..4f5254b6951 100644
--- a/gnovm/tests/files/zrealm4.gno
+++ b/gnovm/tests/files/zrealm4.gno
@@ -2,8 +2,8 @@
package test
import (
- "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl"
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
var tree *avl.AvlNode
@@ -28,7 +28,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -57,7 +57,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -66,7 +66,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// }
@@ -175,7 +175,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -185,11 +185,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "595a04d579b5f4488af282873f64a902913646a6",
+// "Hash": "de0c4b2dd935220f7d37d10fc9feb1448bfb011d",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4"
// }
// }
diff --git a/gnovm/tests/files/zrealm5.gno b/gnovm/tests/files/zrealm5.gno
index e65b089c18d..ebe107290e7 100644
--- a/gnovm/tests/files/zrealm5.gno
+++ b/gnovm/tests/files/zrealm5.gno
@@ -2,8 +2,8 @@
package test
import (
- "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl"
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
var tree *avl.AvlNode
@@ -28,7 +28,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -57,7 +57,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -66,7 +66,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// }
@@ -83,7 +83,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -112,7 +112,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -121,7 +121,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -131,11 +131,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "55e56c78d2a901aaab1efe57d29e0f30e3b8cf72",
+// "Hash": "1a4158d473290431f9d4f9c5a85a3b6697640f2a",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5"
// }
// }
@@ -246,7 +246,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -256,11 +256,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "ce2f1ef3316fb6092409cfa03ee5af4950c071a6",
+// "Hash": "011a2960ff92aedda8acd122b9f4d251902e0cd8",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4"
// }
// }
diff --git a/gnovm/tests/files/zrealm6.gno b/gnovm/tests/files/zrealm6.gno
index 20615fa7d39..9884ab1909c 100644
--- a/gnovm/tests/files/zrealm6.gno
+++ b/gnovm/tests/files/zrealm6.gno
@@ -2,8 +2,8 @@
package test
import (
- "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl"
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
var tree *avl.AvlNode
@@ -29,7 +29,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -58,7 +58,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -67,7 +67,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// }
@@ -84,7 +84,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -113,7 +113,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -122,7 +122,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -132,11 +132,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "61131413833a7638001260b674860d3d3afce0dc",
+// "Hash": "b1719c55a9b07d432385f020b0bdbc678ba2b9ac",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6"
// }
// }
@@ -155,7 +155,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -184,7 +184,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -193,7 +193,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -203,11 +203,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "bdc65e33d3ee6d6335e55f235115504922ee30fd",
+// "Hash": "8d00c3fa6c15cb0d78dcbaa23df49f96bbc9591b",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5"
// }
// }
@@ -318,7 +318,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -328,11 +328,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "5b4102ad6dc4a542b6b036ec6f5b99d0b37dbc64",
+// "Hash": "8f2cb2a771ddc55ab5798b791e16df547c94d862",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4"
// }
// }
diff --git a/gnovm/tests/files/zrealm7.gno b/gnovm/tests/files/zrealm7.gno
index 9decb0dae10..a706ffcad78 100644
--- a/gnovm/tests/files/zrealm7.gno
+++ b/gnovm/tests/files/zrealm7.gno
@@ -2,8 +2,8 @@
package test
import (
- "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl"
- "github.com/gnolang/gno/_test/timtadh/data-structures/types"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl"
+ "github.com/gnolang/gno/_test/timtadh/data_structures/types"
)
var tree *avl.AvlNode
@@ -30,7 +30,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -59,7 +59,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -68,7 +68,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// }
@@ -85,7 +85,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -114,7 +114,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -123,7 +123,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -133,11 +133,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "a57d5931df0feca0936066442c91cb86362662a1",
+// "Hash": "2ac7cc7e6fdb1ff6dc1f340486011f1449757d85",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7"
// }
// }
@@ -156,7 +156,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -185,7 +185,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// },
@@ -194,7 +194,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// }
// }
@@ -211,7 +211,7 @@ func main() {
// {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/types.String"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/types.String"
// },
// "V": {
// "@type": "/gno.StringValue",
@@ -240,7 +240,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -250,11 +250,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "a0b7f434a4a6029a1fb7b155a76be557199b7df5",
+// "Hash": "a0af92becf7bef8d5d71c94e8f8f044e4cfe526d",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:4"
// }
// }
@@ -265,7 +265,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -275,11 +275,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "1021321597dd510115ccd8896076624c04f4dfb4",
+// "Hash": "752161efcfe5a3e2ef70c03ff4354097f09ada56",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6"
// }
// }
@@ -390,7 +390,7 @@ func main() {
// "@type": "/gno.PointerType",
// "Elt": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// }
// },
// "V": {
@@ -400,11 +400,11 @@ func main() {
// "TV": {
// "T": {
// "@type": "/gno.RefType",
-// "ID": "github.com/gnolang/gno/_test/timtadh/data-structures/tree/avl.AvlNode"
+// "ID": "github.com/gnolang/gno/_test/timtadh/data_structures/tree/avl.AvlNode"
// },
// "V": {
// "@type": "/gno.RefValue",
-// "Hash": "b18a7f834ba211ddd8b33135252de091edc96655",
+// "Hash": "c59d05a21bf190551bb15a8b9d41a9e8da717f3d",
// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5"
// }
// }
diff --git a/gnovm/tests/integ/empty-dir/.gitkeep b/gnovm/tests/integ/empty_dir/.gitkeep
similarity index 100%
rename from gnovm/tests/integ/empty-dir/.gitkeep
rename to gnovm/tests/integ/empty_dir/.gitkeep
diff --git a/gnovm/tests/integ/empty-gno1/empty.gno b/gnovm/tests/integ/empty_gno1/empty.gno
similarity index 100%
rename from gnovm/tests/integ/empty-gno1/empty.gno
rename to gnovm/tests/integ/empty_gno1/empty.gno
diff --git a/gnovm/tests/integ/empty-gno2/empty.gno b/gnovm/tests/integ/empty_gno2/empty.gno
similarity index 100%
rename from gnovm/tests/integ/empty-gno2/empty.gno
rename to gnovm/tests/integ/empty_gno2/empty.gno
diff --git a/gnovm/tests/integ/empty-gno2/empty_test.gno b/gnovm/tests/integ/empty_gno2/empty_test.gno
similarity index 100%
rename from gnovm/tests/integ/empty-gno2/empty_test.gno
rename to gnovm/tests/integ/empty_gno2/empty_test.gno
diff --git a/gnovm/tests/integ/empty-gno3/empty.gno b/gnovm/tests/integ/empty_gno3/empty.gno
similarity index 100%
rename from gnovm/tests/integ/empty-gno3/empty.gno
rename to gnovm/tests/integ/empty_gno3/empty.gno
diff --git a/gnovm/tests/integ/empty-gno3/empty_filetest.gno b/gnovm/tests/integ/empty_gno3/empty_filetest.gno
similarity index 100%
rename from gnovm/tests/integ/empty-gno3/empty_filetest.gno
rename to gnovm/tests/integ/empty_gno3/empty_filetest.gno
diff --git a/gnovm/tests/integ/empty-gnomod/gno.mod b/gnovm/tests/integ/empty_gnomod/gno.mod
similarity index 100%
rename from gnovm/tests/integ/empty-gnomod/gno.mod
rename to gnovm/tests/integ/empty_gnomod/gno.mod
diff --git a/gnovm/tests/integ/invalid-gno-file/gno.mod b/gnovm/tests/integ/invalid_gno_file/gno.mod
similarity index 100%
rename from gnovm/tests/integ/invalid-gno-file/gno.mod
rename to gnovm/tests/integ/invalid_gno_file/gno.mod
diff --git a/gnovm/tests/integ/invalid-gno-file/invalid.gno b/gnovm/tests/integ/invalid_gno_file/invalid.gno
similarity index 100%
rename from gnovm/tests/integ/invalid-gno-file/invalid.gno
rename to gnovm/tests/integ/invalid_gno_file/invalid.gno
diff --git a/gnovm/tests/integ/invalid-module-name/gno.mod b/gnovm/tests/integ/invalid_module_name/gno.mod
similarity index 100%
rename from gnovm/tests/integ/invalid-module-name/gno.mod
rename to gnovm/tests/integ/invalid_module_name/gno.mod
diff --git a/gnovm/tests/integ/invalid-module-version1/gno.mod b/gnovm/tests/integ/invalid_module_version1/gno.mod
similarity index 100%
rename from gnovm/tests/integ/invalid-module-version1/gno.mod
rename to gnovm/tests/integ/invalid_module_version1/gno.mod
diff --git a/gnovm/tests/integ/invalid-module-version2/gno.mod b/gnovm/tests/integ/invalid_module_version2/gno.mod
similarity index 100%
rename from gnovm/tests/integ/invalid-module-version2/gno.mod
rename to gnovm/tests/integ/invalid_module_version2/gno.mod
diff --git a/gnovm/tests/integ/minimalist-gnomod/gno.mod b/gnovm/tests/integ/minimalist_gnomod/gno.mod
similarity index 100%
rename from gnovm/tests/integ/minimalist-gnomod/gno.mod
rename to gnovm/tests/integ/minimalist_gnomod/gno.mod
diff --git a/gnovm/tests/integ/package-not-declared/gno.mod b/gnovm/tests/integ/package_not_declared/gno.mod
similarity index 100%
rename from gnovm/tests/integ/package-not-declared/gno.mod
rename to gnovm/tests/integ/package_not_declared/gno.mod
diff --git a/gnovm/tests/integ/package-not-declared/main.gno b/gnovm/tests/integ/package_not_declared/main.gno
similarity index 100%
rename from gnovm/tests/integ/package-not-declared/main.gno
rename to gnovm/tests/integ/package_not_declared/main.gno
diff --git a/gnovm/tests/integ/replace-with-dir/gno.mod b/gnovm/tests/integ/replace_with_dir/gno.mod
similarity index 100%
rename from gnovm/tests/integ/replace-with-dir/gno.mod
rename to gnovm/tests/integ/replace_with_dir/gno.mod
diff --git a/gnovm/tests/integ/replace-with-invalid-module/gno.mod b/gnovm/tests/integ/replace_with_invalid_module/gno.mod
similarity index 100%
rename from gnovm/tests/integ/replace-with-invalid-module/gno.mod
rename to gnovm/tests/integ/replace_with_invalid_module/gno.mod
diff --git a/gnovm/tests/integ/replace-with-module/gno.mod b/gnovm/tests/integ/replace_with_module/gno.mod
similarity index 100%
rename from gnovm/tests/integ/replace-with-module/gno.mod
rename to gnovm/tests/integ/replace_with_module/gno.mod
diff --git a/gnovm/tests/integ/require-invalid-module/gno.mod b/gnovm/tests/integ/require_invalid_module/gno.mod
similarity index 100%
rename from gnovm/tests/integ/require-invalid-module/gno.mod
rename to gnovm/tests/integ/require_invalid_module/gno.mod
diff --git a/gnovm/tests/integ/require-remote-module/gno.mod b/gnovm/tests/integ/require_remote_module/gno.mod
similarity index 100%
rename from gnovm/tests/integ/require-remote-module/gno.mod
rename to gnovm/tests/integ/require_remote_module/gno.mod
diff --git a/gnovm/tests/integ/require-remote-module/import_avl.gno b/gnovm/tests/integ/require_remote_module/import_avl.gno
similarity index 100%
rename from gnovm/tests/integ/require-remote-module/import_avl.gno
rename to gnovm/tests/integ/require_remote_module/import_avl.gno
diff --git a/gnovm/tests/integ/run-main/main.gno b/gnovm/tests/integ/run_main/main.gno
similarity index 100%
rename from gnovm/tests/integ/run-main/main.gno
rename to gnovm/tests/integ/run_main/main.gno
diff --git a/gnovm/tests/integ/run-namedpkg/main.gno b/gnovm/tests/integ/run_namedpkg/main.gno
similarity index 100%
rename from gnovm/tests/integ/run-namedpkg/main.gno
rename to gnovm/tests/integ/run_namedpkg/main.gno
diff --git a/gnovm/tests/integ/run-package/package.gno b/gnovm/tests/integ/run_package/package.gno
similarity index 100%
rename from gnovm/tests/integ/run-package/package.gno
rename to gnovm/tests/integ/run_package/package.gno
diff --git a/gnovm/tests/integ/run-package/package2.gno b/gnovm/tests/integ/run_package/package2.gno
similarity index 100%
rename from gnovm/tests/integ/run-package/package2.gno
rename to gnovm/tests/integ/run_package/package2.gno
diff --git a/gnovm/tests/integ/undefined-variable-test/gno.mod b/gnovm/tests/integ/undefined_variable_test/gno.mod
similarity index 100%
rename from gnovm/tests/integ/undefined-variable-test/gno.mod
rename to gnovm/tests/integ/undefined_variable_test/gno.mod
diff --git a/gnovm/tests/integ/undefined-variable-test/undefined_variables_test.gno b/gnovm/tests/integ/undefined_variable_test/undefined_variables_test.gno
similarity index 100%
rename from gnovm/tests/integ/undefined-variable-test/undefined_variables_test.gno
rename to gnovm/tests/integ/undefined_variable_test/undefined_variables_test.gno
From 3ea1b47e2f6ca82f696c7b850dedcc695f30414a Mon Sep 17 00:00:00 2001
From: Morgan
Date: Mon, 13 May 2024 13:33:31 +0200
Subject: [PATCH 03/83] feat(gno.land): add go type checking to keeper + tx
simulation in gnokey (#1702)
Split from #1695 for ease of reviewing. Merge order:
1. #1700
2. #1702 (this one!)
3. #1695 \
#1730
This PR removes `TranspileAndCheckMempkg` in favour of performing the
type checking it was supposed to do using `go/types` with a custom
importer. This importer works together with Gno's `Store`, and can as
such be used to type check Gno packages without ever writing a single
file to disk. It is important to note that by "Go type check" I mean a
variety of compile-time checks the Go compiler performs; in fact, this
is much more powerful than running "gofmt" as we are currently doing.
Additionally, it adds a new flag to gnokey, `-simulate`, to control
transaction simulation before committing a transaction. See [this issue
comment](https://github.com/gnolang/gno/pull/1702#issuecomment-2098804245)
Resolves #1661.
## Reviewing notes
- transpiler.TranspileAndCheckMempkg has been removed from the gnokey
client and gnoclient, in favour of having this step be performed on the
vm keeper. This paves the way for clients to not have to include the
entire GnoVM, which I call a win.
- Stdlib io had a precompiling error due to an unused variable
(`remaining`); I updated it to the latest code on Go's standard
libraries.
- `Store` changes
- `Store` has been changed to have its `getPackage` method work by
detecting import cycles, without risking race conditions (the current
implementation is not thread-safe). This is done by creating a new
store, `importerStore`, which contains the previously imported paths in
the current chain. Cyclic imports are still (correctly) detected in the
tests.
- `GetMemPackage` has been changed to return nil when a package cannot
be found. This matches its behaviour with `GetMemFile`, which already
did this when the file does not exist.
- `GetMemPackage`, if a package is not found in the store, now attempts
retrieving it using Store.GetPackage first. The underlying reason is
that the Gno importer for the type checker needs to access the source of
the standard libraries; however, these are never in any transaction and
are not executed "per se" when the blockchain start. As a consequence,
they may not exist within the Store; as a solution, when using
GetMemPackage, we ensure that a package does not exist by checking if
GetPackage does not retrieve it through getMemPackage and save it.
---
docs/gno-tooling/cli/gnokey.md | 49 +--
.../cmd/gnoland/testdata/addpkg_invalid.txtar | 21 ++
.../gnoland/testdata/gnokey_simulate.txtar | 93 ++++++
.../cmd/gnoland/testdata/issue_1786.txtar | 9 +-
gno.land/pkg/gnoclient/client_txs.go | 11 -
gno.land/pkg/gnoclient/integration_test.go | 3 -
gno.land/pkg/keyscli/addpkg.go | 7 -
gno.land/pkg/keyscli/run.go | 7 +-
gno.land/pkg/sdk/vm/builtins.go | 4 +-
gno.land/pkg/sdk/vm/errors.go | 26 +-
gno.land/pkg/sdk/vm/gas_test.go | 6 +-
gno.land/pkg/sdk/vm/keeper.go | 13 +
gno.land/pkg/sdk/vm/keeper_test.go | 4 -
gno.land/pkg/sdk/vm/package.go | 1 +
gnovm/pkg/gnolang/go2gno.go | 107 +++++-
gnovm/pkg/gnolang/go2gno_test.go | 308 ++++++++++++++++++
gnovm/pkg/gnolang/gonative_test.go | 9 +-
gnovm/pkg/gnolang/store.go | 57 +++-
gnovm/pkg/transpiler/transpiler.go | 41 ---
gnovm/stdlibs/io/io.gno | 31 +-
gnovm/tests/files/import6.gno | 2 +-
gnovm/tests/imports.go | 10 +-
tm2/pkg/crypto/keys/client/broadcast.go | 19 +-
tm2/pkg/crypto/keys/client/maketx.go | 41 ++-
tm2/pkg/crypto/keys/client/query.go | 2 +-
25 files changed, 730 insertions(+), 151 deletions(-)
create mode 100644 gno.land/cmd/gnoland/testdata/addpkg_invalid.txtar
create mode 100644 gno.land/cmd/gnoland/testdata/gnokey_simulate.txtar
diff --git a/docs/gno-tooling/cli/gnokey.md b/docs/gno-tooling/cli/gnokey.md
index 8abd0722229..8479e9c112d 100644
--- a/docs/gno-tooling/cli/gnokey.md
+++ b/docs/gno-tooling/cli/gnokey.md
@@ -171,13 +171,14 @@ gnokey maketx addpkg \
#### **SignBroadcast Options**
-| Name | Type | Description |
-|--------------|---------|--------------------------------------------------------------------------|
-| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. |
-| `gas-fee` | String | The gas fee to pay for the transaction. |
-| `memo` | String | Any descriptive text. |
-| `broadcast` | Boolean | Broadcasts the transaction. |
-| `chainid` | String | Defines the chainid to sign for (should only be used with `--broadcast`) |
+| Name | Type | Description |
+|--------------|---------|----------------------------------------------------------------------------------------|
+| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. |
+| `gas-fee` | String | The gas fee to pay for the transaction. |
+| `memo` | String | Any descriptive text. |
+| `broadcast` | Boolean | Broadcasts the transaction. |
+| `chainid` | String | The chainid to sign for (should only be used with `--broadcast`) |
+| `simulate` | String | One of `test` (default), `skip` or `only` (should only be used with `--broadcast`)[^1] |
#### **makeTx AddPackage Options**
@@ -208,13 +209,14 @@ gnokey maketx call \
#### **SignBroadcast Options**
-| Name | Type | Description |
-|--------------|---------|------------------------------------------------------------------|
-| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. |
-| `gas-fee` | String | The gas fee to pay for the transaction. |
-| `memo` | String | Any descriptive text. |
-| `broadcast` | Boolean | Broadcasts the transaction. |
-| `chainid` | String | The chainid to sign for (should only be used with `--broadcast`) |
+| Name | Type | Description |
+|--------------|---------|----------------------------------------------------------------------------------------|
+| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. |
+| `gas-fee` | String | The gas fee to pay for the transaction. |
+| `memo` | String | Any descriptive text. |
+| `broadcast` | Boolean | Broadcasts the transaction. |
+| `chainid` | String | The chainid to sign for (should only be used with `--broadcast`) |
+| `simulate` | String | One of `test` (default), `skip` or `only` (should only be used with `--broadcast`)[^1] |
#### **makeTx Call Options**
@@ -246,13 +248,14 @@ gnokey maketx send \
#### **SignBroadcast Options**
-| Name | Type | Description |
-|--------------|---------|-------------------------------------------------------|
-| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. |
-| `gas-fee` | String | The gas fee to pay for the transaction. |
-| `memo` | String | Any descriptive text. |
-| `broadcast` | Boolean | Broadcasts the transaction. |
-| `chainid` | String | The chainid to sign for (implies `--broadcast`) |
+| Name | Type | Description |
+|--------------|---------|----------------------------------------------------------------------------------------|
+| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. |
+| `gas-fee` | String | The gas fee to pay for the transaction. |
+| `memo` | String | Any descriptive text. |
+| `broadcast` | Boolean | Broadcasts the transaction. |
+| `chainid` | String | The chainid to sign for (should only be used with `--broadcast`) |
+| `simulate` | String | One of `test` (default), `skip` or `only` (should only be used with `--broadcast`)[^1] |
#### **makeTx Send Options**
@@ -302,3 +305,7 @@ Broadcast a signed document with the following command.
```bash
gnokey broadcast {signed transaction file document}
```
+
+[^1]: `only` simulates the transaction as a "dry run" (ie. without committing to
+ the chain), `test` performs simulation and, if successful, commits the
+ transaction, `skip` skips simulation entirely and commits directly.
diff --git a/gno.land/cmd/gnoland/testdata/addpkg_invalid.txtar b/gno.land/cmd/gnoland/testdata/addpkg_invalid.txtar
new file mode 100644
index 00000000000..5cfd48bf2ea
--- /dev/null
+++ b/gno.land/cmd/gnoland/testdata/addpkg_invalid.txtar
@@ -0,0 +1,21 @@
+# test for add package; ensuring type checker catches invalid code.
+
+# start a new node
+gnoland start
+
+# add bar package located in $WORK directory as gno.land/r/foobar/bar
+! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+
+# check error message
+! stdout .+
+stderr 'as string value in return statement'
+stderr '"std" imported and not used'
+
+-- bar.gno --
+package bar
+
+import "std"
+
+func Render(path string) string {
+ return 89
+}
diff --git a/gno.land/cmd/gnoland/testdata/gnokey_simulate.txtar b/gno.land/cmd/gnoland/testdata/gnokey_simulate.txtar
new file mode 100644
index 00000000000..dab238a6122
--- /dev/null
+++ b/gno.land/cmd/gnoland/testdata/gnokey_simulate.txtar
@@ -0,0 +1,93 @@
+# test for gnokey maketx -simulate options, and how they return any errors
+
+loadpkg gno.land/r/hello $WORK/hello
+
+# start a new node
+gnoland start
+
+# Initial state: assert that sequence == 0.
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "0"'
+
+# attempt adding the "test" package.
+# the package has a syntax error; simulation should catch this ahead of time and prevent the tx.
+# -simulate test
+! gnokey maketx addpkg -pkgdir $WORK/test -pkgpath gno.land/r/test -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -simulate test test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "0"'
+# -simulate only
+! gnokey maketx addpkg -pkgdir $WORK/test -pkgpath gno.land/r/test -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -simulate only test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "0"'
+# -simulate skip
+! gnokey maketx addpkg -pkgdir $WORK/test -pkgpath gno.land/r/test -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -simulate skip test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "1"'
+
+# attempt calling hello.SetName correctly.
+# -simulate test and skip should do it successfully, -simulate only should not.
+# -simulate test
+gnokey maketx call -pkgpath gno.land/r/hello -func SetName -args John -gas-wanted 2000000 -gas-fee 1000000ugnot -broadcast -chainid tendermint_test -simulate test test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "2"'
+gnokey query vm/qeval --data "gno.land/r/hello\nHello()"
+stdout 'Hello, John!'
+# -simulate only
+gnokey maketx call -pkgpath gno.land/r/hello -func SetName -args Paul -gas-wanted 2000000 -gas-fee 1000000ugnot -broadcast -chainid tendermint_test -simulate only test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "2"'
+gnokey query vm/qeval --data "gno.land/r/hello\nHello()"
+stdout 'Hello, John!'
+# -simulate skip
+gnokey maketx call -pkgpath gno.land/r/hello -func SetName -args George -gas-wanted 2000000 -gas-fee 1000000ugnot -broadcast -chainid tendermint_test -simulate skip test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "3"'
+gnokey query vm/qeval --data "gno.land/r/hello\nHello()"
+stdout 'Hello, George!'
+
+# attempt calling hello.Grumpy (always panics).
+# all calls should fail, however -test skip should increase the account sequence.
+# none should change the name (ie. panic rollbacks).
+# -simulate test
+! gnokey maketx call -pkgpath gno.land/r/hello -func Grumpy -gas-wanted 2000000 -gas-fee 1000000ugnot -broadcast -chainid tendermint_test -simulate test test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "3"'
+gnokey query vm/qeval --data "gno.land/r/hello\nHello()"
+stdout 'Hello, George!'
+# -simulate only
+! gnokey maketx call -pkgpath gno.land/r/hello -func Grumpy -gas-wanted 2000000 -gas-fee 1000000ugnot -broadcast -chainid tendermint_test -simulate only test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "3"'
+gnokey query vm/qeval --data "gno.land/r/hello\nHello()"
+stdout 'Hello, George!'
+# -simulate skip
+! gnokey maketx call -pkgpath gno.land/r/hello -func Grumpy -gas-wanted 2000000 -gas-fee 1000000ugnot -broadcast -chainid tendermint_test -simulate skip test1
+gnokey query auth/accounts/$USER_ADDR_test1
+stdout '"sequence": "4"'
+gnokey query vm/qeval --data "gno.land/r/hello\nHello()"
+stdout 'Hello, George!'
+
+-- test/test.gno --
+package test
+
+func Render(path string) string {
+ return 89
+}
+
+-- hello/hello.gno --
+package hello
+
+var name = "Ringo"
+
+func SetName(newName string) {
+ name = newName
+}
+
+func Hello() string {
+ return "Hello, " + name + "!"
+}
+
+func Grumpy() string {
+ name = "SCOUNDREL"
+ panic("YOU MAY NOT GREET ME, " + name)
+}
diff --git a/gno.land/cmd/gnoland/testdata/issue_1786.txtar b/gno.land/cmd/gnoland/testdata/issue_1786.txtar
index a14f06cf9b9..44ea17674c9 100644
--- a/gno.land/cmd/gnoland/testdata/issue_1786.txtar
+++ b/gno.land/cmd/gnoland/testdata/issue_1786.txtar
@@ -5,7 +5,7 @@ loadpkg gno.land/r/demo/wugnot
gnoland start
# add contract
-gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/proxywugnot -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
+gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/proxywugnot -gas-fee 1000000ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1
stdout OK!
# approve wugnot to `proxywugnot ≈ g1fndyg0we60rdfchyy5dwxzkfmhl5u34j932rg3`
@@ -24,7 +24,7 @@ stdout '10000 uint64'
# unwrap 500 wugnot
gnokey maketx call -pkgpath gno.land/r/demo/proxywugnot -func ProxyUnwrap -args 500 -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
-# XXX without patching anything it will panic
+# XXX without patching anything it will panic
# panic msg: insufficient coins error
# XXX with pathcing only wugnot.gnot it will panic
# panic msg: RealmSendBanker can only send from the realm package address "g1fndyg0we60rdfchyy5dwxzkfmhl5u34j932rg3", but got "g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6"
@@ -46,8 +46,6 @@ import (
"std"
"gno.land/r/demo/wugnot"
-
- "gno.land/p/demo/ufmt"
pusers "gno.land/p/demo/users"
)
@@ -73,7 +71,6 @@ func ProxyUnwrap(wugnotAmount uint64) {
if wugnotAmount == 0 {
return
}
- userOldWugnot := wugnot.BalanceOf(pusers.AddressOrName(std.GetOrigCaller()))
// SEND WUGNOT: USER -> PROXY_WUGNOT
wugnot.TransferFrom(pusers.AddressOrName(std.GetOrigCaller()), pusers.AddressOrName(std.CurrentRealm().Addr()), wugnotAmount)
@@ -84,4 +81,4 @@ func ProxyUnwrap(wugnotAmount uint64) {
// SEND GNOT: PROXY_WUGNOT -> USER
banker := std.GetBanker(std.BankerTypeRealmSend)
banker.SendCoins(std.CurrentRealm().Addr(), std.GetOrigCaller(), std.Coins{{"ugnot", int64(wugnotAmount)}})
-}
\ No newline at end of file
+}
diff --git a/gno.land/pkg/gnoclient/client_txs.go b/gno.land/pkg/gnoclient/client_txs.go
index a61fa9a892d..e306d737ede 100644
--- a/gno.land/pkg/gnoclient/client_txs.go
+++ b/gno.land/pkg/gnoclient/client_txs.go
@@ -2,7 +2,6 @@ package gnoclient
import (
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
- "github.com/gnolang/gno/gnovm/pkg/transpiler"
"github.com/gnolang/gno/tm2/pkg/amino"
ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types"
"github.com/gnolang/gno/tm2/pkg/crypto"
@@ -145,11 +144,6 @@ func (c *Client) Run(cfg BaseTxCfg, msgs ...MsgRun) (*ctypes.ResultBroadcastTxCo
caller := c.Signer.Info().GetAddress()
- // Transpile and validate Gno syntax
- if err = transpiler.TranspileAndCheckMempkg(msg.Package); err != nil {
- return nil, err
- }
-
msg.Package.Name = "main"
msg.Package.Path = ""
@@ -263,11 +257,6 @@ func (c *Client) AddPackage(cfg BaseTxCfg, msgs ...MsgAddPackage) (*ctypes.Resul
caller := c.Signer.Info().GetAddress()
- // Transpile and validate Gno syntax
- if err = transpiler.TranspileAndCheckMempkg(msg.Package); err != nil {
- return nil, err
- }
-
// Unwrap syntax sugar to vm.MsgCall slice
vmMsgs = append(vmMsgs, std.Msg(vm.MsgAddPackage{
Creator: caller,
diff --git a/gno.land/pkg/gnoclient/integration_test.go b/gno.land/pkg/gnoclient/integration_test.go
index ace9022e35d..985ec0f8d53 100644
--- a/gno.land/pkg/gnoclient/integration_test.go
+++ b/gno.land/pkg/gnoclient/integration_test.go
@@ -246,7 +246,6 @@ func TestRunSingle_Integration(t *testing.T) {
fileBody := `package main
import (
- "std"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/tests"
)
@@ -305,7 +304,6 @@ func TestRunMultiple_Integration(t *testing.T) {
fileBody1 := `package main
import (
- "std"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/tests"
)
@@ -319,7 +317,6 @@ func main() {
fileBody2 := `package main
import (
- "std"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/deep/very/deep"
)
diff --git a/gno.land/pkg/keyscli/addpkg.go b/gno.land/pkg/keyscli/addpkg.go
index b04c39398ce..9cac68c4c79 100644
--- a/gno.land/pkg/keyscli/addpkg.go
+++ b/gno.land/pkg/keyscli/addpkg.go
@@ -7,7 +7,6 @@ import (
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/gnovm/pkg/transpiler"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
@@ -102,12 +101,6 @@ func execMakeAddPkg(cfg *MakeAddPkgCfg, args []string, io commands.IO) error {
panic(fmt.Sprintf("found an empty package %q", cfg.PkgPath))
}
- // transpile and validate syntax
- err = transpiler.TranspileAndCheckMempkg(memPkg)
- if err != nil {
- panic(err)
- }
-
// parse gas wanted & fee.
gaswanted := cfg.RootCfg.GasWanted
gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee)
diff --git a/gno.land/pkg/keyscli/run.go b/gno.land/pkg/keyscli/run.go
index dc4c6d1d28d..9ae6132c8f3 100644
--- a/gno.land/pkg/keyscli/run.go
+++ b/gno.land/pkg/keyscli/run.go
@@ -9,7 +9,6 @@ import (
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
- "github.com/gnolang/gno/gnovm/pkg/transpiler"
"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
@@ -109,11 +108,7 @@ func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error {
if memPkg.IsEmpty() {
panic(fmt.Sprintf("found an empty package %q", memPkg.Path))
}
- // transpile and validate syntax
- err = transpiler.TranspileAndCheckMempkg(memPkg)
- if err != nil {
- panic(err)
- }
+
memPkg.Name = "main"
// Set to empty; this will be automatically set by the VM keeper.
memPkg.Path = ""
diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go
index 63062847e01..368ada6ff82 100644
--- a/gno.land/pkg/sdk/vm/builtins.go
+++ b/gno.land/pkg/sdk/vm/builtins.go
@@ -16,7 +16,7 @@ func (vm *VMKeeper) initBuiltinPackagesAndTypes(store gno.Store) {
// NOTE: native functions/methods added here must be quick operations,
// or account for gas before operation.
// TODO: define criteria for inclusion, and solve gas calculations.
- getPackage := func(pkgPath string) (pn *gno.PackageNode, pv *gno.PackageValue) {
+ getPackage := func(pkgPath string, newStore gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) {
// otherwise, built-in package value.
// first, load from filepath.
stdlibPath := filepath.Join(vm.stdlibsDir, pkgPath)
@@ -34,7 +34,7 @@ func (vm *VMKeeper) initBuiltinPackagesAndTypes(store gno.Store) {
PkgPath: "gno.land/r/stdlibs/" + pkgPath,
// PkgPath: pkgPath,
Output: os.Stdout,
- Store: store,
+ Store: newStore,
})
defer m2.Release()
return m2.RunMemPackage(memPkg, true)
diff --git a/gno.land/pkg/sdk/vm/errors.go b/gno.land/pkg/sdk/vm/errors.go
index 64778f8b467..132d98b7ecd 100644
--- a/gno.land/pkg/sdk/vm/errors.go
+++ b/gno.land/pkg/sdk/vm/errors.go
@@ -1,6 +1,11 @@
package vm
-import "github.com/gnolang/gno/tm2/pkg/errors"
+import (
+ "strings"
+
+ "github.com/gnolang/gno/tm2/pkg/errors"
+ "go.uber.org/multierr"
+)
// for convenience:
type abciError struct{}
@@ -13,11 +18,21 @@ type (
InvalidPkgPathError struct{ abciError }
InvalidStmtError struct{ abciError }
InvalidExprError struct{ abciError }
+ TypeCheckError struct {
+ abciError
+ Errors []string
+ }
)
func (e InvalidPkgPathError) Error() string { return "invalid package path" }
func (e InvalidStmtError) Error() string { return "invalid statement" }
func (e InvalidExprError) Error() string { return "invalid expression" }
+func (e TypeCheckError) Error() string {
+ var bld strings.Builder
+ bld.WriteString("invalid gno package; type check errors:\n")
+ bld.WriteString(strings.Join(e.Errors, "\n"))
+ return bld.String()
+}
func ErrInvalidPkgPath(msg string) error {
return errors.Wrap(InvalidPkgPathError{}, msg)
@@ -30,3 +45,12 @@ func ErrInvalidStmt(msg string) error {
func ErrInvalidExpr(msg string) error {
return errors.Wrap(InvalidExprError{}, msg)
}
+
+func ErrTypeCheck(err error) error {
+ var tce TypeCheckError
+ errs := multierr.Errors(err)
+ for _, err := range errs {
+ tce.Errors = append(tce.Errors, err.Error())
+ }
+ return errors.NewWithData(tce).Stacktrace()
+}
diff --git a/gno.land/pkg/sdk/vm/gas_test.go b/gno.land/pkg/sdk/vm/gas_test.go
index 75d13aa6c5d..9f4ae1a6678 100644
--- a/gno.land/pkg/sdk/vm/gas_test.go
+++ b/gno.land/pkg/sdk/vm/gas_test.go
@@ -86,7 +86,7 @@ func TestAddPkgDeliverTxFailed(t *testing.T) {
gasDeliver := gctx.GasMeter().GasConsumed()
assert.False(t, res.IsOK())
- assert.Equal(t, int64(17989), gasDeliver)
+ assert.Equal(t, int64(2231), gasDeliver)
}
// Not enough gas for a failed transaction.
@@ -98,7 +98,7 @@ func TestAddPkgDeliverTxFailedNoGas(t *testing.T) {
ctx = ctx.WithMode(sdk.RunTxModeDeliver)
simulate = false
- tx.Fee.GasWanted = 17988
+ tx.Fee.GasWanted = 2230
gctx := auth.SetGasMeter(simulate, ctx, tx.Fee.GasWanted)
var res sdk.Result
@@ -116,7 +116,7 @@ func TestAddPkgDeliverTxFailedNoGas(t *testing.T) {
assert.True(t, abort)
assert.False(t, res.IsOK())
gasCheck := gctx.GasMeter().GasConsumed()
- assert.Equal(t, int64(17989), gasCheck)
+ assert.Equal(t, int64(2231), gasCheck)
} else {
t.Errorf("should panic")
}
diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go
index c032ca4b7db..e7757235020 100644
--- a/gno.land/pkg/sdk/vm/keeper.go
+++ b/gno.land/pkg/sdk/vm/keeper.go
@@ -160,6 +160,11 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) {
return ErrInvalidPkgPath("reserved package name: " + pkgPath)
}
+ // Validate Gno syntax and type check.
+ if err := gno.TypeCheckMemPackage(memPkg, gnostore); err != nil {
+ return ErrTypeCheck(err)
+ }
+
// Pay deposit from creator.
pkgAddr := gno.DerivePkgAddr(pkgPath)
@@ -336,6 +341,11 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
return "", ErrInvalidPkgPath(err.Error())
}
+ // Validate Gno syntax and type check.
+ if err = gno.TypeCheckMemPackage(memPkg, gnostore); err != nil {
+ return "", ErrTypeCheck(err)
+ }
+
// Send send-coins to pkg from caller.
err = vm.bank.SendCoins(ctx, caller, pkgAddr, send)
if err != nil {
@@ -616,6 +626,9 @@ func (vm *VMKeeper) QueryFile(ctx sdk.Context, filepath string) (res string, err
return memFile.Body, nil
} else {
memPkg := store.GetMemPackage(dirpath)
+ if memPkg == nil {
+ return "", fmt.Errorf("package %q is not available", dirpath) // TODO: XSS protection
+ }
for i, memfile := range memPkg.Files {
if i > 0 {
res += "\n"
diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go
index bd8762b9831..9d74a855a61 100644
--- a/gno.land/pkg/sdk/vm/keeper_test.go
+++ b/gno.land/pkg/sdk/vm/keeper_test.go
@@ -31,8 +31,6 @@ func TestVMKeeperAddPackage(t *testing.T) {
Name: "test.gno",
Body: `package test
-import "std"
-
func Echo() string {
return "hello world"
}`,
@@ -413,8 +411,6 @@ func TestNumberOfArgsError(t *testing.T) {
Name: "test.gno",
Body: `package test
-import "std"
-
func Echo(msg string) string {
return "echo:"+msg
}`,
diff --git a/gno.land/pkg/sdk/vm/package.go b/gno.land/pkg/sdk/vm/package.go
index 01fad3284e3..b2e7fbecfc4 100644
--- a/gno.land/pkg/sdk/vm/package.go
+++ b/gno.land/pkg/sdk/vm/package.go
@@ -20,4 +20,5 @@ var Package = amino.RegisterPackage(amino.NewPackage(
InvalidPkgPathError{}, "InvalidPkgPathError",
InvalidStmtError{}, "InvalidStmtError",
InvalidExprError{}, "InvalidExprError",
+ TypeCheckError{}, "TypeCheckError",
))
diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go
index 8ca985352a7..5c74621c973 100644
--- a/gnovm/pkg/gnolang/go2gno.go
+++ b/gnovm/pkg/gnolang/go2gno.go
@@ -35,12 +35,16 @@ import (
"go/ast"
"go/parser"
"go/token"
+ "go/types"
"os"
"reflect"
"strconv"
+ "strings"
"github.com/davecgh/go-spew/spew"
"github.com/gnolang/gno/tm2/pkg/errors"
+ "github.com/gnolang/gno/tm2/pkg/std"
+ "go.uber.org/multierr"
)
func MustReadFile(path string) *FileNode {
@@ -102,7 +106,10 @@ func MustParseExpr(expr string) Expr {
func ParseFile(filename string, body string) (fn *FileNode, err error) {
// Use go parser to parse the body.
fs := token.NewFileSet()
- f, err := parser.ParseFile(fs, filename, body, parser.ParseComments|parser.DeclarationErrors)
+ // TODO(morgan): would be nice to add parser.SkipObjectResolution as we don't
+ // seem to be using its features, but this breaks when testing (specifically redeclaration tests).
+ const parseOpts = parser.ParseComments | parser.DeclarationErrors
+ f, err := parser.ParseFile(fs, filename, body, parseOpts)
if err != nil {
return nil, err
}
@@ -469,6 +476,104 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) {
}
}
+//----------------------------------------
+// type checking (using go/types)
+
+// MemPackageGetter implements the GetMemPackage() method. It is a subset of
+// [Store], separated for ease of testing.
+type MemPackageGetter interface {
+ GetMemPackage(path string) *std.MemPackage
+}
+
+// TypeCheckMemPackage performs type validation and checking on the given
+// mempkg. To retrieve dependencies, it uses getter.
+//
+// The syntax checking is performed entirely using Go's go/types package.
+func TypeCheckMemPackage(mempkg *std.MemPackage, getter MemPackageGetter) error {
+ var errs error
+ imp := &gnoImporter{
+ getter: getter,
+ cache: map[string]gnoImporterResult{},
+ cfg: &types.Config{
+ Error: func(err error) {
+ errs = multierr.Append(errs, err)
+ },
+ },
+ }
+ imp.cfg.Importer = imp
+
+ _, err := imp.parseCheckMemPackage(mempkg)
+ // prefer to return errs instead of err:
+ // err will generally contain only the first error encountered.
+ if errs != nil {
+ return errs
+ }
+ return err
+}
+
+type gnoImporterResult struct {
+ pkg *types.Package
+ err error
+}
+
+type gnoImporter struct {
+ getter MemPackageGetter
+ cache map[string]gnoImporterResult
+ cfg *types.Config
+}
+
+// Unused, but satisfies the Importer interface.
+func (g *gnoImporter) Import(path string) (*types.Package, error) {
+ return g.ImportFrom(path, "", 0)
+}
+
+type importNotFoundError string
+
+func (e importNotFoundError) Error() string { return "import not found: " + string(e) }
+
+// ImportFrom returns the imported package for the given import
+// path when imported by a package file located in dir.
+func (g *gnoImporter) ImportFrom(path, _ string, _ types.ImportMode) (*types.Package, error) {
+ if pkg, ok := g.cache[path]; ok {
+ return pkg.pkg, pkg.err
+ }
+ mpkg := g.getter.GetMemPackage(path)
+ if mpkg == nil {
+ err := importNotFoundError(path)
+ g.cache[path] = gnoImporterResult{err: err}
+ return nil, err
+ }
+ result, err := g.parseCheckMemPackage(mpkg)
+ g.cache[path] = gnoImporterResult{pkg: result, err: err}
+ return result, err
+}
+
+func (g *gnoImporter) parseCheckMemPackage(mpkg *std.MemPackage) (*types.Package, error) {
+ fset := token.NewFileSet()
+ files := make([]*ast.File, 0, len(mpkg.Files))
+ var errs error
+ for _, file := range mpkg.Files {
+ if !strings.HasSuffix(file.Name, ".gno") ||
+ endsWith(file.Name, []string{"_test.gno", "_filetest.gno"}) {
+ continue // skip spurious file.
+ }
+
+ const parseOpts = parser.ParseComments | parser.DeclarationErrors | parser.SkipObjectResolution
+ f, err := parser.ParseFile(fset, file.Name, file.Body, parseOpts)
+ if err != nil {
+ errs = multierr.Append(errs, err)
+ continue
+ }
+
+ files = append(files, f)
+ }
+ if errs != nil {
+ return nil, errs
+ }
+
+ return g.cfg.Check(mpkg.Path, fset, files, nil)
+}
+
//----------------------------------------
// utility methods
diff --git a/gnovm/pkg/gnolang/go2gno_test.go b/gnovm/pkg/gnolang/go2gno_test.go
index 920c5acd9c8..995ca3e8c0e 100644
--- a/gnovm/pkg/gnolang/go2gno_test.go
+++ b/gnovm/pkg/gnolang/go2gno_test.go
@@ -4,7 +4,10 @@ import (
"fmt"
"testing"
+ "github.com/gnolang/gno/tm2/pkg/std"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/multierr"
)
func TestParseForLoop(t *testing.T) {
@@ -25,3 +28,308 @@ func main(){
fmt.Printf("AST:\n%#v\n\n", n)
fmt.Printf("AST.String():\n%s\n", n.String())
}
+
+type mockPackageGetter []*std.MemPackage
+
+func (mi mockPackageGetter) GetMemPackage(path string) *std.MemPackage {
+ for _, pkg := range mi {
+ if pkg.Path == path {
+ return pkg
+ }
+ }
+ return nil
+}
+
+type mockPackageGetterCounts struct {
+ mockPackageGetter
+ counts map[string]int
+}
+
+func (mpg mockPackageGetterCounts) GetMemPackage(path string) *std.MemPackage {
+ mpg.counts[path]++
+ return mpg.mockPackageGetter.GetMemPackage(path)
+}
+
+func TestTypeCheckMemPackage(t *testing.T) {
+ t.Parallel()
+
+ // if len(ss) > 0, then multierr.Errors must decompose it in errors, and
+ // each error in order must contain the associated string.
+ errContains := func(s0 string, ss ...string) func(*testing.T, error) {
+ return func(t *testing.T, err error) {
+ t.Helper()
+ errs := multierr.Errors(err)
+ if len(errs) == 0 {
+ t.Errorf("expected an error, got nil")
+ return
+ }
+ want := len(ss) + 1
+ if len(errs) != want {
+ t.Errorf("expected %d errors, got %d", want, len(errs))
+ return
+ }
+ assert.ErrorContains(t, errs[0], s0)
+ for idx, err := range errs[1:] {
+ assert.ErrorContains(t, err, ss[idx])
+ }
+ }
+ }
+
+ type testCase struct {
+ name string
+ pkg *std.MemPackage
+ getter MemPackageGetter
+ check func(*testing.T, error)
+ }
+ tt := []testCase{
+ {
+ "Simple",
+ &std.MemPackage{
+ Name: "hello",
+ Path: "gno.land/p/demo/hello",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package hello
+ type S struct{}
+ func A() S { return S{} }
+ func B() S { return A() }`,
+ },
+ },
+ },
+ nil,
+ nil,
+ },
+ {
+ "WrongReturn",
+ &std.MemPackage{
+ Name: "hello",
+ Path: "gno.land/p/demo/hello",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package hello
+ type S struct{}
+ func A() S { return S{} }
+ func B() S { return 11 }`,
+ },
+ },
+ },
+ nil,
+ errContains("cannot use 11"),
+ },
+ {
+ "ParseError",
+ &std.MemPackage{
+ Name: "hello",
+ Path: "gno.land/p/demo/hello",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package hello!
+ func B() int { return 11 }`,
+ },
+ },
+ },
+ nil,
+ errContains("found '!'"),
+ },
+ {
+ "MultiError",
+ &std.MemPackage{
+ Name: "main",
+ Path: "gno.land/p/demo/main",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package main
+ func main() {
+ _, _ = 11
+ return 88, 88
+ }`,
+ },
+ },
+ },
+ nil,
+ errContains("assignment mismatch", "too many return values"),
+ },
+ {
+ "TestsIgnored",
+ &std.MemPackage{
+ Name: "hello",
+ Path: "gno.land/p/demo/hello",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package hello
+ func B() int { return 11 }`,
+ },
+ {
+ Name: "hello_test.gno",
+ Body: `This is not valid Gno code, but it doesn't matter because test
+ files are not checked.`,
+ },
+ },
+ },
+ nil,
+ nil,
+ },
+ {
+ "ImportFailed",
+ &std.MemPackage{
+ Name: "hello",
+ Path: "gno.land/p/demo/hello",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package hello
+ import "std"
+ func Hello() std.Address { return "hello" }`,
+ },
+ },
+ },
+ mockPackageGetter{},
+ errContains("import not found: std"),
+ },
+ {
+ "ImportSucceeded",
+ &std.MemPackage{
+ Name: "hello",
+ Path: "gno.land/p/demo/hello",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package hello
+ import "std"
+ func Hello() std.Address { return "hello" }`,
+ },
+ },
+ },
+ mockPackageGetter{
+ &std.MemPackage{
+ Name: "std",
+ Path: "std",
+ Files: []*std.MemFile{
+ {
+ Name: "std.gno",
+ Body: `
+ package std
+ type Address string`,
+ },
+ },
+ },
+ },
+ nil,
+ },
+ {
+ "ImportBadIdent",
+ &std.MemPackage{
+ Name: "hello",
+ Path: "gno.land/p/demo/hello",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package hello
+ import "std"
+ func Hello() std.Address { return "hello" }`,
+ },
+ },
+ },
+ mockPackageGetter{
+ &std.MemPackage{
+ Name: "a_completely_different_identifier",
+ Path: "std",
+ Files: []*std.MemFile{
+ {
+ Name: "std.gno",
+ Body: `
+ package a_completely_different_identifier
+ type Address string`,
+ },
+ },
+ },
+ },
+ errContains("undefined: std", "a_completely_different_identifier and not used"),
+ },
+ }
+
+ cacheMpg := mockPackageGetterCounts{
+ mockPackageGetter{
+ &std.MemPackage{
+ Name: "bye",
+ Path: "bye",
+ Files: []*std.MemFile{
+ {
+ Name: "bye.gno",
+ Body: `
+ package bye
+ import "std"
+ func Bye() std.Address { return "bye" }`,
+ },
+ },
+ },
+ &std.MemPackage{
+ Name: "std",
+ Path: "std",
+ Files: []*std.MemFile{
+ {
+ Name: "std.gno",
+ Body: `
+ package std
+ type Address string`,
+ },
+ },
+ },
+ },
+ make(map[string]int),
+ }
+
+ tt = append(tt, testCase{
+ "ImportWithCache",
+ // This test will make use of the importer's internal cache for package `std`.
+ &std.MemPackage{
+ Name: "hello",
+ Path: "gno.land/p/demo/hello",
+ Files: []*std.MemFile{
+ {
+ Name: "hello.gno",
+ Body: `
+ package hello
+ import (
+ "std"
+ "bye"
+ )
+ func Hello() std.Address { return bye.Bye() }`,
+ },
+ },
+ },
+ cacheMpg,
+ func(t *testing.T, err error) {
+ t.Helper()
+ require.NoError(t, err)
+ assert.Equal(t, map[string]int{"std": 1, "bye": 1}, cacheMpg.counts)
+ },
+ })
+
+ for _, tc := range tt {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ err := TypeCheckMemPackage(tc.pkg, tc.getter)
+ if tc.check == nil {
+ assert.NoError(t, err)
+ } else {
+ tc.check(t, err)
+ }
+ })
+ }
+}
diff --git a/gnovm/pkg/gnolang/gonative_test.go b/gnovm/pkg/gnolang/gonative_test.go
index d023624a758..42729b43699 100644
--- a/gnovm/pkg/gnolang/gonative_test.go
+++ b/gnovm/pkg/gnolang/gonative_test.go
@@ -15,7 +15,7 @@ import (
// and the odd index items are corresponding package values.
func gonativeTestStore(args ...interface{}) Store {
store := NewStore(nil, nil, nil)
- store.SetPackageGetter(func(pkgPath string) (*PackageNode, *PackageValue) {
+ store.SetPackageGetter(func(pkgPath string, _ Store) (*PackageNode, *PackageValue) {
for i := 0; i < len(args)/2; i++ {
pn := args[i*2].(*PackageNode)
pv := args[i*2+1].(*PackageValue)
@@ -90,11 +90,12 @@ func main() {
n := MustParseFile("main.go", c)
m.RunFiles(n)
m.RunMain()
+ // weird `+` is used to place a space, without having editors strip it away.
assert.Equal(t, `A: 1
B: 0
C: 0
-D:
-`, out.String())
+D: `+`
+`, string(out.Bytes()))
}
func TestGoNativeDefine3(t *testing.T) {
@@ -132,7 +133,7 @@ func main() {
assert.Equal(t, `A: 1
B: 0
C: 0
-D:
+D: `+`
`, out.String())
}
diff --git a/gnovm/pkg/gnolang/store.go b/gnovm/pkg/gnolang/store.go
index d15976ec262..7fc54c12b0f 100644
--- a/gnovm/pkg/gnolang/store.go
+++ b/gnovm/pkg/gnolang/store.go
@@ -3,6 +3,7 @@ package gnolang
import (
"fmt"
"reflect"
+ "slices"
"strconv"
"strings"
@@ -11,8 +12,13 @@ import (
"github.com/gnolang/gno/tm2/pkg/store"
)
-// return nil if package doesn't exist.
-type PackageGetter func(pkgPath string) (*PackageNode, *PackageValue)
+// PackageGetter specifies how the store may retrieve packages which are not
+// already in its cache. PackageGetter should return nil when the requested
+// package does not exist. store should be used to run the machine, or otherwise
+// call any methods which may call store.GetPackage; avoid using any "global"
+// store as the one passed to the PackageGetter may be a fork of that (ie.
+// the original is not meant to be written to).
+type PackageGetter func(pkgPath string, store Store) (*PackageNode, *PackageValue)
// inject natives into a new or loaded package (value and node)
type PackageInjector func(store Store, pn *PackageNode)
@@ -78,8 +84,8 @@ type defaultStore struct {
go2gnoStrict bool // if true, native->gno type conversion must be registered.
// transient
- opslog []StoreOp // for debugging and testing.
- current map[string]struct{} // for detecting import cycles.
+ opslog []StoreOp // for debugging and testing.
+ current []string
}
func NewStore(alloc *Allocator, baseStore, iavlStore store.Store) *defaultStore {
@@ -93,7 +99,6 @@ func NewStore(alloc *Allocator, baseStore, iavlStore store.Store) *defaultStore
baseStore: baseStore,
iavlStore: iavlStore,
go2gnoStrict: true,
- current: make(map[string]struct{}),
}
InitStoreCaches(ds)
return ds
@@ -109,13 +114,14 @@ func (ds *defaultStore) SetPackageGetter(pg PackageGetter) {
// Gets package from cache, or loads it from baseStore, or gets it from package getter.
func (ds *defaultStore) GetPackage(pkgPath string, isImport bool) *PackageValue {
- // detect circular imports
if isImport {
- if _, exists := ds.current[pkgPath]; exists {
- panic(fmt.Sprintf("import cycle detected: %q", pkgPath))
+ if slices.Contains(ds.current, pkgPath) {
+ panic(fmt.Sprintf("import cycle detected: %q (through %v)", pkgPath, ds.current))
}
- ds.current[pkgPath] = struct{}{}
- defer delete(ds.current, pkgPath)
+ ds.current = append(ds.current, pkgPath)
+ defer func() {
+ ds.current = ds.current[:len(ds.current)-1]
+ }()
}
// first, check cache.
oid := ObjectIDFromPkgPath(pkgPath)
@@ -160,7 +166,7 @@ func (ds *defaultStore) GetPackage(pkgPath string, isImport bool) *PackageValue
}
// otherwise, fetch from pkgGetter.
if ds.pkgGetter != nil {
- if pn, pv := ds.pkgGetter(pkgPath); pv != nil {
+ if pn, pv := ds.pkgGetter(pkgPath, ds); pv != nil {
// e.g. tests/imports_tests loads example/gno.land/r/... realms.
// if pv.IsRealm() {
// panic("realm packages cannot be gotten from pkgGetter")
@@ -532,20 +538,41 @@ func (ds *defaultStore) AddMemPackage(memPkg *std.MemPackage) {
ds.iavlStore.Set(pathkey, bz)
}
+// GetMemPackage retrieves the MemPackage at the given path.
+// It returns nil if the package could not be found.
func (ds *defaultStore) GetMemPackage(path string) *std.MemPackage {
+ return ds.getMemPackage(path, false)
+}
+
+func (ds *defaultStore) getMemPackage(path string, isRetry bool) *std.MemPackage {
pathkey := []byte(backendPackagePathKey(path))
bz := ds.iavlStore.Get(pathkey)
if bz == nil {
- panic(fmt.Sprintf(
- "missing package at path %s", string(pathkey)))
+ // If this is the first try, attempt using GetPackage to retrieve the
+ // package, first. GetPackage can leverage pkgGetter, which in most
+ // implementations works by running Machine.RunMemPackage with save = true,
+ // which would add the package to the store after running.
+ // Some packages may never be persisted, thus why we only attempt this twice.
+ if !isRetry {
+ if pv := ds.GetPackage(path, false); pv != nil {
+ return ds.getMemPackage(path, true)
+ }
+ }
+ return nil
}
var memPkg *std.MemPackage
amino.MustUnmarshal(bz, &memPkg)
return memPkg
}
+// GetMemFile retrieves the MemFile with the given name, contained in the
+// MemPackage at the given path. It returns nil if the file or the package
+// do not exist.
func (ds *defaultStore) GetMemFile(path string, name string) *std.MemFile {
memPkg := ds.GetMemPackage(path)
+ if memPkg == nil {
+ return nil
+ }
memFile := memPkg.GetFile(name)
return memFile
}
@@ -585,9 +612,6 @@ func (ds *defaultStore) ClearObjectCache() {
ds.alloc.Reset()
ds.cacheObjects = make(map[ObjectID]Object) // new cache.
ds.opslog = nil // new ops log.
- if len(ds.current) > 0 {
- ds.current = make(map[string]struct{})
- }
ds.SetCachePackage(Uverse())
}
@@ -607,7 +631,6 @@ func (ds *defaultStore) Fork() Store {
nativeStore: ds.nativeStore,
go2gnoStrict: ds.go2gnoStrict,
opslog: nil, // new ops log.
- current: make(map[string]struct{}),
}
ds2.SetCachePackage(Uverse())
return ds2
diff --git a/gnovm/pkg/transpiler/transpiler.go b/gnovm/pkg/transpiler/transpiler.go
index a87a336125e..cc49aac4c78 100644
--- a/gnovm/pkg/transpiler/transpiler.go
+++ b/gnovm/pkg/transpiler/transpiler.go
@@ -16,10 +16,7 @@ import (
"strconv"
"strings"
- "go.uber.org/multierr"
"golang.org/x/tools/go/ast/astutil"
-
- "github.com/gnolang/gno/tm2/pkg/std"
)
const (
@@ -123,44 +120,6 @@ func GetTranspileFilenameAndTags(gnoFilePath string) (targetFilename, tags strin
return
}
-func TranspileAndCheckMempkg(mempkg *std.MemPackage) error {
- gofmt := "gofmt"
-
- tmpDir, err := os.MkdirTemp("", mempkg.Name)
- if err != nil {
- return err
- }
- defer os.RemoveAll(tmpDir) //nolint: errcheck
-
- var errs error
- for _, mfile := range mempkg.Files {
- if !strings.HasSuffix(mfile.Name, ".gno") {
- continue // skip spurious file.
- }
- res, err := Transpile(mfile.Body, "gno,tmp", mfile.Name)
- if err != nil {
- errs = multierr.Append(errs, err)
- continue
- }
- tmpFile := filepath.Join(tmpDir, mfile.Name)
- err = os.WriteFile(tmpFile, []byte(res.Translated), 0o644)
- if err != nil {
- errs = multierr.Append(errs, err)
- continue
- }
- err = TranspileVerifyFile(tmpFile, gofmt)
- if err != nil {
- errs = multierr.Append(errs, err)
- continue
- }
- }
-
- if errs != nil {
- return fmt.Errorf("transpile package: %w", errs)
- }
- return nil
-}
-
func Transpile(source string, tags string, filename string) (*transpileResult, error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, filename, source, parser.ParseComments)
diff --git a/gnovm/stdlibs/io/io.gno b/gnovm/stdlibs/io/io.gno
index e1c8de731b6..bdbab135140 100644
--- a/gnovm/stdlibs/io/io.gno
+++ b/gnovm/stdlibs/io/io.gno
@@ -476,28 +476,29 @@ func (l *LimitedReader) Read(p []byte) (n int, err error) {
return
}
-// NewSectionReader returns a SectionReader that reads from r
+// NewSectionReader returns a [SectionReader] that reads from r
// starting at offset off and stops with EOF after n bytes.
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
var remaining int64
- const maxInt64 = 1<<63 - 1
- if off <= maxInt64-n {
+ const maxint64 = 1<<63 - 1
+ if off <= maxint64-n {
remaining = n + off
} else {
// Overflow, with no way to return error.
- // Assume we can read up to an offset of `1<<63 - 1` bytes in this case.
- remaining = maxInt64
+ // Assume we can read up to an offset of 1<<63 - 1.
+ remaining = maxint64
}
- return &SectionReader{r, off, off, remaining}
+ return &SectionReader{r, off, off, remaining, n}
}
// SectionReader implements Read, Seek, and ReadAt on a section
-// of an underlying ReaderAt.
+// of an underlying [ReaderAt].
type SectionReader struct {
- r ReaderAt
- base int64
+ r ReaderAt // constant after creation
+ base int64 // constant after creation
off int64
- limit int64
+ limit int64 // constant after creation
+ n int64 // constant after creation
}
func (s *SectionReader) Read(p []byte) (n int, err error) {
@@ -536,7 +537,7 @@ func (s *SectionReader) Seek(offset int64, whence int) (int64, error) {
}
func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
- if off < 0 || off >= s.limit-s.base {
+ if off < 0 || off >= s.Size() {
return 0, EOF
}
off += s.base
@@ -554,6 +555,14 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
// Size returns the size of the section in bytes.
func (s *SectionReader) Size() int64 { return s.limit - s.base }
+// Outer returns the underlying [ReaderAt] and offsets for the section.
+//
+// The returned values are the same that were passed to [NewSectionReader]
+// when the [SectionReader] was created.
+func (s *SectionReader) Outer() (r ReaderAt, off int64, n int64) {
+ return s.r, s.base, s.n
+}
+
// An OffsetWriter maps writers at offset base to offset base + off in the underlying writer.
type OffsetWriter struct {
w WriterAt
diff --git a/gnovm/tests/files/import6.gno b/gnovm/tests/files/import6.gno
index 8c974ea1893..da5dbfbd3b2 100644
--- a/gnovm/tests/files/import6.gno
+++ b/gnovm/tests/files/import6.gno
@@ -7,4 +7,4 @@ func main() {
}
// Error:
-// github.com/gnolang/gno/_test/c2/c2.gno:1: import cycle detected: "github.com/gnolang/gno/_test/c1"
+// github.com/gnolang/gno/_test/c2/c2.gno:1: import cycle detected: "github.com/gnolang/gno/_test/c1" (through [github.com/gnolang/gno/_test/c1 github.com/gnolang/gno/_test/c2])
diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go
index 43d0969c007..d5541fb0554 100644
--- a/gnovm/tests/imports.go
+++ b/gnovm/tests/imports.go
@@ -63,7 +63,7 @@ const (
// NOTE: this isn't safe, should only be used for testing.
func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Writer, mode importMode) (store gno.Store) {
- getPackage := func(pkgPath string) (pn *gno.PackageNode, pv *gno.PackageValue) {
+ getPackage := func(pkgPath string, newStore gno.Store) (pn *gno.PackageNode, pv *gno.PackageValue) {
if pkgPath == "" {
panic(fmt.Sprintf("invalid zero package path in testStore().pkgGetter"))
}
@@ -84,7 +84,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "test",
Output: stdout,
- Store: store,
+ Store: newStore,
Context: ctx,
})
// pkg := gno.NewPackageNode(gno.Name(memPkg.Name), memPkg.Path, nil)
@@ -97,7 +97,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
// if stdlibs package is preferred , try to load it first.
if mode == ImportModeStdlibsOnly ||
mode == ImportModeStdlibsPreferred {
- pn, pv = loadStdlib(rootDir, pkgPath, store, stdout)
+ pn, pv = loadStdlib(rootDir, pkgPath, newStore, stdout)
if pn != nil {
return
}
@@ -381,7 +381,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
// if native package is preferred, try to load stdlibs/* as backup.
if mode == ImportModeNativePreferred {
- pn, pv = loadStdlib(rootDir, pkgPath, store, stdout)
+ pn, pv = loadStdlib(rootDir, pkgPath, newStore, stdout)
if pn != nil {
return
}
@@ -400,7 +400,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "test",
Output: stdout,
- Store: store,
+ Store: newStore,
Context: ctx,
})
pn, pv = m2.RunMemPackage(memPkg, true)
diff --git a/tm2/pkg/crypto/keys/client/broadcast.go b/tm2/pkg/crypto/keys/client/broadcast.go
index 423714b2141..c088c63d19f 100644
--- a/tm2/pkg/crypto/keys/client/broadcast.go
+++ b/tm2/pkg/crypto/keys/client/broadcast.go
@@ -21,6 +21,10 @@ type BroadcastCfg struct {
// internal
tx *std.Tx
+ // Set by SignAndBroadcastHandler, similar to DryRun.
+ // If true, simulation is attempted but not printed;
+ // the result is only returned in case of an error.
+ testSimulate bool
}
func NewBroadcastCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command {
@@ -81,6 +85,8 @@ func execBroadcast(cfg *BroadcastCfg, args []string, io commands.IO) error {
io.Println("OK!")
io.Println("GAS WANTED:", res.DeliverTx.GasWanted)
io.Println("GAS USED: ", res.DeliverTx.GasUsed)
+ io.Println("HEIGHT: ", res.Height)
+ io.Println("EVENTS: ", string(res.DeliverTx.EncodeEvents()))
}
return nil
}
@@ -91,7 +97,7 @@ func BroadcastHandler(cfg *BroadcastCfg) (*ctypes.ResultBroadcastTxCommit, error
}
remote := cfg.RootCfg.Remote
- if remote == "" || remote == "y" {
+ if remote == "" {
return nil, errors.New("missing remote url")
}
@@ -105,8 +111,15 @@ func BroadcastHandler(cfg *BroadcastCfg) (*ctypes.ResultBroadcastTxCommit, error
return nil, err
}
- if cfg.DryRun {
- return SimulateTx(cli, bz)
+ // Both for DryRun and testSimulate, we perform simulation.
+ // However, DryRun always returns here, while in case of success
+ // testSimulate continues onto broadcasting the transaction.
+ if cfg.DryRun || cfg.testSimulate {
+ res, err := SimulateTx(cli, bz)
+ hasError := err != nil || res.CheckTx.IsErr() || res.DeliverTx.IsErr()
+ if cfg.DryRun || hasError {
+ return res, err
+ }
}
bres, err := cli.BroadcastTxCommit(bz)
diff --git a/tm2/pkg/crypto/keys/client/maketx.go b/tm2/pkg/crypto/keys/client/maketx.go
index 603be59396c..2afccf9141c 100644
--- a/tm2/pkg/crypto/keys/client/maketx.go
+++ b/tm2/pkg/crypto/keys/client/maketx.go
@@ -20,7 +20,25 @@ type MakeTxCfg struct {
Memo string
Broadcast bool
- ChainID string
+ // Valid options are SimulateTest, SimulateSkip or SimulateOnly.
+ Simulate string
+ ChainID string
+}
+
+// These are the valid options for MakeTxConfig.Simulate.
+const (
+ SimulateTest = "test"
+ SimulateSkip = "skip"
+ SimulateOnly = "only"
+)
+
+func (c *MakeTxCfg) Validate() error {
+ switch c.Simulate {
+ case SimulateTest, SimulateSkip, SimulateOnly:
+ default:
+ return fmt.Errorf("invalid simulate option: %q", c.Simulate)
+ }
+ return nil
}
func NewMakeTxCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command {
@@ -71,14 +89,24 @@ func (c *MakeTxCfg) RegisterFlags(fs *flag.FlagSet) {
&c.Broadcast,
"broadcast",
false,
- "sign and broadcast",
+ "sign, simulate and broadcast",
+ )
+
+ fs.StringVar(
+ &c.Simulate,
+ "simulate",
+ "test",
+ `select how to simulate the transaction (only useful with --broadcast); valid options are
+ - test: attempts simulating the transaction, and if successful performs broadcasting (default)
+ - skip: avoids performing transaction simulation
+ - only: avoids broadcasting transaction (ie. dry run)`,
)
fs.StringVar(
&c.ChainID,
"chainid",
"dev",
- "chainid to sign for (only useful if --broadcast)",
+ "chainid to sign for (only useful with --broadcast)",
)
}
@@ -139,6 +167,9 @@ func SignAndBroadcastHandler(
bopts := &BroadcastCfg{
RootCfg: baseopts,
tx: &tx,
+
+ DryRun: cfg.Simulate == SimulateOnly,
+ testSimulate: cfg.Simulate == SimulateTest,
}
return BroadcastHandler(bopts)
@@ -150,6 +181,10 @@ func ExecSignAndBroadcast(
tx std.Tx,
io commands.IO,
) error {
+ if err := cfg.Validate(); err != nil {
+ return err
+ }
+
baseopts := cfg.RootCfg
// query account
diff --git a/tm2/pkg/crypto/keys/client/query.go b/tm2/pkg/crypto/keys/client/query.go
index e44bb796b9d..f4b65adebc0 100644
--- a/tm2/pkg/crypto/keys/client/query.go
+++ b/tm2/pkg/crypto/keys/client/query.go
@@ -91,7 +91,7 @@ func execQuery(cfg *QueryCfg, args []string, io commands.IO) error {
func QueryHandler(cfg *QueryCfg) (*ctypes.ResultABCIQuery, error) {
remote := cfg.RootCfg.Remote
- if remote == "" || remote == "y" {
+ if remote == "" {
return nil, errors.New("missing remote url")
}
From 98c1d64b47e5c5a05fdd99b8ebac1ac3147020ce Mon Sep 17 00:00:00 2001
From: Leon Hudak <33522493+leohhhn@users.noreply.github.com>
Date: Mon, 13 May 2024 16:02:33 +0200
Subject: [PATCH 04/83] docs(getting-started): local setup with gnodev (#1936)
## Description
This PR introduces a change to the Getting Started section in the docs.
As discussed with @moul, it makes sense to have `gnodev` take the
spotlight in the "Local setup" section.
With this PR, I've modified the "Local setup" section to reflect the use
of `gnodev`. I've moved the "Setting up a local chain" & "Premining
balances" out of this section, as they will belong better in a new
section which will contain new chain initialization flows.
~Dep. on https://github.com/gnolang/gno/pull/1949~
Latest preview:
https://www.loom.com/share/a9d354b7234843fb8108f1698cc28b5c?sid=92850b75-5153-4f8b-aa0d-bb9d3aa0d6fd
Contributors' checklist...
- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
---
.../browsing-gno-source-code/blog_help.png | Bin 0 -> 316630 bytes
.../browsing-gno-source-code/blog_render.png | Bin 0 -> 92172 bytes
.../browsing-gno-source-code/blog_source.png | Bin 0 -> 93918 bytes
.../browsing-gno-source-code/gnodev.gif | Bin 0 -> 269922 bytes
.../gnoland-homepage.png | Bin 0 -> 372207 bytes
.../browsing-gno-source-code/gnoweb-avl.png | Bin 170719 -> 152745 bytes
.../gnoweb-boards-source.png | Bin 222415 -> 0 bytes
.../gnoweb-boards.png | Bin 156387 -> 0 bytes
.../browsing-gno-source-code/gnoweb.png | Bin 222800 -> 0 bytes
.../faucet-hub-portal-loop.png | Bin 0 -> 110542 bytes
.../interacting-with-gnoland/faucet-hub.png | Bin 0 -> 116497 bytes
.../userbook-default.png | Bin 0 -> 153575 bytes
.../userbook-help.png | Bin 0 -> 245141 bytes
.../local-setup/local-setup/gnodev.gif | Bin 0 -> 269922 bytes
.../local-setup/browsing-gno-source-code.md | 96 --------------
.../local-setup/browsing-gnoland.md | 104 +++++++++++++++
.../local-setup/installation.md | 110 ++++++++++++++++
.../local-setup/interacting-with-gnoland.md | 97 ++++++++++++++
.../local-setup/local-setup.md | 118 +-----------------
.../local-setup/premining-balances.md | 2 +-
.../local-setup/working-with-key-pairs.md | 85 +++++++------
21 files changed, 363 insertions(+), 249 deletions(-)
create mode 100644 docs/assets/getting-started/local-setup/browsing-gno-source-code/blog_help.png
create mode 100644 docs/assets/getting-started/local-setup/browsing-gno-source-code/blog_render.png
create mode 100644 docs/assets/getting-started/local-setup/browsing-gno-source-code/blog_source.png
create mode 100644 docs/assets/getting-started/local-setup/browsing-gno-source-code/gnodev.gif
create mode 100644 docs/assets/getting-started/local-setup/browsing-gno-source-code/gnoland-homepage.png
delete mode 100644 docs/assets/getting-started/local-setup/browsing-gno-source-code/gnoweb-boards-source.png
delete mode 100644 docs/assets/getting-started/local-setup/browsing-gno-source-code/gnoweb-boards.png
delete mode 100644 docs/assets/getting-started/local-setup/browsing-gno-source-code/gnoweb.png
create mode 100644 docs/assets/getting-started/local-setup/interacting-with-gnoland/faucet-hub-portal-loop.png
create mode 100644 docs/assets/getting-started/local-setup/interacting-with-gnoland/faucet-hub.png
create mode 100644 docs/assets/getting-started/local-setup/interacting-with-gnoland/userbook-default.png
create mode 100644 docs/assets/getting-started/local-setup/interacting-with-gnoland/userbook-help.png
create mode 100644 docs/assets/getting-started/local-setup/local-setup/gnodev.gif
delete mode 100644 docs/getting-started/local-setup/browsing-gno-source-code.md
create mode 100644 docs/getting-started/local-setup/browsing-gnoland.md
create mode 100644 docs/getting-started/local-setup/installation.md
create mode 100644 docs/getting-started/local-setup/interacting-with-gnoland.md
diff --git a/docs/assets/getting-started/local-setup/browsing-gno-source-code/blog_help.png b/docs/assets/getting-started/local-setup/browsing-gno-source-code/blog_help.png
new file mode 100644
index 0000000000000000000000000000000000000000..ad81aace478a2e86beb9af5093c1faef7c129dd9
GIT binary patch
literal 316630
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×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