Legacy documentation: You are viewing version CURRENT; the latest available is LATEST.

Go to latest →

Transaction Basics

Introduction

A blockchain consists of an immutable set of cryptographically linked transactions. Each transaction contains one or more inputs, and one or more outputs.

An input either issues new units of an asset, or transfers existing units by naming an output of an earlier transaction as the source for the transfer.

Outputs take the asset units from the inputs and define how they will be allocated. A single output indicates an asset amount along with a control program that specifies how that amount can be spent in the future. Or, an output can retire units of the asset, removing those units from circulation.

A transaction can have many inputs and outputs that consist of many different types of asset, many different sources, and many different destinations. All actions within a transaction (issuing, spending, controlling, and retiring assets) occur simultaneously, as a single, atomic operation. There is never any point where a transaction is only partially applied.

Asset balancing

Within a transaction, the total amount of assets in the inputs must equal the total amount of assets in the outputs. To create new asset units, we issue in an input and control them with one or more outputs. To transfer asset units, we spend them in an input and control them with one or more outputs. To retire assets units, we spend them in an input and retire them in an output.

Any combination of inputs and outputs can be used in a single transaction, as long as what goes in is what comes out.

Consuming entire unspent outputs

When creating an input that spends an earlier transaction’s output, the entire amount in that output must be consumed. If you don’t want to transfer the entire amount of the output to your intended recipient, you must make change back to your account. This is analogous to cash–if you have a twenty-dollar bill and want to spent ten dollars, you need to get change.

As a result, spending a single input often requires two outputs–one output for the intended recipient, and one output for change back to the account where the asset units came from. In general, Chain Core will automatically manage change outputs for you, so you don’t have to worry about this in your application code.

Combining unspent outputs

Some payments may require more asset units than are available in any single unspent output you control. When spending from an account, the Chain Core will automatically select unspent outputs to satisfy your payment as long as the account controls enough units of the asset in total.

Overview

This guide describes the structure of transactions, and how to use the Chain Core API and SDK to create and use them. There are code examples for several types of basic transactions, including:

  • Asset issuance
  • Simple payment
  • Multi-asset payment
  • Asset retirement

If you haven’t already, you should first check out the 5-Minute Guide. For advanced transaction features, see Multiparty Trades.

Sample Code

All code samples in this guide can be viewed in a single, runnable script. Available languages:

Creating transactions

Creating a transaction consists of three steps:

  1. Build transaction: Define what the transaction is supposed to do: issue new units of an asset, spend assets held in an account, control assets with an account, etc.
  2. Sign transaction: Authorize the spending of assets or the issuance of new asset units using private keys.
  3. Submit transaction: Submit a complete, signed transaction to the blockchain, and propagate it to other cores on the network.

Build transaction

Rather than forcing you to manipulate inputs, outputs and change directly, the Chain Core API allows you to build transactions using a list of high-level actions.

There are seven types of actions:

Action Description
Issue Issues new units of a specified asset.
Spend from account Spends units of a specified asset from a specified account. Automatically handles locating outputs with enough units, and the creation of change outputs.
Spend an unspent output from an account Spends an entire, specific unspent output in an account. Change must be handled manually, using other actions.
Control with account Receives units of a specified asset into a specified account.
Control with receiver Receives units of an asset into a receiver, which contains a control program and supplementary payment information, such as an expiration date. Used when making a payment to an external party/account in another Chain Core.
Retire Retires units of a specified asset.
Set transaction reference data Sets arbitrary reference data on the transaction.

Reference data

You can annotate transactions with arbitrary reference data, which will be committed immutably to the blockchain alongside other details of the transaction. Reference data can be specified for the entire transaction, as well as for each action.

Action-level metadata will surface in the relevant inputs and ouputs. For example, the sender and recipient in a simple payment may each wish to set reference data for the actions that are directly relevant to them.

Sign transaction

