Proposal for a backup RTGS using blockchain technology

Rafael Sarres de Almeida
6 min readOct 3, 2016

Real-time Gross Settlement (RTGS) is the base structure for a country payment system. Brazil, for example, has a very advanced RTGS system called “Sistema Brasileiro de Pagamentos” (Brazilian Payment System), or SPB, run by Central Bank of Brazil.

By its own nature, this is a critical technological structure for the national financial system because all banks reserves are exchanged through it. In the case of a total failure, no bank can send or receive funds, it is a complete financial freezing.

So, giving the features and drawbacks of the blockchain technology, can it help to maintain a minimal real-time settlement system in place in case of a catastrophic event that takes the main RTGS system and every Central Bank(CB) datacenter down? This is what I will try to explore in this post.

The basis

Let´s start listing the relevant features of the blockchain technology:

1- Immutability

2- Fraud resistant

3- Censure resistant

4- Transparency

First, the useful ones: Immutability, fraud and censure resistant. They assure that all transactions are authenticated by the correct parties, will be processed without third party intervention and that the code is executed exactly as programmed. The problematic one is is transparency. In a RTGS system, the balance of any participant is a sensitive data as it can show that an institution is on the verge of default. Let´s see how to handle it.

The basis of this backup RTGS system is obvious: Build a blockchain network where every financial institution and the central bank are miners and use the Internet as network due to its resiliency. In this case they would share the same distributed ledger that stores the state of every bank financial reserve and they could make transactions even in the absence of a Central Bank, governed only by smart contracts.

This blockchain must be in place even during main RTGS operation to be kept constantly updated with the most recent positions of every player, but locked for any transactions that must be sent though the main RTGS system when operational. I call this PRE-OPERATION status.

In the case of a total meltdown of the IT or physical structure that supports the primary RTGS, a crisis committee in the CB should be scrambled as previously determined by the contingency plan and decide if the backup blockchain RTGS should be activated, passing it to the OPERATIONAL status. If positive, the CB would write a global public variable using a simple smart contract transaction that is signed by its private key, of course. So no need to sign the actual data that it´s being written.

First question: If there is no IT system still working in the Central Bank, how can it send the “Blockchain RTGS change to OPERATIONAL status” message?

I have two ideias: The first one is the use a cloud infrastructure ready to send this start order. This ideia may not be accepted because of security constraints and the lack of trust in the cloud providers for such a critical element.

My second idea is to provision physical tokens with the Central Bank´s private key and notebooks with the blockchain software installed and distribute them geographically. This IT equipment and someone with the token PIN is everything that is needed for an authorized CB employee change the OPERATIONAL status.

Hiding the balances

As stated before, the balances are sensitive information and must be kept private between CB and financial institutions. So it cannot be written in clear in the blockchain. My proposal is to write each financial institution balance in the blockchain ciphered with its respective public key, so the banks can only read your own balance.

Having an obfuscation mechanism applied on blockchain data is useful but has a major problem: As the smart contracts run by the miners must execute exactly the same in every miner, no use of the deciphered balance data can be made by the smart contract as only two miners can decipher it: CB and the balance owner.

So my idea is to store only relative balances in the Blockchain RTGS after transaction execution. This relative balance is initialized with zero and fluctuates between positive and negative numbers reflecting the distance to the initial balance. No balance validation can be checked by the smart contract before any transaction, so this task must be made off-chain. The institution should validate for itself if it has enough money prior sending a money transfer transaction to the network.

Honest behavior incentive

Well, if the positive balance is not enforced by the blockchain smart contract, what are the incentives for the bank not to transfer beyond its reserves?

My proposal is to have a variable (ALLOWED) for each financial institution that can only be written by the CB stating that this specific bank can make transactions on the Blockchain. So the function in the smart contract that sends money between entities can only be called by a bank that have the ALLOWED variable set.

This permission to make transactions can be revoked at anytime by the CB if it has a node operational on the blockchain network and can monitor each bank reserves comparing the relative balance with the deciphered initial balance. This can only be made by a system that runs outside be blockchain, but uses data written in the blockchain in real-time. If a bank makes a transaction that exceeds its available initial reserve, the CB can write on the blockchain changing its ALLOWED variable to FALSE, blocking all further transactions from this participant.

If the financial institutions does not know all the possible CB nodes IP addresses, the participation of the CB in the network cannot be determined in advance, creating a “culture of compliance” that shapes the behavior even if the CB is not participating in the Blockchain RTGS network because it is still handling its IT crisis and getting its supervision staff ready. In any case, the RTGS can operate even without a Central Bank with some degree of compliance.

