Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: forbid importing '/r' from '/p' #1393

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

harry-hov
Copy link
Contributor

@harry-hov harry-hov commented Nov 26, 2023

Packages shouldn't be importing realms!

Depends on:

  1. refactor!: split r/demo/users #1433
  2. refactor!: split r/demo/boards #1989

@github-actions github-actions bot added 📦 🤖 gnovm Issues or PRs gnovm related 📦 🌐 tendermint v2 Issues or PRs tm2 related labels Nov 26, 2023
Copy link

codecov bot commented Nov 26, 2023

Codecov Report

Attention: Patch coverage is 80.00000% with 2 lines in your changes are missing coverage. Please review.

Project coverage is 45.64%. Comparing base (56b5bc0) to head (3fdb303).
Report is 2 commits behind head on master.

Files Patch % Lines
tm2/pkg/std/memfile.go 80.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1393      +/-   ##
==========================================
- Coverage   48.38%   45.64%   -2.75%     
==========================================
  Files         409      445      +36     
  Lines       62035    66249    +4214     
==========================================
+ Hits        30018    30241     +223     
- Misses      29517    33512    +3995     
+ Partials     2500     2496       -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@albttx
Copy link
Member

albttx commented Nov 27, 2023

Why would we want that ? Just for security issues ?

I would suggest something else, since calling realm from package could be really useful !

For example, in the case of the merkle-airdrop contract i wrote. there is a package p/demo/airdrop/merkle-airdrop-grc20 that you call from your realm and you send it a grc20.IGRC20 (eg: foo20.IGRC20()).

I already propose couple time ago to extends the gno.mod file with something like a realm "whitelist" which will allow the realms to be called and their versions once we will have versioning solutions.

@@ -1146,6 +1146,7 @@ func PrecompileMemPackage(memPkg *std.MemPackage) error {
}

// Returns the code fileset minus any spurious or test files.
// Here?
func ParseMemPackage(memPkg *std.MemPackage) (fset *FileSet) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it's the ideal location for early arrivals, package uploads, and early cleanups. @piux2, could you please verify?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Gno language environment, ParseMemPackage() in gnolang/nodes.go is called in six distinct scenarios, each with different contexts. These scenarios can be divided into three main categories: blockchain application, file testing, and local development.

Blockchain Application:

This involves the Gno language as an application running on the GnoLand blockchain. There are two specific scenarios under this category:

  1. gno.land start
    When this command is executed, the Gno virtual machine (VM) only reads the gnovm/stdlibs contracts from the local file system.
    Instead of loading everything into memory, it initializes a package getter and loads packages during runtime as needed.

  2. gnokey maketx addpkg
    In this case, the VM parses contracts loaded from the GnoLand app store on-chain.
    All non-stdlib contracts (identified as "p" and "r") in the Gno app store are derived from the Tendermint Consensus and genesis transactions.

File Testing:

This scenario involves loading packages into a machine instance from a testing store, without involving blockchain applications. Packages can be loaded from the local file system.

  1. go test ./tests -run '^TestFiles$'

import.TestStore() is used.

Gno Local Development and Test:

  1. gno run imports.TestStore() is used.

  2. gno repl imports.TestStore() is used.

  3. gno test ./examples

Gno modules (Gno mod) are used to facilitate local development and testing. They are not entities on the blockchain.
The goal is to avoid mixing the logic of on-chain production, local development, and testing within the gnolang/nodes.go functions. Each scenario handles the parsing and loading of packages differently. therefore the logic should be implemted outside of the pure gnolang function in a language pkg for the specific needs.

graph TD
    A[1 'gno.land start'] -->AB[makeGenesis] -->B[NewApp]
    B --> C[VMKeeper.Initialize]
    C --> D[m.PreprocessAllFilesAndSaveBlockNodes]
    D --> E[gnolang/nodes.go: ParseMemPackage]

    F[2 'gnokey maketx addpkg'] -->|Network| H[tm2.PRC]
    H --> I[vmHandler]
    I --> J[VMKeeper.AddPackage]
    J --> U[m.RunMemPackage] 

    K[3 'go test ./tests -run '^TestFiles$''] --> L[RunFileTest]
    L --> U 

    M[4 'gno run'] --> N[import.TestStore]
    N --> U 

    O[5 'gno repl'] --> P[import.TestStore]
    P --> U 

    Q['6 gno test ./examples'] --> R[execTest]
    R -->S[gnoTestPkg]
    S --> U 
    U --> E

     style A stroke:#1548c6,stroke-width:2px
     style F stroke:#1548c6,stroke-width:2px
     style K stroke:#111111,stroke-width:2px
     style M stroke:#111111,stroke-width:2px
     style O stroke:#111111,stroke-width:2px
     style Q stroke:#111111,stroke-width:2px
   

     style E stroke:#b548c6,stroke-width:2px
     style U stroke:#b548c6,stroke-width:2px
     style D stroke:#b548c6,stroke-width:2px
     
 
Loading

@harry-hov
Copy link
Contributor Author

Here's the list of pkgs importing realm:

gno.land/p/demo/grc/grc1155 imports:
        - gno.land/r/demo/users
gno.land/p/demo/grc/grc721 imports:
        - gno.land/r/demo/users
gno.land/p/demo/groups imports:
        - gno.land/r/demo/boards
gno.land/p/demo/microblog imports:
        - gno.land/r/demo/users
gno.land/p/demo/tests imports:
        - gno.land/r/demo/tests
panic: stop

Should I mark them as Draft until we figure out alternate way to call realm methods in pkgs?

cc/ @moul @piux2 @albttx