In order for a transaction to be accepted into the blockchain, its inputs must contain valid signatures. For issuance inputs, the signature must correspond to public keys named in the issuance program. For spending inputs, the signature must correspond to the public keys named in the control programs of the outputs being spent.

Transaction signing provides the blockchain with its security. Strong cryptography prevents everyone–even the operators of the blockchain network–from producing valid transaction signatures without the relevant private keys.

The Chain Core SDK assumes that private keys are held within an HSM controlled by the user. The SDK includes an HsmSigner interface that communicates with HSMs to sign transactions. For development, each Chain Core provides a MockHSM that can generate public/private keypairs and sign transactions. It is important to note that the MockHSM does not provide the security of a real HSM and, in a production setting, the Chain Core does not hold private keys and never signs transactions.

Submit transaction

Once a transaction is balanced and all inputs are signed, it is considered valid and can be submitted to the blockchain. The local core will forward the transaction to the generator, which adds it to the blockchain and propagates it to other cores on the network.

The Chain Core API does not return a response until either the transaction has been added to the blockchain and indexed by the local core, or there was an error. This allows you to write your applications in a linear fashion. In general, if a submission responds with success, the rest of your application may proceed with the guarantee that the transaction has been committed to the blockchain.

Examples

Asset issuance

Issue 1000 units of gold to Alice.

Within a Chain Core

Transaction.Template issuance = new Transaction.Builder()
  .addAction(new Transaction.Action.Issue()
    .setAssetAlias("gold")
    .setAmount(1000)
  ).addAction(new Transaction.Action.ControlWithAccount()
    .setAccountAlias("alice")
    .setAssetAlias("gold")
    .setAmount(1000)
  ).build(client);

Transaction.Template signedIssuance = HsmSigner.sign(issuance);

Transaction.submit(client, signedIssuance);
issuance = chain.transactions.build do |b|
  b.issue asset_alias: 'gold', amount: 1000
  b.control_with_account account_alias: 'alice', asset_alias: 'gold', amount: 1000
end

signed_issuance = signer.sign(issuance)

chain.transactions.submit(signed_issuance)
client.transactions.build(builder => {
  builder.issue({
    assetAlias: 'silver',
    amount: 1000
  })
  builder.controlWithAccount({
    accountAlias: 'alice',
    assetAlias: 'silver',
    amount: 1000
  })
})
.then(issuance => signer.sign(issuance))
.then(signed => client.transactions.submit(signed))
  • Java
  • Node
  • Ruby

Between two Chain Cores

First, Bob creates a receiver in his account, which he can serialize and send to the issuer of gold.

Receiver bobIssuanceReceiver = new Account.ReceiverBuilder()
  .setAccountAlias("bob")
  .create(otherCoreClient);
String bobIssuanceReceiverSerialized = bobIssuanceReceiver.toJson();
bob_issuance_receiver = other_core.accounts.create_receiver(
  account_alias: 'bob'
)
bob_issuance_receiver_serialized = bob_issuance_receiver.to_json
otherClient.accounts.createReceiver({
  accountAlias: 'bob'
}).then(bobIssuanceReceiver => {
  return JSON.stringify(bobIssuanceReceiver)
})
  • Java
  • Node
  • Ruby

The issuer then builds, signs, and submits a transaction, sending gold to Bob’s receiver.

Transaction.Template issuanceToReceiver = new Transaction.Builder()
  .addAction(new Transaction.Action.Issue()
    .setAssetAlias("gold")
    .setAmount(10)
  ).addAction(new Transaction.Action.ControlWithReceiver()
    .setReceiver(Receiver.fromJson(bobIssuanceReceiverSerialized))
    .setAssetAlias("gold")
    .setAmount(10)
  ).build(client);

Transaction.Template signedIssuanceToReceiver = HsmSigner.sign(issuanceToReceiver);

Transaction.submit(client, signedIssuanceToReceiver);
issuance_to_receiver = chain.transactions.build do |b|
  b.issue asset_alias: 'gold', amount: 10
  b.control_with_receiver(
    receiver: JSON.parse(bob_issuance_receiver_serialized),
    asset_alias: 'gold',
    amount: 10
  )
