All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.corda.contracts.clause.AbstractConserveAmount.kt Maven / Gradle / Ivy

package net.corda.contracts.clause

import net.corda.contracts.asset.OnLedgerAsset
import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor
import java.security.PublicKey

/**
 * Standardised clause for checking input/output balances of fungible assets. Requires that a
 * Move command is provided, and errors if absent. Must be the last clause under a grouping clause;
 * errors on no-match, ends on match.
 */
abstract class AbstractConserveAmount, C : CommandData, T : Any> : Clause>() {

    private companion object {
        val log = loggerFor>()
    }

    /**
     * Generate an transaction exiting fungible assets from the ledger.
     *
     * @param tx transaction builder to add states and commands to.
     * @param amountIssued the amount to be exited, represented as a quantity of issued currency.
     * @param assetStates the asset states to take funds from. No checks are done about ownership of these states, it is
     * the responsibility of the caller to check that they do not attempt to exit funds held by others.
     * @return the public key of the assets issuer, who must sign the transaction for it to be valid.
     */
    @Deprecated("This function will be removed in a future milestone", ReplaceWith("OnLedgerAsset.generateExit()"))
    @Throws(InsufficientBalanceException::class)
    fun generateExit(tx: TransactionBuilder, amountIssued: Amount>,
                     assetStates: List>,
                     deriveState: (TransactionState, Amount>, PublicKey) -> TransactionState,
                     generateMoveCommand: () -> CommandData,
                     generateExitCommand: (Amount>) -> CommandData): PublicKey
    = OnLedgerAsset.generateExit(tx, amountIssued, assetStates, deriveState, generateMoveCommand, generateExitCommand)

    override fun verify(tx: TransactionForContract,
                        inputs: List,
                        outputs: List,
                        commands: List>,
                        groupingKey: Issued?): Set {
        require(groupingKey != null) { "Conserve amount clause can only be used on grouped states" }
        val matchedCommands = commands.filter { command -> command.value is FungibleAsset.Commands.Move || command.value is FungibleAsset.Commands.Exit<*> }
        val inputAmount: Amount> = inputs.sumFungibleOrNull() ?: throw IllegalArgumentException("there is at least one asset input for group $groupingKey")
        val deposit = groupingKey!!.issuer
        val outputAmount: Amount> = outputs.sumFungibleOrZero(groupingKey)

        // If we want to remove assets from the ledger, that must be signed for by the issuer and owner.
        val exitKeys: Set = inputs.flatMap { it.exitKeys }.toSet()
        val exitCommand = matchedCommands.select>(parties = null, signers = exitKeys).filter { it.value.amount.token == groupingKey }.singleOrNull()
        val amountExitingLedger: Amount> = exitCommand?.value?.amount ?: Amount(0, groupingKey)

        requireThat {
            "there are no zero sized inputs" using inputs.none { it.amount.quantity == 0L }
            "for reference ${deposit.reference} at issuer ${deposit.party} the amounts balance: ${inputAmount.quantity} - ${amountExitingLedger.quantity} != ${outputAmount.quantity}" using
                    (inputAmount == outputAmount + amountExitingLedger)
        }

        verifyMoveCommand(inputs, commands)

        // This is safe because we've taken the commands from a collection of C objects at the start
        @Suppress("UNCHECKED_CAST")
        return matchedCommands.map { it.value }.toSet()
    }

    override fun toString(): String = "Conserve amount between inputs and outputs"
}