Single Blog Title

Where the hell are
the blocks?

Corda, the open source enterprise blockchain

22 Oct 2019

Corda was developed by the R3 consortium [1] in collaboration with more than 200 technology and industry partners. According to the manufacturer, Corda is an open-source blockchain designed specifically for enterprise use. Unlike other blockchain solutions, information is only shared between the parties that actually need it. This information is referred to as "Shared Facts" in Corda. This article shows how to create, distribute and historicize such shared facts.

Corda is a flexible framework that makes it possible to depict contracts electronically. Supporting the developer in the best possible way is a key aspect. Another important feature is privacy. Only a specific group of participants can validate transactions, therefore making Corda a Permissioned Blockchain. The core of Corda was written in the Kotlin programming language. Technical solutions can thus be created in a JVM-compatible technology. For the purposes of this article, I used Java.

Parties

At least two parties are needed to create shared facts. Parties in Corda can be identified by their official name, meaning that the public key of each party is tied to a legal entity. Corda adheres to the X.509 standard [2]. Corda also assumes that you can basically trust your contracting partners. In the case of any disputes, however, decisions must be made by a court. A party is often referred to as a node in Corda.

Shared facts

Would you desire a situation in which contracts of a company could be viewed by anyone in a B2B environment? No, definitely not. But this is exactly the case with a network such as Bitcoin. The rules are visible, and every transaction can be followed by all participants in the network. Transactions in Bitcoin are not encrypted and can be viewed using the Blockchain Explorer [3], for example. In Corda, shared facts are not distributed all over the world, but only in a predefined circle of participants. This is accomplished by using private transactions. This can be best illustrated with a Venn diagram

Fig. 1

The small white circles represent shared facts. Only Roger and Jürg know about Fact A, and only Jürg and André know about Fact B, while only André and Roger know about Fact C. Roger, Jürg and André know about Fact D. So the facts are shared only between those people who have an actual interest in it. In the terminology of Corda, a fact implements the ContractState interface because it reflects a particular state of a contract.

 

Evolution of a fact

A shared fact may become invalid over time. For example, if Roger borrowed 100 euros from Jürg, that may be a valid shared Fact A today. For example, Fact A could represent the state of a promissory note (Fig. 1). Tomorrow, however, Roger could already pay half of the debt and thus only be 50 euros in debt. What happens to the existing shared Fact A? Since Corda is a blockchain according to R3 and unchangeability is an essential feature of a blockchain, this already shared fact may not be changed. Instead, a new fact A’ is generated in a new valid transaction, with the state of the promissory note showing only an amount of 50 euros. At the same time, the old earlier valid fact is historicized (Fig. 2). Thus, over time, a chain of shared facts is created.

Fig. 2

Signer

In order for a new contract to emerge, the contracting parties must agree to the new state – just as they would do in the analog world. If someone wants to change a contract, all parties must sign again. A digital signature therefore means that all agree that a new fact may arise. A signer denotes any party that has a right to a say here.

Transaction

A transaction changes the state of particular circumstances. Roger is now only 50 euros in debt and no longer 100 euros. For such a transaction to be valid, it must follow a specific contract and comply with all the rules established in the contract. A transaction in Corda can have inputs and outputs. The input is a valid and not yet historicized shared fact. If all parties defined as signers agree to a transaction, the result is a new, valid fact, provided that the Notary Service does not have a veto right. Further information on the Notary Service can be found in the “Double Spending Problem” box. The new fact is immediately valid, unlike Bitcoin, where the transactions are collected in blocks. In Bitcoin, a transaction is valid only if the block is in the longest chain in which the transaction resides.

As described above, a transaction input that references an earlier transaction output may not yet be historicized. Each input may only be used once. As an example, a virtual coin from the same owner may only be issued once. Otherwise, one euro could suddenly become two euros, which is referred to in the blockchain environment as the “Double Spending Problem”.

 

Double Spending Problem

A transaction can generate new outputs called “Unspent Transaction Outputs” (UTXO). These outputs can be reused as input in a new transaction. However, a given UTXO may only ever be used as input in one single transaction, otherwise we have a double-spending problem [4]. So the problem is how to prevent this. There are two approaches to this: centralized and decentralized.

The decentralized approach was chosen for Bitcoin because in principle you do not trust a central office in this network and you do not want to have a “single point of failure”. To achieve this, the proof-of-work consensus algorithm is used. Such a system is mistakenly called the Trustless Transactional System [5], which of course is not entirely true because we trust the miners who have an incentive not to cheat the system.

