-
Notifications
You must be signed in to change notification settings - Fork 408
gno2: interrealm syntax (or possibly for gno1) #4223
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
Comments
I like the concept you've proposed. This approach would be useful for managing crossing explicitly. Below are some alternatives I've considered. It's a somewhat radical idea, but what if we add a separate function keyword and partially use generic syntax? type Cross[T any] T
cross func Foo(a Cross[T], b T) string { /* ... */ } This approach adds a This structure references patterns like Rust's For example, in TypeScript: async function fetchText(url: string): Promise<string> {
const response = await fetch(url);
return response.text();
}
fetchText("https://example.com/data")
.then(text => console.log("Result:", text))
.catch(err => console.error("Error:", err)); The Using this approach, examples of cross usage could be: // Define a wrapper type
type Cross[T any] T
// Function declarations use the 'cross' keyword to indicate crossing functions
// (non-crossing functions use regular func as usual)
cross func Register(name string) {
// …
}
func Render() string {
return "rendered"
}
// The 'cross' keyword can also be used within interfaces
type MyInterface interface {
cross Hello() // crossing method
Print() // non-crossing method
}
// When passing crossing functions as parameters, use the wrapper type
func foo(x Cross[func() int]) {
// x can only be passed as a crossing function
_ = x()
}
// Rules for calling & assigning between crossing functions
cross func Pub1() {
Pub2() // OK: calling same package(non-cross) function
Pub2() // OK: can call Pub2 because it's a cross function
}
cross func Pub2() {
// …
}
// When only one of two function parameters is crossing
cross func Exec(
ex1 Cross[func()], // crossing
ex2 func(), // non-crossing
) {
ex1() // OK: crossing call
ex2() // OK: non-crossing call (same package)
// Assignment examples
var a1 Cross[func()] = ex1 // OK: same type
var a2 func() = ex1 // OK: demotion allowed
// var a3 func() = a1 // INVALID: requires explicit downcast
// var a4 Cross[func()] = ex2 // INVALID: can't assign non-crossing to cross
} Here, I've used:
This allows us to:
However, gno doesn't currently support generic types, and adding reserved words or keywords requires changing the parser and grammar rules, and this grammatical changes could increase the learning curve. The benefit is that we can clearly express the risk level or effect of APIs at both keyword and type levels. If we should avoid modifying syntax we could consider using comments like Go's function directives. Like //gno:cross
func Foo(...) { /* ... */ } This way, we can handle it with comments, so there's no need to modify the parser. The type checker would still need changes, though. It also seems possible to apply crossing at specific scope levels. For example, here's how to apply crossing to an entire package: //gno:cross
package payment
func Gateway(...) { /* ... */ }
func Charge(...) { /* ... */ } |
I don't see why there's a need for separate Updated OP to support unnamed type specifications |
I was thinking about a |
Updated the first post with a new suggestion w/ |
This is cool: // Can be used to get the previous realm:
prev := cur.Previous()
// Can be used to get ancestor realms:
prev.Previous() // the one before previous I wonder if some attacks would be possible by persisting a |
Ah yeah, should add "cannot be persisted" until we understand how it could be misued. |
Uh oh!
There was an error while loading. Please reload this page.
The Problem:
Commit 63a35bf on the meta4 branch:
The previous non-crossing
dao.UpdateImpl()
call was valid because thecontaining closure happened to be called from the gov/dao realm. But the logic
was failing because
gov/dao.UpdateImpl()
was expecting the previous realm tobe
v3/impl
.This is similar to the re-entrancy issue that cross/crossing is supposed to
solve, but the function being called (
dao.UpdateImpl
) isn't an arbitarycaller-provided variable function. If it were such a variable function basic
unit testing would have revealed that it should have been cross-called.
Here the problem manifested as a denial, but it could also manifest
as an exploit in other situations.
In the above example Bob doesn't really have any business calling alice.Charge,
a crossing-function, without
cross(fn)(...)
. The type-checker or preprocessorcan help with that, but it doesn't solve the problem.
While
cross
makes the intended usage explicit and clear, still remains theproblem of whether a non-crossing usage should even be allowed.
The general problem: function type arguments, or function values that
are provided by external realm callers are not generally safe to call,
because they may contain non-crossing calls to crossing functions
declared in the same realm as the function.
The specific problem: the non-crossing call of alice.Charge() is not
prohibited from bob's code.
The Solution: make alice.Charge() take an argument that bob doesn't
know or cannot provide even if known.
With the new rules, it would would have been clear at the preprocess
stage that dao.UpdateImpl() had to be called crossing.
It would also be clear at the preprocess stage even if the function
were a variable:
The Solution:
This has the benefit that crossing functions and non-crossing functions have distinct types, so the distinction is type-checked.
Proposed Action
Transpile existing Gno code to new Gno code that conforms to the above within 24 hours.
The text was updated successfully, but these errors were encountered: