用Substrate区块链实现UTXO功能

2019-11-19  本文已影响0人  SeanC52111

首先我们定义实现UTXO所需要的结构体:

/// Single transaction to be dispatched
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash)]
pub struct Transaction {
    /// UTXOs to be used as inputs for current transaction
    pub inputs: Vec<TransactionInput>,

    /// UTXOs to be created as a result of current transaction dispatch
    pub outputs: Vec<TransactionOutput>,
}

结构体里声明了inputs, ouptuts,为UTXO结构中的输入和输出。#[...]表示一系列属性,它们可以告知Rust编译器来实现不同的功能,比如比较函数,哈希函数,序列化函数等。

这里的inputsoutputs的类型均为存有TransactionInputTransactionOutput的Vec(向量)。下面我们来定义对应TransactionInput的结构:

/// Single transaction input that refers to one UTXO
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash)]
pub struct TransactionInput {
    /// Reference to an UTXO to be spent
    pub parent_output: H256,

    /// Proof that transaction owner is authorized to spend referred UTXO
    pub signature: Signature,
}

TransactionInput中罗列了一个单独的UTXO所需要的全部信息。首先我们需要指明我们要使用哪个当前存在的UTXO,以便于之后将其花掉。最好的方法就是用哈希来充当它的标识。parent_output就存有这样的哈希。

要花掉UTXO,所有者必须用私钥签名,之后即可用公钥进行验证。这样的证明proof被存在signature域中。

接下来我们看TransactionOutput结构:

/// Single transaction output to create upon transaction dispatch
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Hash)]
pub struct TransactionOutput {
    /// Value associated with this output
    pub value: Value,

    /// Public key associated with this output. In order to spend this output
    /// owner must provide a proof by hashing whole `TransactionOutput` and
    /// signing it with a corresponding private key.
    pub pubkey: H256,

    /// Unique (potentially random) value used to distinguish this
    /// particular output from others addressed to the same public
    /// key with the same value. Prevents potential replay attacks.
    pub salt: u32,
}

其中,盐值salt是用来对相同的value, pubkey每次产生不同的Hash值。

状态

前面我们已经定义了相关的数据结构,比如如何在区块链中表示UTXO对应的交易结构。下面我们来看看如何存储这些transactions到Substrate的state db中。

在之前的博客中 https://www.jianshu.com/p/f9a41a2fc90a
我们知道在Substrate中进行自定义存储需要在decl_storage! macro当中定义。

decl_storage! {
    trait Store for Module<T: Trait> as Utxo {
        /// All valid unspent transaction outputs are stored in this map.
        /// Initial set of UTXO is populated from the list stored in genesis.
        UnspentOutputs build(|config: &GenesisConfig<T>| {
            config.initial_utxo
                .iter()
                .cloned()
                .map(|u| (BlakeTwo256::hash_of(&u), u))
                .collect::<Vec<_>>()
        }): map H256 => Option<TransactionOutput>;

        /// Total leftover value to be redistributed among authorities.
        /// It is accumulated during block execution and then drained
        /// on block finalization.
        LeftoverTotal: Value;

        /// Outputs that are locked
        LockedOutputs: map H256 => Option<LockStatus<T>>;
    }

    add_extra_genesis {
        config(initial_utxo): Vec<TransactionOutput>;
    }
}

上述代码定义了:

同时,上述代码还定义了在区块链启动得时候初始化UTXO。
需要注意的是,区块的存储和状态的存储有很大的区别。区块的存储对于区块链节点来说是十分重要的组成部分,它被用来储存区块链中的区块。而对于状态存储来说,它是和逻辑息息相关的。它包含了所有反应当前状态的数据和关系。为了验证新接收的transactions,我们只需要关心各个参与的party的状态和资金。这也解释了为何情节点也可以验证transactions。

逻辑

当我们说Alice收到来自Bob的转账,实际上发生的是一些列Bob付给Alice的UTXO会被标记为已经花掉。紧接着,一些列新的UTXO(由Bob产生给Alice的)会变成有效的UTXO(可在之后被使用)。

这就是给予UTXO转账的基本逻辑,我们在验证和分发transactions的时候需要考虑这样的逻辑。下面我们看看具体UTXO module(模块)的代码:

decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        /// Dispatch a single transaction and update UTXO set accordingly
        pub fn execute(origin, transaction: Transaction) -> Result {
            ensure_inherent(origin)?;

            let leftover = match Self::check_transaction(&transaction)? {
                CheckInfo::MissingInputs(_) => return Err("all parent outputs must exist and be unspent"),
                CheckInfo::Totals { input, output } => input - output
            };

            Self::update_storage(&transaction, leftover)?;
            Self::deposit_event(Event::TransactionExecuted(transaction));

            Ok(())
        }

        /// Handler called by the system on block finalization
        fn on_finalise() {
            let authorities: Vec<_> = Consensus::authorities().iter().map(|&a| a.into()).collect();
            Self::spend_leftover(&authorities);
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读