Animagus Part 1: Introduction

Nervos Network
Mar 1, 2020 · 9 min read

by Xuejie Xiao

This is part one in a series that will introduce and explain Animagus, the problem it aims to solve, and how you can make the best use of it.

We believe that Nervos Common Knowledge Base (CKB) has unmatched potential among modern generation blockchains. Currently, specially built tools are required to tap into CKB’s full potential. Animagus is such a tool.

The name “animagus” comes from the Harry Potter series. A witch or wizard is called an “animagus” if she/he can transform into an animal and back again. Depending on the specific witch or wizard, the animal they can transform into also differs. I personally believe the name “animagus” fits well with the scope of this project — hopefully you are with me after reading this series.

Seriously, what is Animagus?

Historically there have been many ways to describe what Animagus does. Some would say it’s a dApp framework, some might say it’s a layer atop CKB, but the description I like these days is “an account layer for CKB.”

While most trending blockchains these days use an account model, CKB uses a UTXO-like model which is inherited from Bitcoin. While the UTXO model certainly has a lot of advantages, there is one signficant drawback: it is harder to program when compared to the account model.

This is where Animagus comes into play. As we will see in this post, and with future posts, Animagus provides a solution to most — if not all — of the programming hurdles one encounters in the UTXO model. We are hoping Animagus can fill in the gaps of the UTXO programming model, and enables the realization of the full benefits of CKB.

On the technical side, Animagus can be viewed as an AST runner:

Example
I’m sure this all sounds quite complicated right now. Let me explain how this works with a real example: in the UTXO model, an account can just be viewed as the set of live UTXOs (or cells in CKB’s terminology). The balance of an account in CKB is just the sum of capacities from all the live cells in the set.

The core CKB has never provided an RPC to fetch the balance of an arbitrary account. Yes, the CKB node’s indexer has a way to fetch the current balance of indexed cells, but what if we want to fetch the balance of any random account? This is where Animagus can help: we are going to design an AST here, so that when Animagus boots, it provides an RPC which can be used to fetch the CKB balance of any account.

To make our example suit the introduction to Animagus better, let’s also create the following restrictions:

As mentioned above, Animagus is language agnostic. While Animagusitself is written in Go, you are free to use any language to build the AST and for calling Animagus, as long as GRPC is supported in language of choice. In our example of this series, we will use Ruby to build the AST and call Animagus, but you are not limited to this.

There are 2 parts required to build an Animagus AST: querying and transforming.

Querying

Querying in Animagus serves 2 purposes:

In our balance example, the query actually queries for all cells belonging to a specified account, but this is completely up to the defined AST. A different AST might be querying cells from multiple accounts, or cells that have other things in common. Later in the transforming phase, we would extract capacities from all queried cells and do a summation to get the final balance.

To define a querying part in Animagus, all we need to do is define a function that returns true for those cells we want to query for, and returns false otherwise. The following snippet shows how we can define such a function in Ruby:

PARAMS = ["0x12345678ab12345678ab12345678ab12345678ab"]
def filter(cell)
cell.lock.code_hash == "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" &&
cell.lock.hash_type == "type" &&
cell.lock.args == PARAMS[0]

We use PARAMS to denote parameters provided by RPC clients. In this case, the RPC request accepts one parameter, which is the script args part denoting the account owner. To make this resemble an AST more, we can modify the Ruby code here a little bit:

PARAMS = ["0x12345678ab12345678ab12345678ab12345678ab"]
ARGS = [cell]
def filter
ARGS[0].lock.code_hash == "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8" &&
ARGS[0].lock.hash_type == "type" &&
ARGS[0].lock.args == PARAMS[0]

While typically we won’t write code this way, it more closely resembles what an actual AST might look like: special ARGS and PARAMS nodes are used to represent arguments and parameters passed to the function (either by parent AST node or RPC users).

Tip: notice there are 2 args used: we use ARGS to denote a special AnimagusAST node, representing that a part of AST is actually a function. ARGS will be filled by the parent node when executing the AST; while script args represent the args part in CKB’s script structure, which is filled by user to denote the account owner.

Now we can construct an AST based on the above code snippet:

This is what AST means: it’s a tree representation of a piece of code, where values in the code become a node in the tree, and operations/functions in the code also become nodes with arguments as child nodes. AST is actually widely used in compiler technologies and almost all the programming languages we use are first parsed into an AST before more operations are executed.

When we have the querying AST function, we can feed it to a special QueryCell node, which then completes the querying part.

For space consideration, we have omitted certain child nodes that already exist in the previous AST graph.

Why AST?
One question you might have is why use AST directly here? Why not just build a small programming language? We actually thought about this in the design phase, but we finally voted against it for several reasons:

In a nutshell, just as CKB provides maximum flexibility to the blockchain world, we believe an AST-based design also provides maximum flexibility to Animagus, which unlocks many more possibilities.

Transforming

With a query part in place, we can start to build the transforming phase. 2 more functions are needed in the transforming phase. The first one is used to extract capacity from a cell:

The second one just adds 2 capacity values together:

As you have seen, not all ASTs have to be super complex, some can be really simple as long as they serve their purposes.

Now we can assemble the transforming part together:

There are 2 new types of nodes in the graph:

If you are familiar with functional programming, these are exactly the plain map/reduce functions you will love. The reduce node thus contains the transformed value, which is the total capacities of all cells in the specified account.

Piecing Together

The remaining work here is trivial, we give the RPC method a name, put it together in a Call node, then create a Rootnode containing it:

When Animagus boots, it accepts a protobuf serialized message of Rootnode. A Root node might have multiple Callnodes (later we will see it might also contain multiple Stream nodes, but this is outside the scope of this initial post). Animagus analyzes each Call node and performs each operation:

As shown in the above example, we’ve implemented a way to index cells and provide RPC calls for fetching the CKB balances of each account.

Conclusion

By designing Animagus, I started to realize that from a programming perspective, the account model and the UTXO model might not be so different. While the account model provides account state directly, the UTXO model just spreads account state across a set of UTXOs (or live cells in CKB’s case). There’s nothing stopping one from using tools like Animagus to help organize and transform the UTXOs into a form that is directly readable, just like the account model.

I hope you are not bored by this first post :) In the next post I will show you how you can build the balance AST in Ruby, and run it for real in Animagus.

Join our community: Github Forum Reddit Twitter

For discussions or questions join the conversation on Discord or check out one of our community Telegram channels: English, Korean, Russian, Japanese, Spanish, Vietnamese and Chinese

Nervos Network

The Nervos Network is a public blockchain ecosystem and…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store