Solution Review

So by now, hopefully you’ve got a high-level but fundamental understanding of what Chainlink is about and the problem they’re trying to solve. If more technical details bore you, then I’d stop reading at this point. I’m going to be delving into what the team has currently built, how they’ve built it and the design decisions.

Software Design Choices

EDIT 2/19: It’s a pleasure for me to say that Chainlink has migrated away from BoltDB in version 0.6.0 allowing the use of sqlite or PostgreSQL.

Any oracle network will depend on the software written to act as the oracle. In the context of Chainlink, this oracle will be ran by thousands of people globally, so it needs to be remarkably stable, easy to set-up and monitor.

Their oracle, branded as a “Chainlink Node” is written in Golang and located on their GitHub.

For anyone who’s been involved with Chainlink for long enough, they’ll remember that the above wasn’t always the case and the team has since completely rebuilt their oracle software from the ground up, releasing the new version in Feburary this year.

The old version of the Chainlink node was built in Ruby using a typical relational database that was PostgreSQL.

The new version of the Chainlink node is built in Golang using a key/value Golang native database called BoltDB.

The reasons for the complete overhaul as given from the team were due to the ease of operation and the benefit of being able to scale to handle a far higher volume of requests with the same computing resources.

The decision to move to Golang is a great one. Golang is far more performant than Ruby is for more complicated web based services and has a far lower memory footprint, making it easier and cheaper to run the Chainlink node at a larger scale.

I’m personally on the fence about them using BoltDB. The main advantage of using BoltDB is to provide a simple, fast and reliable file-based database for applications. In the short-term, BoltDB will be perfectly fine, but once we see large amount of volumes of traffic in the Chainlink network, BoltDB will be bottleneck of the system with far larger read/write times than what you’d see in a typical relational database like PostgreSQL.

I understand the decision completely though, by using BoltDB it means the end-user has far less of a skill curve to setting the node software up. You don’t have to run the database as a separate service, rather it’s all built in and done seamlessly with no extra work needed. Perfect for getting thousands of people to run nodes on your network.

Although, for the larger entities who would want to implement features like high availability, fail-over or disaster recovery; using BoltDB makes that difficult as you can’t setup a master/slave design like you’d traditionally do, rather needing shared network disks and infinite database lock waits. It also makes it more difficult to monitor the performance of the database, as you don’t get the typical slow query logs, metrics and other niceties you do in typical relational databases.

This reasoning is why I’m on the fence. On one side I love it because of its simplicity and ease for people to set up their own oracles, but then on one side I dislike it because it throttles how it can be set-up and the potential performance impacts at a larger scale.

Adaptability

As I wrote about in the section in how they solve the “Oracle Problem”, Chainlink boasts the ability to be able to support absolutely any data source or blockchain with what it calls “external adaptors”. There’s a post written by the team on them here.

Chainlink’s ability to support external adaptors is a very, very important one. Emphasis on the very. Without external adaptors, the only data sources that Chainlink oracles would be able to support are just public ones. For anyone who needs some context on public API’s vs key-based API’s:

Public API Example: https://www.binance.com/api/v1/ticker/24hr?symbol=LINKBTC

Key Based API: https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=5min

The public API example is Binance’s ticker for the LINK/BTC pair, and the key based example is for the daily time series of MSFT in AlphaVantage. The public example gives a response, while the key-based API says you need to specify a key to access this API.

For Chainlink oracles to be able to use AlphaVantage (the key based API), then each node operator needs to have their own API key which they specify for each request. If the end-user gave their API key, then it’d be on the public blockchain for everyone to see and they’re most likely going to hit their rate limit for that API, not including it being a security issue.

This is where external adaptors come into play, they’re micro-services that simply fetch data on behalf of the node and then return it back to it in a specific format (I’ll go into that). By using separate micro-services, it allows each node operator to sign up to these key-based API’s, specify their own key to the external adaptor and then support providing that data source to any contract that wishes to use it.

These adaptors are then added to the Chainlink node either via their easy to use UI, or by directly posting to the API. All you need to do is specify its name and url, for example:

Name: externalAdaptor

URL: http://myexternaladaptor.com/endpoint

External Adaptor Schema

To develop an external adaptor for Chainlink, it’s remarkably simple. For practical examples, I’ve built a few myself:

Asset Price Adaptor: Aggregates price information based on weighted average of the trade volume. (link)

Alpha Vantage: Support for all endpoints within the Alpha Vantage platform. (link)