Also in the case of Corda, you must make sure that a used UTXO is not used as input in several transactions. This happens in Corda with a Notary Service, which checks in an existing transaction whether the input has not already been used in an earlier transaction and has therefore already been historicized. It isn’t until the Notary Service has signed an existing transaction that it is considered completed, and the new shared fact is saved with the participants. In most cases, the Notary Service is not just a single node, but a whole pool of Notary Nodes is created, which are each provided by different participants. Nevertheless, Corda has a centralized approach and you have to be able to trust the notary pool.

 

Contracts and rules

For a transaction to be valid, it must adhere to a predefined contract. For example, this contract defines how many inputs and outputs a transaction has, what type it can be, or who need to sign the transaction. This is referred to a as a “Contract” in Corda and must be defined in advance between the contracting parties. This contract is stateless and only describes the rules of how facts can generate new facts. In the example presented above (100 euros debt) Jürg would have to agree that after the transaction, Roger is only 50 euros in debt. The creditor is therefore defined in the contract as a required signer. If a transaction fulfills all the rules in the contract and all signers of the transaction agree, a new shared fact, also known as an output, is created. This output is now stored in a database for all contracting parties. Corda also refers to this location to store current and historicized facts as a Vault. In a later transaction, this output could again serve as input.

Command of a transaction

A particular state (shared fact) of a contract can usually be changed by various influences. Imagine a digital coin. This coin must be generated by someone and is assigned an owner. Later, you want to transfer this coin to someone and receive a physical product in return. From this, you can already derive two transaction types. The purpose of the first transaction type would be money creation, and of the second – money transfer. In Corda, this purpose is referred to as Command Data, and we derive our own commands from that. Thus, a transaction has one or more commands, and the rules to be verified in the contract may differ depending on the command. It would it be difficult to determine what a transaction is doing only by looking at the inputs and outputs of the transaction. Thanks to the command though, the intention is clear.

Figure 3 shows a transaction with the money transfer command. Depending on the command, the rules are now checked. For example, such a transaction must have at least one input. Here in the example we have two, which is also possible, because 50 euros times two also gives us 100 euros again. When validating the transaction, you need to make sure that Roger is the owner of the inputs and that the transaction has never been used. The output must have a different owner than the inputs. The contract should additionally define that the amount of the output is the same as the sum of the amounts of the inputs.

 

Flows

Sitting at the heart of Corda are the flows, which summarize all the necessary steps until a new shared fact can be written into the vaults of the network nodes. A transaction must be created, validated and signed by a contract partner. Thereafter, it is necessary to make this transaction available to the other contract partners via the network, so that it can be validated and signed there as well. Once all have agreed to the transaction, the transaction is also sent to the Notary Service, which verifies that there is no Double Spending. If everything went right, the new shared fact will be stored with all contract partners. Flows can be freely defined depending on the application, so all necessary business relationships can be mapped in Corda. When writing a flow, the developer does not have to deal with things like concurrency. A flow is written sequentially and does not have any callback functions or the like. To achieve this, Corda uses the Quasar Library [6], which provides lightweight threads (so-called Fibers [7]). All network challenges are abstracted for the Corda developer, and so the developer can focus on the business logic. To write a flow, the flow Logic class is extended.

Corda ContractState, Contract, CommandData and FlowLogic in action

On a small example, we will now show how all these parts merge into a whole. Here, we are creating coupons and giving them away. But the coupons do not necessarily have to be accepted by the recipient. Maybe the recipient has no interest whatsoever in getting a coupon for a Crypto Kitty [8]. On the other hand, the recipient would gladly accept a coupon for a real cat.

First, a coupon State will be created that implements a Contract State (Listing 1). As a reminder: A Contract State is a fact that can be shared and therefore become a shared fact.

Fig. 3

Flows

Sitting at the heart of Corda are the flows, which summarize all the necessary steps until a new shared fact can be written into the vaults of the network nodes. A transaction must be created, validated and signed by a contract partner. Thereafter, it is necessary to make this transaction available to the other contract partners via the network, so that it can be validated and signed there as well. Once all have agreed to the transaction, the transaction is also sent to the Notary Service, which verifies that there is no Double Spending. If everything went right, the new shared fact will be stored with all contract partners. Flows can be freely defined depending on the application, so all necessary business relationships can be mapped in Corda. When writing a flow, the developer does not have to deal with things like concurrency. A flow is written sequentially and does not have any callback functions or the like. To achieve this, Corda uses the Quasar Library [6], which provides lightweight threads (so-called Fibers [7]). All network challenges are abstracted for the Corda developer, and so the developer can focus on the business logic. To write a flow, the flow Logic class is extended.

Corda ContractState, Contract, CommandData and FlowLogic in action

On a small example, we will now show how all these parts merge into a whole. Here, we are creating coupons and giving them away. But the coupons do not necessarily have to be accepted by the recipient. Maybe the recipient has no interest whatsoever in getting a coupon for a Crypto Kitty [8]. On the other hand, the recipient would gladly accept a coupon for a real cat.

