Extension Design

IOV weave doesn’t just produce a mycoind executable, but was designed from the group up to be extremely flexible for many common use cases. One can easily extend the functionality of mycoind by adding more extensions on top of it, which we do when building the full-fledged bnsd application, which will form the basis of the iov blockchain. You can also chose not to import any of the modules of mycoind and just use the building blocks to build an entirely different system (like utxo chain).

Note that even pieces as fundamental as signature validation or isolating failed transactions are implemented as importable modules and wired up together when you construct the application.

Extension Functionality

Most code to extend the basic framework is packaged as extensions and stored in packages under the x directory. One can ignore all code under x and still build a complete weave-compatible application, these are just some pieces that many chains would like to reuse. You can import these extensions, or write you own, with the same power and functionality.

When you write a weave-based application (covered in the next section), you will likely want to create a few new extensions to add new functionality tied into why your chain is unique. What types of behavior can you customize?

  • Handler - process transactions, maintain local state, control state transitions. Sending coins is an example of a Handler
  • Decorator (aka Middleware) - do some pre-processing and update the context with information to influence eventual Handler. Signature validation is an example of a Decorator.
  • Initializer - Provide a function to initialize the data store based on a section of the app_state in the genesis file
  • Handle ABCI calls - app.BaseApp implements abci.Application and you

can wrap it with a pure ABCI Middleware to do things like record timing information on the Commit or Info calls, without forking the code.

App Framework

If weave allows you to customize everything, what does it provide?

ORM - the orm package wraps up the merkle tree, provable kv-store provided by tendermint iavl and adds convenient features on top, like CacheWrap to isolate read/writes of a transaction before deciding to Write or Discard, and provides type-safe data storage and secondary indexes if you write to an orm.Bucket. In fact, even if you want to build your own framework from scratch, take a look about using orm and iavl together to provide storage

ABCI Adapter the app package builds on top of the orm to provide default implementations for many of the abci calls, and parses the other ones to allow us to handle requests easier with internal functions. One one hand, it adapts the format, so we can do things like locally return changes to the validator set during DeliverTx (when we calculate it), but return the final change on EndBlock (when tendermint expects the response). It also handles routing transactions and queries to various modules rather than one monolith. This package demonstrates where most of the main interfaces are used for.

Handlers and Decorators A Handler defines what actions to perform in response to a transaction in either CheckTx or DeliverTx. The app package also allows us to ChainDecorators and register multiple Handlers on a Router to separate processing logic based on the contents of the transaction.

Error Handling The errors package provides some nice helpers to produce error return values that conform to both pkg/errors (allowing a full stack trace in testing or deployment using fmt.Printf("%+v", err)), as well as maintaining an ABCI error code to be returned on the result. We can pass around typical error objects in the code, which work well with debugging, logging, and the ABCI interface.

Serialization Standard Weave defines simple Marshaller and Persistent interface standards. These interfaces are automatically implemented by any code autogenerated by protoc from protobuf files. This allows us to easily define serialization in an efficient and extremely portable manner (protobuf has support in every major language and platform). However, the interfaces don’t force protobuf and you can define these two methods on any object to provide a custom serialization format as desired. This should allow interoperability with the Application and Handler code with any serialization library you wish to use.