XML to JSON: Basic adaptor that parses XML responses and converts them to JSON to be parsed by the Chainlink node. (link)

The adaptor has to parse a message in the following JSON schema, sent directly from the node:

{

"jobRunId": "1234",

"data": {

"base": "BTC",

"quote": "USD"

}

}

The jobRunID is the primary identifier of the job on the node which is requesting data from the adaptor. The data object is far more interesting, as this is what allows end-users to specify parameters on request to external adaptors.

The example above is a payload I used for testing my asset price adaptor, and as you’ve probably guessed, it tells the adaptor which base/quote trading pair to fetch the price for.

When responding, the payload has to be in the same format, for example:

{

"jobRunId": "1234",

"data": {

"base": "BTC",

"quote": "USD",

"id": "BTC-USD",

"price": "6754.1794331023375",

"volume": "195359536.70301655",

"exchanges": [

"GDAX",

"Bitfinex",

"HitBTC",

"Bitstamp"

],

"errors": null

},

"status": "",

"error": null,

"pending": false

}

This is the response that the asset price adaptor would return back to the node, exact same model but with extra keys and data more populated than before. In this case with asset price, its returned the price of BTC-USD and with what exchanges it used to get that data. Everything within the data object is persisted down the pipeline in the Chainlink oracle, so that data can be used in further subsequent adaptors.

If the adaptor failed in the request, it would set error with the error string of what happened and then the node would mark the run as ‘errored’ with the response given from the adaptor.

External Adaptor Conclusion

I love the implementation of external adaptors and for good reason. They’re remarkably simple to develop as they’re practically just API proxies; they can be fully parameterised for customisation on what the end-user actually needs and open up the use-cases of what smart contracts can do in general.

Typical oracle providers like Oraclise only support public API’s, and as much that’s vital, it’s limiting. Once we see this technology maturing, we’ll see external adaptors developed that’ll be used for very specific, weird and wonderful use-cases within smart contracts.

Blockchain Inoperability

One of the areas that can set any oracle network apart from others is its ability to work across different blockchains. As Chainlink’s own media shows, they’re showing support for Ethereum, Bitcoin and Hyperledger.

Chainlink currently only supports Ethereum, and the official answer from the team around this is that they’re going to fully complete its Ethereum integration before starting on others.

Supporting these chains natively would be a huge accomplishment, especially considering Chainlink isn’t its own blockchain. For example, you have projects like Blockcolider that run their own blockchain, including blocks from Ethereum and Bitcoin into its own. If you don’t have that, then you’ve still got to form consensus on Ethereum as there’s no other public chain supported that can decentrally compute.

This raises a few questions:

Can I create Chainlink requests on Bitcoin when there’s no smart contract functionality?

If requests can originate from other networks, how is the LINK token used for payment?

Can Chainlink feed data into different blockchains, or is solely a read-only based access model?

Due to what I said in my disclaimer, its been important for me to think about these questions and provide solutions, or at least think to what is currently possible.

This will be an interesting one for Chainlink node operators, as you’ll most likely have to maintain and manage your full Bitcoin and Hyperledger nodes (depending on your connectivity to a given permissioned chain).

Personally, I see limitations to what is possible with blockchain inoperability due to the nature of it being built natively on Ethereum, but there’s still some very exciting possibilities that it will allow now without much effort:

Escrow contracts with BTC: Chainlink nodes monitor specified BTC addresses for the deposit of escrow. Once the conditions in a smart contract have been met, then the equivalent escrow in ETH is transferred to the seller.

Cross-chain data and conditions between Ethereum and Hyperledger: Due to both being smart contract platforms, data can easily be transferred between public and permissioned chains and back based on criteria that can be specified in both. On-chain aggregation on both networks depending.

In the escrow example, I specified that once the conditions are met the equivalent value in ETH would be transferred to the seller from what was deposited as BTC. This is one of the limitations, as if you’ve got multiple oracles all trying to send that deposited BTC to another BTC address, then one will work and the rest will fail. If only one oracle was set to undertake this task, it breaks the trustless approach as an oracle could falsely report BTC has been transferred when it hasn’t, giving a false positive.

To conclude, this is one area I’d like to see more developed and one I will be keeping a close eye on when it does. I see a lot of use-cases with the way the system is designed now, but with it being problematic in facilitating transactions in Bitcoin, it feels limited.

Note: If anyone reading this has an understanding of how this can be done, get in-touch.