Corda API: States

原文地址:https://docs.corda.net/corda-api.html

在阅读本篇文章前,请阅读 Corda 核心概念 - States

ContractState

在 Corda 中,states 是那些实现了 ContractState 的类实例。ContractState 接口定义如下:

/**
 * A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk
 * file that the program can use to persist data across transactions. States are immutable: once created they are never
 * updated, instead, any changes must generate a new successor state. States can be updated (consumed) only once: the
 * notary is responsible for ensuring there is no "double spending" by only signing a transaction if the input states
 * are all free.
 */
@CordaSerializable
interface ContractState {
    /**
     * A _participant_ is any party that is able to consume this state in a valid transaction.
     *
     * The list of participants is required for certain types of transactions. For example, when changing the notary
     * for this state, every participant has to be involved and approve the transaction
     * so that they receive the updated state, and don't end up in a situation where they can no longer use a state
     * they possess, since someone consumed that state during the notary change process.
     *
     * The participants list should normally be derived from the contents of the state.
     */
    val participants: List<AbstractParty>
}

ContractState 只有一个字段 participantsparticipants 是一个 AbstractPartyList,代表了同这个 state 有关的节点。participants 将会:

  • 通常会将 state 存储到他们的账本中
  • 需要为任何涉及到该 state 的 notary 变更和合约升级的交易提供签名
  • 作为 FinalityFlow 的一部分,接收任何涉及到该 state 的最终交易信息

ContractState 子接口

state 的行为可以通过实现 ContractState 的子接口被进一步的定制。最常用的两个子接口包括:

  • LinearState
  • OwnableState

LinearState 代表了一个在任何时间都是只有一个当前版本的共享的事实(shared fact)。LinearState states 通过替换自己的方式实现一个线性的改变。而 OwnableState 则代表在任何时候都可以被自由的拆分或者合并的资产。现金 cash 就是一个 OwnableState 的很好的例子 - 两个已经存在 $5 现金 state 可以合并为一个单独的 $10 的现金 state,或者被拆分成 5 个 $1 的现金 state。对于 OwnableState,它的总金额是更重要的,而不是到底有多少份。

我们可以通过下图来表述这个结构:
Linear State and Ownable State

LinearState

LinearState 接口定义如下:

/**
 * A state that evolves by superseding itself, all of which share the common "linearId".
 *
 * This simplifies the job of tracking the current version of certain types of state in e.g. a vault.
 */
interface LinearState : ContractState {
    /**
     * Unique id shared by all LinearState states throughout history within the vaults of all parties.
     * Verify methods should check that one input and one output share the id in a transaction,
     * except at issuance/termination.
     */
    val linearId: UniqueIdentifier
}

记住在 Corda 中,states 是不可变的,并且不能直接的更改的。然而,我们可以使用有序的 LinearState states 来表现一个事实,这些 states 共同分享一个 linearId,并且他们能够代表一个事实的整个生命周期。

当我们想要扩展一个 LinearState 链的时候,我们会:

  • 使用 linearId 从账本中获取该 state 链中最新的 state
  • 创建一个具有相同 linearId 的新的 state
  • 创建一个包含下边元素的 transaction:
    • 将该 state 链中的当前版本的 state 作为 input
    • 将新创建的 state 作为 output

新创建的 state 现在就成为了这个 state 链的最新的 state,代表了协议的最新的当前 state。

linearId 是一种 UniqueIdentifier 类型,由下边的元素组成:

  • 一个 Java UUID,代表了一个全局唯一的 128 bit 的随机数
  • 一个可选的外部引用(external-reference) 字符串,作为在外部系统中使用的引用

OwnableState

OwnableState 接口定义如下:

/**
 * Return structure for [OwnableState.withNewOwner]
 */
data class CommandAndState(val command: CommandData, val ownableState: OwnableState)

/**
 * A contract state that can have a single owner.
 */
interface OwnableState : ContractState {
    /** There must be a MoveCommand signed by this key to claim the amount. */
    val owner: AbstractParty

    /** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone. */
    fun withNewOwner(newOwner: AbstractParty): CommandAndState
}

其中:

  • owner 是该资产的所有者的公钥 PublicKey
  • withNewOwner(newOwner: AbstractParty) 创建了一个具有新的所有者的 state 的副本

由于 OwnableState 形成了一个可替换的资产(fungible assets)的模型,这种资产可以合并和拆分,OwnableState 实例没有 linearId。一笔交易产生的 $5 现金和另一笔其他的交易产生的 $5 现金会被看作是同样的 state。

其他接口

你也可以通过实现下边的接口来定制你的 state:

  • QueryableState,这可以让 state 能够在节点的数据库中通过使用自定义的属性来被查询
  • SchedulableState,可以允许我们对 state 设置一个将来会发生的动作(比如使用优惠券购买债券)

用户定义字段 User-defined fields