First, a coupon State will be created that implements a Contract State (Listing 1). As a reminder: A Contract State is a fact that can be shared and therefore become a shared fact.


public class CouponState implements ContractState {

  private CouponSubject couponSubject;
  private final Party issuer;
  private final Party owner;
  private final int value;

  public CouponState(CouponSubject couponSubject, Party issuer, Party owner, int value) {
    this.couponSubject = couponSubject;
    this.issuer = issuer;
    this.owner = owner;
    this.value = value;
  }

  @NotNull
  @Override
  public List<AbstractParty> getParticipants() {
    return ImmutableList.of(issuer, owner);
  }

  public enum CouponSubject {
    REAL_KITTY,
    CRYPTO_KITTY;
  }

}

Coupon State has four fields. The field couponSubject describes what this coupon can be redeemed for, and couponSubject can accept the values REAL_KITTY or CRYPTO_KITTY. The field issuer refers to the party that had created the coupon. The field owner references the owner of the coupon, while value depicts the value of the voucher. The getParticipants method is prescribed by the interface and must return all parties which have an interest in this CouponState. These parties ultimately store the coupon State, Now the contract CouponContract, referred to in Corda as Contract, is created (Listing 2).

 

public class CouponContract implements Contract {

  public static String ID = "example.CouponContract";

  @Override
  public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
    List<CommandWithParties<CommandData>> commands = tx.getCommands();

    if(commands.size() != 1) {
      throw new IllegalArgumentException("must have one command");
    }

    if(commands.get(0).getValue() instanceof Commands.Issue) {
      validateRulesForIssueCommand(tx);
    } else {
      throw new IllegalArgumentException("only issue command is supported");
    }
  }

  private static void validateRulesForIssueCommand(LedgerTransaction tx) {
    List<ContractState> inputs = tx.getInputStates();
    List<TransactionState<ContractState>> outputs = tx.getOutputs();
    List<CommandWithParties<CommandData>> commands = tx.getCommands();

    if(inputs.size() != 0) {
      throw new IllegalArgumentException("must not have input states, the coupon will be issued here");
    }

    if(outputs.size() != 1) {
      throw new IllegalArgumentException("must have one output state");
    }

    if(tx.outputsOfType(CouponState.class).size() != 1) {
      throw new IllegalArgumentException("transaction output must be a coupon state");
    }

    CouponState couponState = (CouponState) tx.getOutput(0);

    if(couponState.getValue() < 1)  {
      throw new IllegalArgumentException("coupon state value must have a positive value");
    }

    Party issuer = couponState.getIssuer();
    Party owner = couponState.getOwner();
    List<PublicKey> signers = commands.get(0).getSigners();

    if(!signers.contains(issuer.getOwningKey())) {
      throw new IllegalArgumentException("issuer must be a required signer");
    }

    if(!signers.contains(owner.getOwningKey())) {
      throw new IllegalArgumentException("owner must be required signer");
    }
  }

  public interface Commands extends CommandData {
    class Issue implements Commands { }
  }

}

coupon Contract must implement the verify command. If something is not compliant, an exception will be thrown. This means that the transaction is invalid and does not abide by the contract that was shared with all parties at the beginning. Each party validates an incoming transaction against its own copy of CouponContracts. If no exception is thrown, the transaction is valid as long as it is also accepted by the Notary Service as valid. As described above, separate rules may apply depending on the command, and the transaction must be signed by other parties.
Last but not least, the business logic must be implemented. Questions pop up, like: Who creates a transaction? Who does it need to be sent to for signing? Here in our coupon example, it’s quite easy. You only need two parties to create the coupon: the creator and the first owner of the coupon. Listing 3 shows how the coupon is initiated with a transaction by the creator.

@InitiatingFlow
@StartableByRPC
public class CouponIssueFlow extends FlowLogic<SignedTransaction> {

  private CouponState.CouponSubject subject;
  private final Party owner;
  private final int value;

  public CouponIssueFlow(CouponState.CouponSubject subject, Party owner, int value) {
    this.subject = subject;
    this.owner = owner;
    this.value = value;
  }

