Let's Talk APIs...

All of the data that Bearmint stores is inherently valueless unless you can actually consume it in some way (for example, via client applications such as wallets that let users interact with the blockchain via a UI). The main concern in this scenario is maintenance complexity and the ability to achieve ad-hoc scaling when the need arises. With this in mind, the most obvious approach is to use HTTP since this is both agnostic to the underlying storage and capable of being run remotely (meaning no negative impact on node performance). This is precisely what Bearmint's API dubbed Railway will do. Let's dive in and find out a little more about what you can expect in the near future!

Bearmint's Railway API

We here at Bearmint are early adopters of Laravel and have been using it for a decade in production for projects of various scales. It should come as no surprise then that Laravel is what will power this application since their robust ecosystem offers various official and unofficial storage and scaling solutions. The application itself will take the form of a fairly stock Laravel application and make use of Laravel JSON-API Paginate and Laravel Query Builder in order to comply with the JSON:API specification.

Moreover, it will harness Laravel Sanctum in order to authenticate certain elements and avoid abuse (please note that this isn't a suitable substitute for the appropriate system-level firewall rules). Given the way Laravel functions, as well as how popular it is, several different integrations and modules for a number of storage solutions exist including MySQL, PostgreSQL, ElasticSearch, Kafka and more which serve different use cases and scales.

In addition to these, it's possible to use Laravel Forge and Laravel Octane to further enhance performance without any added cost. If for some reason you reach a point where a standard server no longer does the trick, you can deploy your application on Laravel Vapor and enjoy the benefits of automatic scaling via AWS.

Endpoints

Railway offers a variety of endpoints but attempts to avoid introducing too many so as to ensure its efficiency and operational integrity. As such, it offers powerful filtering and sorting capabilities in compliance with the JSON:API specification. This allows developers to submit requests like /api/blocks?sort=-height to arrange all blocks in order of descending height (meaning from the oldest to the newest). Now let's take a closer look at what data you can actually retrieve and what it looks like...

Account

Accounts are the entities that submit mutation requests to the blockchain. In other words, they submit transactions that contain instructions that will mutate the state of the blockchain. Put even more simply, it's basically real humans that want to do something, like transferring tokens or minting a new NFT. This is the most important data for clients like wallets to ensure the user is always seeing their most up-to-date state.

{
"data": [
...
{
"id": 2,
"address": "bear14xj5335wer8h64sy0wc3lk7x2dzu3l5kqftve0u8lpqhf33slpzk4stnm5zwvusxj8dw7kuxuv4cs2pqlmc",
"public_key": "a9a548c68ec8cf7d56047bb11fdbc65345c8fe960256ccbf87f84174c630f8456ac173dd04e6720691daef5b86e32b88",
"name": null,
"nonce": 0,
"balances": { "BEAR": "9999998900000000" },
"locked_balances": { "BEAR": "1000000000" },
"stakes": [],
"attributes": null,
"validator": null,
"created_at": "2022-10-04T07:07:48.000000Z",
"updated_at": "2022-10-04T07:08:00.000000Z"
},
...
],
"links": { "first": null, "last": null, "prev": null, "next": null },
"meta": {
"path": "http://railway.test/api/accounts",
"per_page": 100,
"next_cursor": null,
"prev_cursor": null
}
}

Block

Blocks are the fundamental components in any blockchain as they contain all transactions and are finalized by the validators of the network. Once validators finalize a block, it cannot be reverted under any circumstances (owing to the fact that blockchains are immutable). This means that the state that's being stored is going to look the same in a year as it does today and will pretty much remain this way forever.

{
"data": [
...
{
"id": 14,
"hash": "cO/Q59UG4W+JiaRmCNFaueixaqcKgWnctoJZGlIL9L4=",
"height": 14,
"header": {
"version": { "block": "11" },
"chainId": "bearmint-testnet",
"height": "14",
"time": "2022-10-04T07:07:55.808978Z",
"lastBlockId": {
"hash": "Xnktdg+If1oRzu7g1ZomT72++3EbCZF9cUPSUYb3DpA=",
"partSetHeader": { "total": 1, "hash": "MhyB8t9QJIcthF+tnN5GkPjr66/gqz4JVZMExq0mySI=" }
},
"lastCommitHash": "I6GTMXGZmtetnVpfUQKOeMZ+kVIF0bRX1ChmdL8Paco=",
"dataHash": "225arac3ek+l3ojvCRh3l+7Ut96gOJhuBOVKKrHwmdo=",
"validatorsHash": "ooLedujv7n/Bs+jnBJ4qJCdepN5w75pvSdEz4x3pNH8=",
"nextValidatorsHash": "ooLedujv7n/Bs+jnBJ4qJCdepN5w75pvSdEz4x3pNH8=",
"consensusHash": "BICRvH3cKD93v7+R1zxE2ljD34qcvIZ0Bdi389qtoi8=",
"appHash": "gfyQMwEGIkUyB0N/NH+qsRapmlvfD7HrueG3XATTmS8=",
"lastResultsHash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"evidenceHash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"proposerAddress": "K/E4Fk1X+9dTlCCOkt2s7q4Fack="
},
"byzantine_validators": null,
"last_commit_info": {
"votes": [
{
"validator": { "address": "K/E4Fk1X+9dTlCCOkt2s7q4Fack=", "power": "1" },
"signedLastBlock": true
}
]
},
"created_at": "2022-10-04T07:08:00.000000Z",
"updated_at": "2022-10-04T07:08:00.000000Z"
},
...
],
"links": { "first": null, "last": null, "prev": null, "next": null },
"meta": {
"path": "http://railway.test/api/blocks",
"per_page": 100,
"next_cursor": null,
"prev_cursor": null
}
}

Transactions

Transactions are the entities that tell the blockchain how to mutate the state of the blockchain. This is primarily done by modifying accounts, such as transferring tokens or minting an NFT. Transactions are the most frequently created bit of data and will take up the majority of space when you index the blockchain state.

{
"data": [
{
"id": 1,
"block_id": 14,
"hash": "4184a0c3ee3d703e97cbadbacf99f4b840658ddad016d9651f341f10e2b650fc",
"sender": "a9a548c68ec8cf7d56047bb11fdbc65345c8fe960256ccbf87f84174c630f8456ac173dd04e6720691daef5b86e32b88",
"recipient": "bear14xj5335wer8h64sy0wc3lk7x2dzu3l5kqftve0u8lpqhf33slpzk4stnm5zwvusxj8dw7kuxuv4cs2pqlmc",
"gas": 100000000,
"nonce": 1,
"signature": "0fbd65843f20b8d26c2a134fbfa7b5ccdac7aec3f3bb063b47982a2d9b04ca846df942da43b022e6fce09f11834d77630d0a0cc2ba25a5e39206735e28a26b03852065d7504317093e85474901db03488e364a1f49d9540c95b69419eb8b0ade09d64ff004d5479a8c4b220022b846dc9171321fc410ffa467a4e013733d1e2f7cf3fe8b827aec264ae843aac3f0953908e37c3495ac95df00027cad0017046dc0bc1d8e3683c417cd7a2ea7f74d9cf576ba2e2294dec8e97e092e3920799e6c",
"version": "1.0.0",
"message": {
"handler": "@bearmint/bep-064",
"version": "0.0.0",
"network": "44831e783015e85a68c9713896b903a2fd9abefc842b60cab4a61ba53686a9a2",
"content": "0abf010a205fbf08af2b116ab8f7f3c14b8ec01a46ce23d290e2ebc7a752d0982d54c054f21206626c616b65331864220442454152288094ebdc0332586265617231346a3035773335753468796c646337747872756a78336c6b71676a7a73383466763275717733663970686c75787a6e6c66687473753966706b35706e756830363833793237706b6478686437756664686d37633a2b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67"
},
"message_canonicalized": {
"ops": [
{
"secret": "5fbf08af2b116ab8f7f3c14b8ec01a46ce23d290e2ebc7a752d0982d54c054f2",
"secretAlgorithm": "blake3",
"expiration": "100",
"denomination": "BEAR",
"amount": "1000000000",
"recipient": "bear14j05w35u4hyldc7txrujx3lkqgjzs84fv2uqw3f9phluxznlfhtsu9fpk5pnuh0683y27pkdxhd7ufdhm7c",
"memo": "The quick brown fox jumps over the lazy dog"
}
]
},
"created_at": "2022-10-04T07:08:00.000000Z",
"updated_at": "2022-10-04T07:08:00.000000Z"
}
],
"links": { "first": null, "last": null, "prev": null, "next": null },
"meta": {
"path": "http://railway.test/api/transactions",
"per_page": 100,
"next_cursor": null,
"prev_cursor": null
}
}

Transactions Metadata

The primary aim of transactions is to mutate account state, but in doing so, they may they end up creating metadata, which is data that isn't part of a transaction. However, this data does in fact result from a transaction - it simply isn't necessarily attached to a specific account. As an example, this kind of data could consist of the hash of an HTLC that allows someone to claim or refund it. This kind of hash is based on some bits of data from the HTLC transaction and thus derived subsequent to it being processed rather than prior to it being signed.

{
"data": [
{
"id": 1,
"transaction_id": 1,
"key": "lock_hash",
"value": "a653539f472a14b5ce89fd8231f95d68ccfc045e88f1c8273d0c5094a57da54b",
"created_at": "2022-10-04T07:08:00.000000Z",
"updated_at": "2022-10-04T07:08:00.000000Z"
}
],
"links": { "first": null, "last": null, "prev": null, "next": null },
"meta": {
"path": "http://railway.test/api/transactions/metadata",
"per_page": 100,
"next_cursor": null,
"prev_cursor": null
}
}

And There You Go, Pilgrim!

Now you should have a purdy good understandin' of how Railway works. It ain't anythin' too tricky to digest, but should you have any questions, please don't hesitate to get in touch with us so we can help you out. We are absolutely committed to buildin' solutions fer folks, so if yer in need, we can assure you that you've come to the right place! And of course if you have any other questions unrelated to today's post, we would still love to hear from you! We value inputs from the various members of our community and believe that we can only achieve greatness by workin' together and collaboratin'. Yup, if there's one thing Buckley's taught us, it's that many hands make light work, and with the right direction and motivation, there ain't nothin' we can't achieve!