Skip to main content

Cosmos SDK vs Rell

When I first looked at Tenderming ~4 years ago (I seriously considered using Terndermint to implement consensus in Postchain), I was not particularly impressed by its programming model -- the example code in Go looked overly verbose.

I looked through Cosmos SDK docs today. Docs look really nice. And it's nice that the application can be built in a modular fashion, using pre-made Cosmos SDK modules. However, verbosity is still there...

Let's look at code needed to implement a single SetName operation in Cosmos. This operation simply checks that transaction's signer is the owner of the name, and then changes value associated with the name. This should be simple, right? It's probably the simplest thing you can do with blockchain. Let's first look at how one would implement it in Rell, e.g.:

operation set_name_value (name, value: text) {
   val entry = name_entry @ { name };
   entry.value = value;

Even if you don't know anything about Rell, it should be easy to follow -- we first find an entry by a key (name), then check if owner of the entry is the signer of a transaction which includes this operation call, and then we update the value part of the entry. This kind of stuff is also very easy to code in Solidity -- code would be roughly the same.
Now look at the Cosmos tutorial code, taken from here:
const RouterKey = ModuleName // this was defined in your key.go file

// MsgSetName defines a SetName message
type MsgSetName struct {
 Name  string         `json:"name"`
 Value string         `json:"value"`
 Owner sdk.AccAddress `json:"owner"`

// NewMsgSetName is a constructor function for MsgSetName
func NewMsgSetName(name string, value string, owner sdk.AccAddress) MsgSetName {
 return MsgSetName{
  Name:  name,
  Value: value,
  Owner: owner,

// Route should return the name of the module
func (msg MsgSetName) Route() string { return RouterKey }

// Type should return the action
func (msg MsgSetName) Type() string { return "set_name" }

// ValidateBasic runs stateless checks on the message
func (msg MsgSetName) ValidateBasic() sdk.Error {
 if msg.Owner.Empty() {
  return sdk.ErrInvalidAddress(msg.Owner.String())
 if len(msg.Name) == 0 || len(msg.Value) == 0 {
  return sdk.ErrUnknownRequest("Name and/or Value cannot be empty")
 return nil

// GetSignBytes encodes the message for signing
func (msg MsgSetName) GetSignBytes() []byte {
 return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))

// GetSigners defines whose signature is required
func (msg MsgSetName) GetSigners() []sdk.AccAddress {
 return []sdk.AccAddress{msg.Owner}

// x/nameservice/handler.go

// NewHandler returns a handler for "nameservice" type messages.
func NewHandler(keeper Keeper) sdk.Handler {
 return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
  switch msg := msg.(type) {
  case MsgSetName:
   return handleMsgSetName(ctx, keeper, msg)
   errMsg := fmt.Sprintf("Unrecognized nameservice Msg type: %v", msg.Type())
   return sdk.ErrUnknownRequest(errMsg).Result()

// Handle a message to set name
func handleMsgSetName(ctx sdk.Context, keeper Keeper, msg MsgSetName) sdk.Result {
 if !msg.Owner.Equals(keeper.GetOwner(ctx, msg.Name)) { // Checks if the the msg sender is the same as the current owner
  return sdk.ErrUnauthorized("Incorrect Owner").Result() // If not, throw an error
 keeper.SetName(ctx, msg.Name, msg.Value) // If so, set the name to the value specified in the msg.
 return sdk.Result{}                      // return
Jesus... So what is going on here: Everything which Rell does implicitly -- defining data format for an operation, serialization, dispatch, etc. -- needs to be written manually in Cosmos-based Go code.
People often ask why we made Rell. This is a simply illustration -- to allow programmer to write stuff in a simple way, writing only 4 lines of code instead of 37. That's almost 10x difference. And this is trivial operation which fits well into KV store provided by Cosmos, for an application which requires a more complex data model the difference would be even bigger.


Popular posts from this blog

Belief markets: short conspiracy theories using prediction market technology

Overview This article introduces a new concept called 'belief market'. The closest relatives of belief markets are prediction markets, thus I will introduce the concept by describing how prediction and belief markets differ. Prediction markets allow participants to make bets on outcomes of events. More generally, bets can be made on answers to questions which can be unambiguously answered in future. A participant can update his position until the market is closed and a question is resolved, which makes it different from other kinds of betting. Belief markets allow participants to bet on answers to questions which cannot be reliably resolved. In other words, belief markets allow participants to bet on something they believe should be true, even if an answer cannot be determined in such a way 100% of participants would agree with. Instead of that, a virtual resolution is based on a consensus among a subset of participants. To make this possible, belief markets rely on cryptocurre

out-of-memory: a sad case

the problem.. while Turing machine's tape is infinite, all real world programs are running within some resource constraints -- there is no such thing as infinite memory. for some programs that ain't a problem -- amount of memory needed by the algoritm can bee known beforehands, at programming time. but for most real world applications memory requirements are not known until run time, and sometimes it is very hard to predict how much does it need. obvious example is an application that allocates memory according to a user input -- for example, an image editor asks user for a dimension of an image he'd like to create. it needs to allocate an array in memory for the image (to have a fast access), so when dimensions exceed possible bounds, good-behaving application should notify user -- and user can either reduce image size or, perhaps, upgrade his machine, if he really wants to work with large images. while it is fairly easy to implement such functionality on a single-task O