end

signed_issuance_to_receiver = signer.sign(issuance_to_receiver)

chain.transactions.submit(signed_issuance_to_receiver)
client.transactions.build(builder => {
  builder.issue({
    assetAlias: 'gold',
    amount: 10
  })
  builder.controlWithReceiver({
    receiver: JSON.parse(bobIssuanceReceiverSerialized),
    assetAlias: 'gold',
    amount: 10
  })
})
.then(issuance => signer.sign(issuance))
.then(signed => client.transactions.submit(signed))
  • Java
  • Node
  • Ruby

Simple payment

Alice pays 10 units of gold to Bob.

Within a Chain Core

Transaction.Template payment = new Transaction.Builder()
  .addAction(new Transaction.Action.SpendFromAccount()
    .setAccountAlias("alice")
    .setAssetAlias("gold")
    .setAmount(10)
  ).addAction(new Transaction.Action.ControlWithAccount()
    .setAccountAlias("bob")
    .setAssetAlias("gold")
    .setAmount(10)
  ).build(client);

Transaction.Template signedPayment = HsmSigner.sign(payment);

Transaction.submit(client, signedPayment);
payment = chain.transactions.build do |b|
  b.spend_from_account account_alias: 'alice', asset_alias: 'gold', amount: 10
  b.control_with_account account_alias: 'bob', asset_alias: 'gold', amount: 10
end

signed_payment = signer.sign(payment)

chain.transactions.submit(signed_payment)
return client.transactions.build(builder => {
  builder.spendFromAccount({
    accountAlias: 'alice',
    assetAlias: 'gold',
    amount: 10
  })
  builder.controlWithAccount({
    accountAlias: 'bob',
    assetAlias: 'gold',
    amount: 10
  })
})
.then(payment => signer.sign(payment))
.then(signed => client.transactions.submit(signed))
  • Java
  • Node
  • Ruby

Between two Chain Cores

First, Bob creates a receiver in his account, which he can serialize and send to Alice.

Receiver bobPaymentReceiver = new Account.ReceiverBuilder()
  .setAccountAlias("bob")
  .create(otherCoreClient);
String bobPaymentReceiverSerialized = bobPaymentReceiver.toJson();
bob_payment_receiver = other_core.accounts.create_receiver(
  account_alias: 'bob'
)
bob_payment_receiver_serialized = bob_payment_receiver.to_json
otherClient.accounts.createReceiver({
  accountAlias: 'bob'
}).then(bobPaymentReceiver => {
  return JSON.stringify(bobPaymentReceiver)
})
  • Java
  • Node
  • Ruby

Alice then builds, signs, and submits a transaction, sending gold to Bob’s receiver.

Transaction.Template paymentToReceiver = new Transaction.Builder()
  .addAction(new Transaction.Action.SpendFromAccount()
    .setAccountAlias("alice")
    .setAssetAlias("gold")
    .setAmount(10)
  ).addAction(new Transaction.Action.ControlWithReceiver()
    .setReceiver(Receiver.fromJson(bobPaymentReceiverSerialized))
    .setAssetAlias("gold")
    .setAmount(10)
  ).build(client);

Transaction.Template signedPaymentToReceiver = HsmSigner.sign(paymentToReceiver);

Transaction.submit(client, signedPaymentToReceiver);
payment_to_receiver = chain.transactions.build do |b|
  b.spend_from_account account_alias: 'alice', asset_alias: 'gold', amount: 10
  b.control_with_receiver(
    receiver: JSON.parse(bob_payment_receiver_serialized),
    asset_alias: 'gold',
    amount: 10
  )
end

signed_payment_to_receiver = signer.sign(payment_to_receiver)