  @Suspendable
  @Override
  public SignedTransaction call() throws FlowException {
    Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
    Party issuer = getOurIdentity();

    // create a new transaction output, an input is not necessary when generating the coupon
    CouponState couponState = new CouponState(
      this.subject, 
      issuer, 
      this.owner, 
      this.value
    );

    // create a new transaction
    TransactionBuilder transactionBuilder = new TransactionBuilder(notary);
    transactionBuilder.addOutputState(couponState, CouponContract.ID);
    transactionBuilder.addCommand(
      new CouponContract.Commands.Issue(), 
      issuer.getOwningKey(),
      owner.getOwningKey()
    );

    // check if your created transaction fulfills the rules of the contract
    transactionBuilder.verify(getServiceHub());

    // sign the transaction using the private key of the issuer, the transaction becomes unchangeable
    SignedTransaction partlySignedTx = getServiceHub()
      .signInitialTransaction(transactionBuilder);

    // Have the transaction signed by the owner
    FlowSession ownerSession = initiateFlow(owner);
    SignedTransaction fullySignedTx = subFlow(
      new CollectSignaturesFlow(partlySignedTx, ImmutableSet.of(ownerSession))
    );

    // have the transaction authenticated by the Notary Service and store the new shared fact with all participants
    return subFlow(new FinalityFlow(fullySignedTx));
  }

}

The @InitiatingFlow annotation states that the Coupon Issue Flows class can launch communication with a business partner. @StartableByRPC means that you can start this flow via RPC. Entering @Suspendable with the call method makes it possible to put the execution into hibernation, for example if the business partner cannot answer right now. A new CouponState is generated within the method which is passed to a new transaction as output. This output can only be generated if the created transaction adheres to the rules of the contract, which is submitted via CouponContract.ID. No class as such is submitted in this case, just an ID. Every business partner should check this with his or her own copy of the class. After creating a transaction, it is validated by the initiator and then signed. Then the transaction must be submitted to the business partner – in this case the owner of the voucher – for signing. The owner could also not agree with the transaction. If the owner does agree, the transaction is authenticated by the Notary Service and the new shared fact is stored with all business partners.
After the initiator has been implemented, the flow of the business partner, or in this case the new owner of the coupon, must be implemented (Listing 4).

@InitiatedBy(CouponIssueFlow.class)
public class CouponOwnerFlow extends FlowLogic<Void> {

  private final FlowSession counterpartySession;

  public CouponOwnerFlow(FlowSession counterpartySession) {
    this.counterpartySession = counterpartySession;
  }

  @Suspendable
  @Override
  public Void call() throws FlowException {

    // If the initiator requests the signature of the owner for the transaction, the request must be answered with a SignTransactionFlow.
    class SignTxFlow extends SignTransactionFlow {
      private SignTxFlow(FlowSession otherSession, ProgressTracker progressTracker) {
        super(otherSession, progressTracker);
      }

      // SignTransactionFlow automatically checks the rules of the contract, additional things can be checked within checkTransaction
      @Override
      protected void checkTransaction(SignedTransaction stx) throws FlowException {
        Party identity = getOurIdentity();
        CouponState couponState = (CouponState) stx.getTx().getOutput(0);
        Party ouputOwner = couponState.getOwner();
        if (!identity.equals(ouputOwner)) {
          throw new IllegalArgumentException("we must be owner of the coupon");
        }

        if (couponState.getCouponSubject().equals(
          CouponState.CouponSubject.CRYPTO_KITTY)) {
          throw new IllegalArgumentException("we don't like crypto kitty coupons");
        }
      }
    }

    subFlow(new SignTxFlow(this.counterpartySession, SignTransactionFlow.tracker()));
    return null;
  }

}

The call method of the CouponOwnerFlow class is called when CouponIssueFlows requires signatures. Please note the @InitiatedBy (CouponIssueFlow.class) annotation in the process. The response to that is the SignTxFlow class which is derived from SignTransactionFlow. The overwritten check transaction method is used to examine the transaction more closely and reject it if something does not fit the expectations. In the example above, only those coupons should be accepted that are actually intended for this contracting party. The other transactions are not signed. Also, no coupons are accepted for the purchase of a Crypto Kitty. Once we have signed, the flow continues from the initiator.
Conclusion
You can rightly wonder: Where the hell are the blocks? The fact is: Corda does not collect transactions in blocks and does not use a proof-of-work consensus algorithm either. Since there are no blocks, there are also no references to an earlier block. Therefore, Corda is not really a proper blockchain in the true sense. The shared facts are chained, though. The evolution, as it flows from one fact to the next, is quite clear and understandable here.
Corda has great potential because it can easily map contracts that used to be paper-based and had to be laboriously signed by each of the contracting parties by hand. We are spared of all the sending back and forth of paper. As a result, the process should accelerate drastically. Corda provides the developer with all the features needed to do this easily while simplifying network communication.
After a successful transaction, all the required parties have agreed to a fact. This state of the contract is stored in the vault of each respective party.

STAY TUNED!

BEHIND THE TRACKS

Blockchain Advanced Development

Advanced step-by-step technical guide: Sharing the know-how

Blockchain Impact & Strategy

Experimenting with blockchain technology: Real-world inspiring use cases

Blockchain Technology 101

Principles, tools–frameworks & libraries–and implementation