首先我们定义实现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,
/// UTXOs to be created as a result of current transaction dispatch
pub outputs: Vec,
}
结构体里声明了inputs
, ouptuts
,为UTXO结构中的输入和输出。#[...]
表示一系列属性,它们可以告知Rust编译器来实现不同的功能,比如比较函数,哈希函数,序列化函数等。
这里的inputs
和outputs
的类型均为存有TransactionInput
和TransactionOutput
的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 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| {
config.initial_utxo
.iter()
.cloned()
.map(|u| (BlakeTwo256::hash_of(&u), u))
.collect::>()
}): map H256 => Option;
/// 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>;
}
add_extra_genesis {
config(initial_utxo): Vec;
}
}
上述代码定义了:
- 未花掉的output
- 当前剩余的金额
- 被锁定的outputs
同时,上述代码还定义了在区块链启动得时候初始化UTXO。
需要注意的是,区块的存储和状态的存储有很大的区别。区块的存储对于区块链节点来说是十分重要的组成部分,它被用来储存区块链中的区块。而对于状态存储来说,它是和逻辑息息相关的。它包含了所有反应当前状态的数据和关系。为了验证新接收的transactions,我们只需要关心各个参与的party的状态和资金。这也解释了为何情节点也可以验证transactions。
逻辑
当我们说Alice收到来自Bob的转账,实际上发生的是一些列Bob付给Alice的UTXO会被标记为已经花掉。紧接着,一些列新的UTXO(由Bob产生给Alice的)会变成有效的UTXO(可在之后被使用)。
这就是给予UTXO转账的基本逻辑,我们在验证和分发transactions的时候需要考虑这样的逻辑。下面我们看看具体UTXO module(模块)的代码:
decl_module! {
pub struct Module 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);
}
}
}