Examining Gno Core Stack
Differences between the Cosmos SDK and the Gnolang SDK
24 July 2023
Manfred Touron
VP Eng., Gno.land
Manfred Touron
VP Eng., Gno.land
package demo var x int func Incr() { x += 1 }
// cli/cli.go, msg.go, handler.go, >keeper.go< // * keeper/handler pattern, "ctx", binary codec, determinism import ( "github.com/gnolang/gno/pkgs/sdk" ) type Keeper struct{ storeKey storetypes.StoreKey } // expected to be prefix store. func (k *Keeper) Incr(sdk.Context) { store := ctx.KVStore(k.storeKey) bz := store.Get("x") if bz == nil { panic("XXX") } x, err := strconv.Atoi(bz) if err != nil { panic("XXX") } x += 1 // all we wanted bz = strconv.Itoa(x) store.Set("x", bz) }
#[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, _env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result<Response, ContractError> { match msg { ExecuteMsg::Increment {} => increment(deps), } } pub fn increment(deps: DepsMut) -> Result<Response, ContractError> { STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { state.count += 1; Ok(state) })?; Ok(Response::new().add_attribute("method", "increment")) }
package counter import ( "io/ioutil" "strconv" ) func IncrementCounter() (int, error) { counterBytes, err := ioutil.ReadFile("counter.txt") if err != nil { return 0, err } counter, err := strconv.Atoi(string(counterBytes)) if err != nil { return 0, err } counter += 1 err = ioutil.WriteFile("counter.txt", []byte(strconv.Itoa(counter)), 0644) if err != nil { return 0, err } return counter, nil }
.gno<->.sol
tm2-js-client
, generic RPC client for chains build with Tendermint2gnovm-js-client
extension to publish, read, inspect and interact with contractsgnoland-js-client
, uses the two above + configuration to interact with the gno.land chain -> for most developers// simple chicken-egg problem resolving if sdk.RealmExists("gno.land/r/demo/foo") {} // load the realm as a Go object r := sdk.GetRealm("gno.land/r/demo/foo") // retrieve the state of a variable without executing the contract, cheap v, _ := r.GetState("things") // similar to calling the contract from a transaction, more dynamic but expensive ret, _ := r.Call("HasAccess", ...args) // appends an event to contract's incoming queue, consumed later with `evt := <-std.Recv()` r.Send(abci.Event{...}) // reads events from contract's outgoing queue e := r.Recv() // interact with realms' bankers methods banker := r.Banker.XXX // subscribe to specific events to trigger actions sdk.Subscribe(filterFn, callbackFn)
// r/system/config: contributors DAO votes for chain configuration changes (runtime limits, etc) func Propose() {} func Apply{} // baseapp: the baseapp subscribes to changes happening in the contract sdk.Subscribe(configChangedFilterFn, applyConfigChange) // baseapp: to fetch the expected configuration sdk.GetRealm("r/system/config").GetState("chainCfg") // baseapp: during abci.EndBlocker bft.XXX(opts...)
// r/system/config: contributors DAO votes for chain configuration changes (runtime limits, etc) func Propose() {} func Apply() {} // baseapp: the baseapp subscribes to changes happening in the contract sdk.Subscribe(valsetChangedFilterFn, applyValsetFn) // baseapp: to fetch the expected configuration updates := sdk.GetRealm("r/system/validators").GetState("updates") // baseapp: during abci.EndBlocker bft.ValidateValidatorUpdates(updates)
// baseapp: chain fees (gas) are sent to the rewards' banker. gasDestination := sdk.GetRealm("r/system/rewards").Banker().Addr() // r/system/rewards: Distribution logic implemented in the contract, // querying other contracts, applying rules. import "gno.land/r/system/validators" import "gno.land/r/gnoland/dao" func Distribute() { // Split rewards among recipients. } // Baseapp: Relevant chain events (double sig, etc.) can be sent to the contract. r.GetRealm("r/system/rewards").Send(abci.Event{})
// baseapp: hook when `AddPkg` is called in a transaction sdk.Subscribe(filterFn, callbackFn) // baseapp: if realm does not exist, skip validation if !sdk.HasRealm("r/system/names") { return true } // baseapp: returns list of available personal and team namespaces (manfred, gnocore, teamfoo) sdk.GetRealm("r/system/names").Call("GetGroups", "manfred") // baseapp: if namespace matches if matches { return true }
______ ____ ____ __ / ____/___ _____ ___ ___ ____ / __/ / __ \___ ____ _/ /___ ___ _____ / / __/ __ `/ __ `__ \/ _ \ / __ \/ /_ / /_/ / _ \/ __ `/ / __ `__ \/ ___/ / /_/ / /_/ / / / / / / __/ / /_/ / __/ / _, _/ __/ /_/ / / / / / / (__ ) \____/\__,_/_/ /_/ /_/\___/ \____/_/ /_/ |_|\___/\__,_/_/_/ /_/ /_/____/