chain.transactions.submit(signed_payment_to_receiver)
client.transactions.build(builder => {
  builder.spendFromAccount({
    accountAlias: 'alice',
    assetAlias: 'gold',
    amount: 10
  })
  builder.controlWithReceiver({
    receiver: JSON.parse(bobPaymentReceiverSerialized),
    assetAlias: 'gold',
    amount: 10
  })
})
.then(payment => signer.sign(payment))
.then(signed => client.transactions.submit(signed))
  • Java
  • Node
  • Ruby

Multi-asset payment

Alice pays 10 units of gold and 20 units of silver to Bob.

Within a Chain Core

Transaction.Template multiAssetPayment = new Transaction.Builder()
  .addAction(new Transaction.Action.SpendFromAccount()
    .setAccountAlias("alice")
    .setAssetAlias("gold")
    .setAmount(10)
  ).addAction(new Transaction.Action.SpendFromAccount()
    .setAccountAlias("alice")
    .setAssetAlias("silver")
    .setAmount(20)
  ).addAction(new Transaction.Action.ControlWithAccount()
    .setAccountAlias("bob")
    .setAssetAlias("gold")
    .setAmount(10)
  ).addAction(new Transaction.Action.ControlWithAccount()
    .setAccountAlias("bob")
    .setAssetAlias("silver")
    .setAmount(20)
  ).build(client);

Transaction.Template signedMultiAssetPayment = HsmSigner.sign(multiAssetPayment);

Transaction.submit(client, signedMultiAssetPayment);
multi_asset_payment = chain.transactions.build do |b|
  b.spend_from_account account_alias: 'alice', asset_alias: 'gold', amount: 10
  b.spend_from_account account_alias: 'alice', asset_alias: 'silver', amount: 20
  b.control_with_account account_alias: 'bob', asset_alias: 'gold', amount: 10
  b.control_with_account account_alias: 'bob', asset_alias: 'silver', amount: 20
end

signed_multi_asset_payment = signer.sign(multi_asset_payment)

chain.transactions.submit(signed_multi_asset_payment)
return client.transactions.build(builder => {
  builder.spendFromAccount({
    accountAlias: 'alice',
    assetAlias: 'gold',
    amount: 10
  })
  builder.spendFromAccount({
    accountAlias: 'alice',
    assetAlias: 'silver',
    amount: 20
  })
  builder.controlWithAccount({
    accountAlias: 'bob',
    assetAlias: 'gold',
    amount: 10
  })
  builder.controlWithAccount({
    accountAlias: 'bob',
    assetAlias: 'silver',
    amount: 20
  })
})
.then(payment => signer.sign(payment))
.then(signed => client.transactions.submit(signed))
  • Java
  • Node
  • Ruby

Between two Chain Cores

Currently, the transaction builder API assigns each receiver to its own output, which means that a single receiver can only be used to receive a single asset type. It’s important for Bob not to re-use receivers, so he creates one for each asset payment he will receive. He serializes both and sends them to Alice.

Receiver bobGoldReceiver = new Account.ReceiverBuilder()
  .setAccountAlias("bob")
  .create(otherCoreClient);
String bobGoldReceiverSerialized = bobGoldReceiver.toJson();

Receiver bobSilverReceiver = new Account.ReceiverBuilder()
  .setAccountAlias("bob")
  .create(otherCoreClient);
String bobSilverReceiverSerialized = bobSilverReceiver.toJson();
bob_gold_receiver = other_core.accounts.create_receiver(
  account_alias: 'bob'
)
bob_gold_receiver_serialized = bob_gold_receiver.to_json

bob_silver_receiver = other_core.accounts.create_receiver(
  account_alias: 'bob'
)
bob_silver_receiver_serialized = bob_silver_receiver.to_json
Promise.all([
  otherClient.accounts.createReceiver({
    accountAlias: 'bob'
  }),
  otherClient.accounts.createReceiver({
    accountAlias: 'bob'
  }),
]).then(receivers => {
  return {
    bobGoldReceiverSerialized: JSON.stringify(receivers[0]),
    bobSilverReceiverSerialized: JSON.stringify(receivers[1]),
  }
})
  • Java
  • Node
  • Ruby