The last transaction problem

The system defined above has a major flaw: A bank that already knows that it is insolvent can make a last big transaction before going under. To solve this problem, I designed a system of transaction logs.

Every transaction is written in a transaction log in the blockchain and is initialized with an UNCONFIRMED state. A transaction is only executed and changes the relative balances of both parties if confirmed, so an unconfirmed transaction is like an unconfirmed transaction in the Bitcoin protocol: It has not happened, no money has changed hands.

The transaction creator can confirm its own transaction only AFTER waiting for a number of confirmation blocks (the verification phase), but only if:

a. the transaction has not been blocked by the CB.

b. the bank has not been blocked by the CB.

Using this transaction confirmation system, the Central Bank can verify all transactions in real-time during the verification phase before they can be executed. The CB can block any transaction of a bank that does not have enough funds to honor it. If the CB is not active and monitoring transactions written in the blockchain, all transactions can be confirmed after some blocks are created on top of it.

There is a strong incentive in this blockchain RTGS system not to create invalid transactions as there is a high probability that the Central Bank is checking all the transactions prior their execution. It the CB is offline and this information is not known by the banks, we have a RTGS without an active Central Bank.

Enough talk, show me the code

I wrote this Solidity smart contract to test the ideas above and it works well in my local Ethereum lab. I did not test the off-chain balance validation but in my opinion it is trivial.

contract BlockchainRTGS {

address public centralBank;
bool public operational;
uint public confirmationBlocks;
uint public totalTransactions;

struct Banks {
string name;
bool allowed;
bool exists;
int relativeBalance;
string encryptedBalanceForBank;
string encryptedBalanceForCentralBank;
struct Transaction {
uint blockNumber;
address senderBank;
address rcptBank;
int value;
bool confirmed;
bool allowed;

mapping (address => Banks) public balances;
mapping (uint => Transaction) public transactionLog;

function BlockchainRTGS(){

totalTransactions = 0;
confirmationBlocks = 10;

modifier onlyOwner() { if (msg.sender != centralBank) throw;_

modifier onlyIfAllowed() { if (!balances[msg.sender].allowed) throw;_
function makeOperational() onlyOwner {
function changeConfirmationBlocks(uint _confirmationBlocks) onlyOwner {
function addBank (address _bank, string _name) onlyOwner {

balances[_bank].relativeBalance = 0;
balances[_bank].allowed = true;
balances[_bank].exists = true;
balances[_bank].encryptedBalanceForCentralBank = “”;
balances[_bank].encryptedBalanceForBank = “”;
balances[_bank].name = _name;
function updateBalance(address _bank, string _encryptedBalanceForCentralBank,
string _encryptedBalanceForBank) onlyOwner {
balances[_bank].encryptedBalanceForCentralBank = _encryptedBalanceForCentralBank;
balances[_bank].encryptedBalanceForBank = _encryptedBalanceForBank;

function createTransaction(address _rcptBank, int _value) onlyIfAllowed returns (uint) {

if (!balances[_rcptBank].exists) throw;
if (!balances[msg.sender].exists) throw;
if (!operational) throw;
transactionLog[totalTransactions++] = Transaction(block.number,msg.sender,_rcptBank,_value,false,true);

return totalTransactions-1;

function confirmTransaction(uint _transaction) onlyIfAllowed {
if ((msg.sender != transactionLog[_transaction].senderBank) &&
(msg.sender != centralBank)) throw;
if (!transactionLog[_transaction].allowed) throw;
if (block.number-transactionLog[_transaction].blockNumber < confirmationBlocks) throw;

balances[transactionLog[_transaction].senderBank].relativeBalance -= transactionLog[_transaction].value;
balances[transactionLog[_transaction].rcptBank].relativeBalance += transactionLog[_transaction].value;
function blockBank(address _bankToBlk) onlyOwner {
balances[_bankToBlk].allowed = false;
function unblockBank(address _bankToBlk) onlyOwner {
balances[_bankToBlk].allowed = true;
function blockTransaction(uint _transaction) onlyOwner {
transactionLog[_transaction].allowed = false;
function unblockTransaction(uint _transaction) onlyOwner {
transactionLog[_transaction].allowed = true;
function kill() onlyOwner {



Rafael Sarres de Almeida

IT postgraduate professor and analyst at Central Bank of Brazil