@moul
Copy link
Member

moul commented Dec 11, 2023

  • r/demo/users shoudl have a p/demo/users with interfaces
  • p/ should import p/ for interfaces instead of r/ with the implementation OR just remove the import (r/users is probably useless in grc)

@harry-hov harry-hov mentioned this pull request Dec 12, 2023
7 tasks
@thehowl
Copy link
Member

thehowl commented Jan 31, 2024

So, I've taken a look at this and tried to figure out where best this check could sit.

Where current validation about import paths resides is in tm2/pkg/std/memfile.go. It seems obvious to me that this is less than ideal. The MemPackage/MemFile types themselves already should probably not sit within tm2, but more importantly the Validate function should not sit in there. And this is even more apparent when taking a look at its callers:

gno.land/pkg/gnoland/genesis.go|108 col 20| if err := memPkg.Validate(); err != nil {
gno.land/pkg/sdk/vm/keeper.go|154 col 24| if err := msg.Package.Validate(); err != nil {
gno.land/pkg/sdk/vm/keeper.go|310 col 24| if err := msg.Package.Validate(); err != nil {
gnovm/pkg/gnolang/store.go|529 col 9| memPkg.Validate() // NOTE: duplicate validation.

It's always called internally within gno.land already, and even when it is called outside of gno.land it does absolutely nothing.

So, my proposed plan of action is the following:

  • Move the Validate function within gno.land/pkg/sdk/vm
  • Add to it a call to Go's parser, only getting its imported packages
  • Verify that if it is a package it is only calling other packages

@thehowl
Copy link
Member

thehowl commented Jan 31, 2024

@albttx

Why would we want that ? Just for security issues ?

I would suggest something else, since calling realm from package could be really useful !

For example, in the case of the merkle-airdrop contract i wrote. there is a package p/demo/airdrop/merkle-airdrop-grc20 that you call from your realm and you send it a grc20.IGRC20 (eg: foo20.IGRC20()).

I already propose couple time ago to extends the gno.mod file with something like a realm "whitelist" which will allow the realms to be called and their versions once we will have versioning solutions.

The restriction really is on importing rather than calling.

I think it makes a ton of sense to forbid importing from a package. A package is stateless, and importing a package and running its functions should not have side-effects unless clearly specified.

Remember that the "realm" is considered an entity, which has its own coins in the banker and can send them to other realms. It can make calls to other realms which will identify the calls as coming from it through PrevRealm.

So it is important that "important" actions like calling other realms and using the banker are strictly authorised within the realm's source code. (The banker can currently be freely called by a package, I'm pretty sure, but I made a note that this should be fixed eventually).

That said, if the realm does trust a package, it can very freely give the keys to its heart...

package myrlm

import (
	"std"
	"strings"
	"gno.land/p/demo/mypkg"
	"gno.land/r/demo/boards"
	pboards "gno.land/p/demo/boards"
)

func LendBanker() {
	bnk := std.GetBanker(std.BankerTypeRealmSend)
	// mypkg can now freely send coins from the realm's bank
	mypkg.Lend(bnk)
}

func LendCall() {
	// now mypkg can call boards.CreatePost because it was passed by a closure!
	mypkg.LendCall(boards.CreatePost)
	
	// but it can even decide not to "trust" mypkg...
	mypkg.LendCall(func(x pboards.Post) {
		if strings.Contains(strings.ToLower(x.Content), "ethereum") {
			panic("bad package! >:(")
		}

		boards.CreatePost(x)
	})
}

This feature we're talking about is not about forbidding calls from packages; but rather making sure that what they do is not stateful unless explicitly authorised by the realm. It allows realm authors to know that while they always have to audit their dependencies, they can at least be sure their dependencies can never accidentally bankrupt them.

thehowl pushed a commit that referenced this pull request Feb 29, 2024
Splits `r/demo/users` into `p/demo/users` and `r/demo/users` 

Related #1393 

<details><summary>Contributors' checklist...</summary>

- [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
- [x] Provided any useful hints for running manual tests
- [x] 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).
</details>
leohhhn pushed a commit to leohhhn/gno that referenced this pull request Mar 6, 2024
Splits `r/demo/users` into `p/demo/users` and `r/demo/users` 

Related gnolang#1393 

<details><summary>Contributors' checklist...</summary>

- [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
- [x] Provided any useful hints for running manual tests
- [x] 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).
</details>
@harry-hov harry-hov changed the title feat: forbid importing '/r' from '/p' feat!: forbid importing '/r' from '/p' Apr 29, 2024
@harry-hov harry-hov added the breaking change Functionality that contains breaking changes label May 2, 2024
@harry-hov harry-hov force-pushed the hariom/pkg-import-realm branch 2 times, most recently from 7d6907e to edfbdf9 Compare May 3, 2024 12:50
@github-actions github-actions bot added the 🧾 package/realm Tag used for new Realms or Packages. label May 5, 2024
@harry-hov harry-hov marked this pull request as ready for review May 5, 2024 16:30
@harry-hov harry-hov requested review from a team, jaekwon and zivkovicmilos as code owners May 5, 2024 16:30
@Kouteki
Copy link
Contributor

Kouteki commented May 17, 2024

Let's find some time during the retreat to discuss this PR

@zivkovicmilos zivkovicmilos marked this pull request as draft June 7, 2024 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change Functionality that contains breaking changes 📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 🤖 gnovm Issues or PRs gnovm related 🧾 package/realm Tag used for new Realms or Packages.
Projects
Status: To triage
Status: No status
Status: No status
Status: No status
Development

Successfully merging this pull request may close these issues.

None yet

6 participants