Alice then builds, signs, and submits a transaction, sending gold and silver to Bob’s receivers.

Transaction.Template multiAssetToReceiver = new Transaction.Builder()
  .addAction(new Transaction.Action.SpendFromAccount()
    .setAccountAlias("alice")
    .setAssetAlias("gold")
    .setAmount(10)
  ).addAction(new Transaction.Action.SpendFromAccount()
    .setAccountAlias("alice")
    .setAssetAlias("silver")
    .setAmount(20)
  ).addAction(new Transaction.Action.ControlWithReceiver()
    .setReceiver(Receiver.fromJson(bobGoldReceiverSerialized))
    .setAssetAlias("gold")
    .setAmount(10)
  ).addAction(new Transaction.Action.ControlWithReceiver()
    .setReceiver(Receiver.fromJson(bobSilverReceiverSerialized))
    .setAssetAlias("silver")
    .setAmount(20)
  ).build(client);

Transaction.Template signedMultiAssetToReceiver = HsmSigner.sign(multiAssetToReceiver);

Transaction.submit(client, signedMultiAssetToReceiver);
multi_asset_to_receiver = chain.transactions.build do |b|
  b.spend_from_account account_alias: 'alice', asset_alias: 'gold', amount: 10
  b.spend_from_account account_alias: 'alice', asset_alias: 'silver', amount: 20
  b.control_with_receiver(
    receiver: JSON.parse(bob_gold_receiver_serialized),
    asset_alias: 'gold',
    amount: 10
  )
  b.control_with_receiver(
    receiver: JSON.parse(bob_silver_receiver_serialized),
    asset_alias: 'silver',
    amount: 20
  )
end

signed_multi_asset_to_receiver = signer.sign(multi_asset_to_receiver)

chain.transactions.submit(signed_multi_asset_to_receiver)
client.transactions.build(builder => {
  builder.spendFromAccount({
    accountAlias: 'alice',
    assetAlias: 'gold',
    amount: 10
  })
  builder.spendFromAccount({
    accountAlias: 'alice',
    assetAlias: 'silver',
    amount: 20
  })
  builder.controlWithReceiver({
    receiver: JSON.parse(bobGoldReceiverSerialized),
    assetAlias: 'gold',
    amount: 10
  })
  builder.controlWithReceiver({
    receiver: JSON.parse(bobSilverReceiverSerialized),
    assetAlias: 'silver',
    amount: 20
  })
})
.then(payment => signer.sign(payment))
.then(signed => client.transactions.submit(signed))
  • Java
  • Node
  • Ruby

Asset retirement

Alice retires 50 units of gold from her account.

Transaction.Template retirement = new Transaction.Builder()
  .addAction(new Transaction.Action.SpendFromAccount()
    .setAccountAlias("alice")
    .setAssetAlias("gold")
    .setAmount(50)
  ).addAction(new Transaction.Action.Retire()
    .setAssetAlias("gold")
    .setAmount(50)
  ).build(client);

Transaction.Template signedRetirement = HsmSigner.sign(retirement);

Transaction.submit(client, signedRetirement);
retirement = chain.transactions.build do |b|
  b.spend_from_account account_alias: 'alice', asset_alias: 'gold', amount: 50
  b.retire asset_alias: 'gold', amount: 50
end

signed_retirement = signer.sign(retirement)

chain.transactions.submit(signed_retirement)
client.transactions.build(builder => {
  builder.spendFromAccount({
    accountAlias: 'alice',
    assetAlias: 'gold',
    amount: 50
  })
  builder.retire({
    assetAlias: 'gold',
    amount: 50
  })
})
.then(retirement => signer.sign(retirement))
.then(signed => client.transactions.submit(signed))
  • Java
  • Node
  • Ruby

Multiparty trades

For examples of advanced transactions, such as trading multiple assets across multiple cores, see Multiparty Trades.