Constructing and broadcasting a Token Transaction is a three-step process for transferring, and a two step process for minting. For the full communication flow, see the Swimlane Diagram.

Step 1: StartTokenTransaction()

StartTokenTransaction is the initial operation where a Wallet indicates to a group of Spark Operators that it wishes to Mint or Transfer funds. This step is non-binding and does not indicate an actual transfer of funds. Instead, this step allows a Wallet and a set of Spark Operators to collectively agree on a ‘Final Token Transaction’ or a set of transaction parameters (including revocation keys) that will represent the actual binding and broadcastable token transaction.

In this step, a Sender or Issuer constructs a Partial Token Transaction indicating the leaves they wish to spend and the public keys of the outputs they wish to send to. They also sign this partial token transaction for each leaf they wish to spend to prove to the Spark Entity that they have permission to execute this operation.

message StartTokenTransactionRequest {
    bytes identity_public_key = 1;
    TokenTransaction partial_token_transaction = 2;
    // List of signatures authorizing movement of tokens from the token input.
    TokenTransactionSignatures token_transaction_signatures = 3;
}

message StartTokenTransactionResponse {
    // This is the same token transaction sent by the wallet with leaf revocation public keys
    // filled. This is the final transaction that is published and gossiped among LRC-20 nodes.
    TokenTransaction final_token_transaction = 1;
    // Information for fetching and resolving the revocation keyshare on a transfer operation.
    // Contains the threshold of keyshares needed and the SO owners of those keyshares.
    SigningKeyshare keyshare_info = 2;
}

Sample Partial Token Transaction

transferTokenTransaction := &pb.TokenTransaction{
    TokenInput: &pb.TokenTransaction_TransferInput{
        TransferInput: &pb.TransferInput{
            LeavesToSpend: []*pb.TokenLeafToSpend{
                {
                    PrevTokenTransactionHash: abcdef12345,
                    PrevTokenTransactionLeafVout: 0,
                },
                {
                    PrevTokenTransactionHash: abcdef12345,
                    PrevTokenTransactionLeafVout: 1,
                },
            },
        },
    },
    OutputLeaves: []*pb.TokenLeafOutput{
        {
            OwnerPublicKey: receiver1PublicKey,
            TokenPublicKey: sUSDPublicKey,
            TokenAmount:    50,
        },
        {
            OwnerPublicKey: receiver2PublicKey,
            TokenPublicKey: sUSDPublicKey,
            TokenAmount:    50,
        },
    },
    SparkOperatorIdentityPublicKeys: []*bytes{sparkOperatorIdentityKey1,sparkOperatorIdentityKey2,}
}

Upon receiving this request, the Spark Entity will validate and process the transaction request. In addition to that, it will assign a Revocation Public Key to each output which will be essential for revoking the transaction later when the receiver wishes to spend those created leaves. For more on Revocation Key derivation and selection, see the section below.

The Spark Operator will then respond by modifying the Partial Token Transaction and return the Final Token Transaction.

The returned FinalTokenTransaction adds the following parameters to each output:

LeafId: 012131414153,
WithdrawalBondSats: bondRequiredForWithdrawal,
WithdrawalRelativeBlockLocktime: timelockForSpendingOfWithdrawedFunds,
RevocationPublicKey: outputLeafUniqueRevocationPublicKey,

Key Parameters

  • Leaf ID: A shared pointer so that the Wallet and SO have a shared reference to refer to the LTTXO being created by this operation.
  • Revocation Public Key: Because each output represents its owned Partially Signed Bitcoin Transaction, each output requires its own immutable Revocation Public Key for future spending.
  • Withdrawal Bond and Withdrawal Locktime: Parameters which specify an immutable set of requirements for a valid unilateral exit transaction of this leaf to Bitcoin L1.

Step 2: SignTokenTransaction()

SignTokenTransaction is the next operation which, once called and received by an SO, makes the Token Transaction irreversible as long as a threshold of SOs respond to the request.

In this step the Wallet simply takes the FinalTokenTransaction from the prior step and generates a unique signature intended for each SO that it would like to sign. Then it makes an RPC call to each SO individually.

message SignTokenTransactionRequest {
    TokenTransaction final_token_transaction = 1;
    repeated OperatorSpecificTokenTransactionSignature operator_specific_signatures = 2;
}

message SignTokenTransactionResponse {
    bytes spark_operator_signature = 1;
    repeated bytes token_transaction_revocation_keyshares = 2;
}

Each Spark Operator then validates the request and, if valid, responds with the revocation keyshares associated with each leaf that is being spent. In addition to this, the Spark Operator also signs the transaction and communicates this signature to an LRC-20 node which can then spread evidence of this transaction to the wider LRC-20 Gossip network. Once a threshold of SOs have signed and responded, the Wallet is now able to derive the Revocation Key.

Step 3: FinalizeTokenTransaction()

The final step is for the wallet to communicate the revocation key it has derived to each of the Spark Operators involved in the transaction. Note that for Mint operations this call is not necessary because these inputs are not associated with a revocation key.

message FinalizeTokenTransactionRequest {
    TokenTransaction final_token_transaction = 1;
    // List of ordered revocation keys that map 1:1 with leaves being spent in the
    // token transaction.
    repeated bytes leaf_to_spend_revocation_keys = 2;
}

This officially revokes the input leaves once the SOs have communicated this to their watchtowers and allows a receiver of the funds within the token transaction to be sure that the sender can not later down the line attempt to spend the funds in the revoked leaves by broadcasting the revoked transaction on chain.

Swimlane