首页 > 解决方案 > Corda 上的代币发行

问题描述

运行 Corda 节点的实体能否在不知道谁拥有这些代币的数量的情况下验证网络中某个代币的发行量?

(例如,某方正在发行由实物黄金支持的黄金代币。投资者如何验证网络中黄金代币的总供应量?目前您要么失去隐私,要么必须信任第三方,如监管节点。)

标签: corda

解决方案


这是一个可能的解决方案。

在下面的代码中,TokenState定义了一个带有附加issuingHash字段的标准标记。TokenContract强制将此字段设置为在令牌已发行但可以传输之前最初发行令牌的交易的哈希值。一旦设置,issuingHash就永远无法修改。

例如,假设有一些发行交易的哈希值7925679A6414AEBF69ED1A250E3E1E4452A4384529E3B690A4B47DD6A9918B93生成 1,000,000 个代币。强制在下一个事务中将其TokenContract设置为令牌。issuingHash

现在,如果原始发行交易被广泛共享,那么每个人都可以确定只有 1,000,000 个代币issuingHash存在。公证池将拒绝任何未来将issuingHash发行交易的输出设置为相同哈希的尝试(这将构成双花尝试),并且在设置之前不能转移代币issuingHash

然后你可以说你只愿意用 代币支付issuingHash 7925679A6414AEBF69ED1A250E3E1E4452A4384529E3B690A4B47DD6A9918B93,知道只有 1,000,000 存在。

data class TokenState(val owner: Party, val amount: Int, val issuingHash: SecureHash?) : ContractState {
    override val participants: List<AbstractParty> = listOf()
}

interface TokenCommands : CommandData {
    class Issue : TokenCommands
    class SetIssuingHash : TokenCommands
    class Transfer : TokenCommands
}

class TokenContract : Contract {
    override fun verify(tx: LedgerTransaction) {
        val tokenCommand = tx.commandsOfType<TokenCommands>().singleOrNull() ?: throw IllegalArgumentException()
        val tokenInputs = tx.inputsOfType<TokenState>()
        val tokenOutputs = tx.outputsOfType<TokenState>()

        when (tokenCommand.value) {
            is TokenCommands.Issue -> {
                if (tokenInputs.isNotEmpty()) throw IllegalArgumentException()
                val tokenOutput = tokenOutputs.singleOrNull() ?: throw IllegalArgumentException()
                if (tokenOutput.issuingHash != null) throw IllegalArgumentException()
            }

            is TokenCommands.SetIssuingHash -> {
                val tokenInput = tokenInputs.singleOrNull() ?: throw IllegalArgumentException()
                val tokenOutput = tokenOutputs.singleOrNull() ?: throw IllegalArgumentException()
                if (tokenInput.issuingHash != null) throw IllegalArgumentException()
                if (tokenOutput.issuingHash != tx.inputs[0].ref.txhash) throw IllegalArgumentException()
                if (tokenOutput.copy(issuingHash = null) != tokenInput) throw IllegalArgumentException()
            }

            // Extend this logic to allow tokens with different `issuingHash`s to be used in the same transaction.
            is TokenCommands.Transfer -> {
                if ((tokenInputs + tokenOutputs).any { it.issuingHash == null }) throw IllegalArgumentException()
                if ((tokenInputs + tokenOutputs).map { it.issuingHash }.toSet().size != 1) throw IllegalArgumentException()
                if (tokenInputs.sumBy { it.amount } != tokenOutputs.sumBy { it.amount }) throw IllegalArgumentException()
            }
        }
    }
}

推荐阅读