除了实现 ContractState 或者子接口外,一个 state 还允许包含任意数量的额外字段和方法。比如下边的代码就定义了一个相对复杂的代表现金 cash 的一个 state:

    /** A state representing a cash claim against some party. */
    data class State(
            override val amount: Amount<Issued<Currency>>,

            /** There must be a MoveCommand signed by this key to claim the amount. */
            override val owner: AbstractParty
    ) : FungibleAsset<Currency>, QueryableState {
        constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: AbstractParty)
                : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)

        override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey)
        override val participants = listOf(owner)

        override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty): FungibleAsset<Currency>
                = copy(amount = amount.copy(newAmount.quantity), owner = newOwner)

        override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)"

        override fun withNewOwner(newOwner: AbstractParty) = CommandAndState(Commands.Move(), copy(owner = newOwner))
        infix fun ownedBy(owner: AbstractParty) = copy(owner = owner)
        infix fun issuedBy(party: AbstractParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party))))
        infix fun issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit)))
        infix fun withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit)))

        /** Object Relational Mapping support. */
        override fun generateMappedObject(schema: MappedSchema): PersistentState {
            return when (schema) {
                is CashSchemaV1 -> CashSchemaV1.PersistentCashState(
                        owner = this.owner,
                        pennies = this.amount.quantity,
                        currency = this.amount.token.product.currencyCode,
                        issuerPartyHash = this.amount.token.issuer.party.owningKey.toStringShort(),
                        issuerRef = this.amount.token.issuer.reference.bytes
                )
            /** Additional schema mappings would be added here (eg. CashSchemaV2, CashSchemaV3, ...) */
                else -> throw IllegalArgumentException("Unrecognised schema $schema")
            }
        }

        /** Object Relational Mapping support. */
        override fun supportedSchemas(): Iterable<MappedSchema> = listOf(CashSchemaV1)
        /** Additional used schemas would be added here (eg. CashSchemaV2, CashSchemaV3, ...) */
    }

Vault

当一个节点记录了一笔新的交易的时候,它还可以选择是否将交易的每一个 output state 存储到它的 vault 中。默认的 vault 实现让这个决定基于以下的规则:

  • 如果 state 是一个 OwnableState,如果该节点是该 state 的 owner 的时候,账本将会记录该 state
  • 如果不是上边的情况,如果节点是该 state 的一个参与者 participants,那么账本就会记录该 state

不相关的 states 是不会存储到节点的账本中的。但是节点还是会将创建该 state 的交易信息存储到它的 transaction storage 中。

TransactionState

当一个 ContractState 被添加到一个 TransactionBuilder 之后,它就被包装成了一个 TransactionState

typealias ContractClassName = String

/**
 * A wrapper for [ContractState] containing additional platform-level state information and contract information.
 * This is the definitive state that is stored on the ledger and used in transaction outputs.
 */
@CordaSerializable
data class TransactionState<out T : ContractState> @JvmOverloads constructor(
        /** The custom contract state */
        val data: T,
        /**
         * The contract class name that will verify this state that will be created via reflection.
         * The attachment containing this class will be automatically added to the transaction at transaction creation
         * time.
         *
         * Currently these are loaded from the classpath of the node which includes the cordapp directory - at some
         * point these will also be loaded and run from the attachment store directly, allowing contracts to be
         * sent across, and run, from the network from within a sandbox environment.
         *
         * TODO: Implement the contract sandbox loading of the contract attachments
         * */
        val contract: ContractClassName,
        /** Identity of the notary that ensures the state is not used as an input to a transaction more than once */
        val notary: Party,
        /**
         * All contract states may be _encumbered_ by up to one other state.
         *
         * The encumbrance state, if present, forces additional controls over the encumbered state, since the platform checks
         * that the encumbrance state is present as an input in the same transaction that consumes the encumbered state, and
         * the contract code and rules of the encumbrance state will also be verified during the execution of the transaction.
         * For example, a cash contract state could be encumbered with a time-lock contract state; the cash state is then only
         * processable in a transaction that verifies that the time specified in the encumbrance time-lock has passed.
         *
         * The encumbered state refers to another by index, and the referred encumbrance state
         * is an output state in a particular position on the same transaction that created the encumbered state. An alternative
         * implementation would be encumbering by reference to a [StateRef], which would allow the specification of encumbrance
         * by a state created in a prior transaction.
         *
         * Note that an encumbered state that is being consumed must have its encumbrance consumed in the same transaction,
         * otherwise the transaction is not valid.
         */
        val encumbrance: Int? = null,
        /**
         * A validator for the contract attachments on the transaction.
         */
        val constraint: AttachmentConstraint = AutomaticHashConstraint)

其中:

  • data 是将被存储到账本中的 state
  • contract 是一个控制 state 转变的合约
  • notary 是这个 state 的 notary service
  • encumbrance 指向了另一个 state,该 state 必须以一个 input 的形式出现在消费此 state 的交易中
  • constraint 是一个该 state 使用的合约代码 contract-code 附件的约束
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值