419 lines
11 KiB
Rust
419 lines
11 KiB
Rust
//! 交易构造模块
|
||
//!
|
||
//! NAC交易包含两部分:交易体 + 宪法收据(CR)
|
||
|
||
use crate::constitutional_receipt::ConstitutionalReceipt;
|
||
use serde::{Deserialize, Serialize};
|
||
use sha3::{Digest, Sha3_256};
|
||
|
||
/// 交易类型
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||
#[repr(u8)]
|
||
pub enum TransactionType {
|
||
/// XTZH转账
|
||
XTZHTransfer = 0,
|
||
/// XIC转账
|
||
XICTransfer = 1,
|
||
/// ACC-20代币转账
|
||
ACC20Transfer = 2,
|
||
/// ACC-1400证券型代币转账
|
||
ACC1400Transfer = 3,
|
||
/// 合约部署
|
||
ContractDeploy = 4,
|
||
/// 合约调用
|
||
ContractCall = 5,
|
||
/// 资产发行
|
||
AssetIssuance = 6,
|
||
}
|
||
|
||
/// 交易载荷(原始交易数据)
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct TransactionPayload {
|
||
/// 交易类型
|
||
pub tx_type: TransactionType,
|
||
/// 发送方地址
|
||
pub from: [u8; 32],
|
||
/// 接收方地址
|
||
pub to: [u8; 32],
|
||
/// 金额(对于XTZH/XIC/ACC-20)
|
||
pub amount: u64,
|
||
/// 资产GNACS编码(对于ACC-20/ACC-1400)
|
||
pub asset_gnacs: Option<[u8; 6]>,
|
||
/// 分区ID(对于ACC-1400)
|
||
pub partition_id: Option<[u8; 32]>,
|
||
/// 附加数据(合约调用参数等)
|
||
pub data: Vec<u8>,
|
||
/// Nonce(防重放)
|
||
pub nonce: u64,
|
||
/// Gas限制
|
||
pub gas_limit: u64,
|
||
/// Gas价格(XIC)
|
||
pub gas_price: u64,
|
||
/// 时间戳
|
||
pub timestamp: u64,
|
||
}
|
||
|
||
impl TransactionPayload {
|
||
/// 创建XTZH转账交易
|
||
pub fn new_xtzh_transfer(
|
||
from: [u8; 32],
|
||
to: [u8; 32],
|
||
amount: u64,
|
||
nonce: u64,
|
||
) -> Self {
|
||
Self {
|
||
tx_type: TransactionType::XTZHTransfer,
|
||
from,
|
||
to,
|
||
amount,
|
||
asset_gnacs: None,
|
||
partition_id: None,
|
||
data: Vec::new(),
|
||
nonce,
|
||
gas_limit: 21000, // 基础转账Gas
|
||
gas_price: 1,
|
||
timestamp: Self::current_timestamp(),
|
||
}
|
||
}
|
||
|
||
/// 创建XIC转账交易
|
||
pub fn new_xic_transfer(
|
||
from: [u8; 32],
|
||
to: [u8; 32],
|
||
amount: u64,
|
||
nonce: u64,
|
||
) -> Self {
|
||
Self {
|
||
tx_type: TransactionType::XICTransfer,
|
||
from,
|
||
to,
|
||
amount,
|
||
asset_gnacs: None,
|
||
partition_id: None,
|
||
data: Vec::new(),
|
||
nonce,
|
||
gas_limit: 21000,
|
||
gas_price: 1,
|
||
timestamp: Self::current_timestamp(),
|
||
}
|
||
}
|
||
|
||
/// 创建ACC-20代币转账交易
|
||
pub fn new_acc20_transfer(
|
||
from: [u8; 32],
|
||
to: [u8; 32],
|
||
amount: u64,
|
||
asset_gnacs: [u8; 6],
|
||
nonce: u64,
|
||
) -> Self {
|
||
Self {
|
||
tx_type: TransactionType::ACC20Transfer,
|
||
from,
|
||
to,
|
||
amount,
|
||
asset_gnacs: Some(asset_gnacs),
|
||
partition_id: None,
|
||
data: Vec::new(),
|
||
nonce,
|
||
gas_limit: 50000, // 代币转账需要更多Gas
|
||
gas_price: 1,
|
||
timestamp: Self::current_timestamp(),
|
||
}
|
||
}
|
||
|
||
/// 创建ACC-1400证券型代币转账交易
|
||
pub fn new_acc1400_transfer(
|
||
from: [u8; 32],
|
||
to: [u8; 32],
|
||
amount: u64,
|
||
asset_gnacs: [u8; 6],
|
||
partition_id: [u8; 32],
|
||
nonce: u64,
|
||
) -> Self {
|
||
Self {
|
||
tx_type: TransactionType::ACC1400Transfer,
|
||
from,
|
||
to,
|
||
amount,
|
||
asset_gnacs: Some(asset_gnacs),
|
||
partition_id: Some(partition_id),
|
||
data: Vec::new(),
|
||
nonce,
|
||
gas_limit: 80000, // 证券型代币需要更多Gas(合规检查)
|
||
gas_price: 1,
|
||
timestamp: Self::current_timestamp(),
|
||
}
|
||
}
|
||
|
||
/// 计算交易哈希
|
||
pub fn hash(&self) -> [u8; 32] {
|
||
let serialized = bincode::serialize(self).unwrap_or_default();
|
||
let mut hasher = Sha3_256::new();
|
||
hasher.update(&serialized);
|
||
let result = hasher.finalize();
|
||
|
||
let mut hash = [0u8; 32];
|
||
hash.copy_from_slice(&result);
|
||
hash
|
||
}
|
||
|
||
/// 获取当前时间戳
|
||
fn current_timestamp() -> u64 {
|
||
// TODO: 使用系统时间
|
||
0
|
||
}
|
||
|
||
/// 估算Gas费用(XIC)
|
||
pub fn estimate_gas_fee(&self) -> u64 {
|
||
self.gas_limit * self.gas_price
|
||
}
|
||
}
|
||
|
||
/// 完整交易(包含CR)
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct Transaction {
|
||
/// 交易载荷
|
||
pub payload: TransactionPayload,
|
||
/// 宪法收据
|
||
pub receipt: Option<ConstitutionalReceipt>,
|
||
/// 签名
|
||
pub signature: Vec<u8>,
|
||
}
|
||
|
||
impl Transaction {
|
||
/// 创建新交易(未签名)
|
||
pub fn new(payload: TransactionPayload) -> Self {
|
||
Self {
|
||
payload,
|
||
receipt: None,
|
||
signature: Vec::new(),
|
||
}
|
||
}
|
||
|
||
/// 附加宪法收据
|
||
pub fn attach_receipt(&mut self, receipt: ConstitutionalReceipt) {
|
||
self.receipt = Some(receipt);
|
||
}
|
||
|
||
/// 签名交易
|
||
pub fn sign(&mut self, signature: Vec<u8>) {
|
||
self.signature = signature;
|
||
}
|
||
|
||
/// 计算签名消息(交易体+CR的联合哈希)
|
||
pub fn signing_message(&self) -> [u8; 32] {
|
||
let mut hasher = Sha3_256::new();
|
||
|
||
// 添加交易载荷哈希
|
||
hasher.update(&self.payload.hash());
|
||
|
||
// 如果有CR,添加CR哈希
|
||
if let Some(ref receipt) = self.receipt {
|
||
hasher.update(&receipt.transaction_hash);
|
||
}
|
||
|
||
let result = hasher.finalize();
|
||
let mut hash = [0u8; 32];
|
||
hash.copy_from_slice(&result);
|
||
hash
|
||
}
|
||
|
||
/// 验证交易完整性
|
||
pub fn verify(&self) -> Result<(), String> {
|
||
// 检查是否有CR
|
||
if self.receipt.is_none() {
|
||
return Err("Missing constitutional receipt".to_string());
|
||
}
|
||
|
||
// 检查是否已签名
|
||
if self.signature.is_empty() {
|
||
return Err("Transaction not signed".to_string());
|
||
}
|
||
|
||
// 验证CR
|
||
if let Some(ref receipt) = self.receipt {
|
||
if !receipt.verify() {
|
||
return Err("Invalid constitutional receipt".to_string());
|
||
}
|
||
|
||
// 验证CR的交易哈希是否匹配
|
||
if receipt.transaction_hash != self.payload.hash() {
|
||
return Err("Constitutional receipt hash mismatch".to_string());
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 序列化为字节(用于广播)
|
||
pub fn to_bytes(&self) -> Result<Vec<u8>, String> {
|
||
bincode::serialize(self).map_err(|e| format!("Serialization error: {}", e))
|
||
}
|
||
|
||
/// 从字节反序列化
|
||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
|
||
bincode::deserialize(bytes).map_err(|e| format!("Deserialization error: {}", e))
|
||
}
|
||
}
|
||
|
||
/// 交易构造器(Builder模式)
|
||
pub struct TransactionBuilder {
|
||
tx_type: TransactionType,
|
||
from: Option<[u8; 32]>,
|
||
to: Option<[u8; 32]>,
|
||
amount: u64,
|
||
asset_gnacs: Option<[u8; 6]>,
|
||
partition_id: Option<[u8; 32]>,
|
||
data: Vec<u8>,
|
||
nonce: u64,
|
||
gas_limit: u64,
|
||
gas_price: u64,
|
||
}
|
||
|
||
impl TransactionBuilder {
|
||
/// 创建新的构造器
|
||
pub fn new(tx_type: TransactionType) -> Self {
|
||
let default_gas = match tx_type {
|
||
TransactionType::XTZHTransfer | TransactionType::XICTransfer => 21000,
|
||
TransactionType::ACC20Transfer => 50000,
|
||
TransactionType::ACC1400Transfer => 80000,
|
||
TransactionType::ContractDeploy => 200000,
|
||
TransactionType::ContractCall => 100000,
|
||
TransactionType::AssetIssuance => 150000,
|
||
};
|
||
|
||
Self {
|
||
tx_type,
|
||
from: None,
|
||
to: None,
|
||
amount: 0,
|
||
asset_gnacs: None,
|
||
partition_id: None,
|
||
data: Vec::new(),
|
||
nonce: 0,
|
||
gas_limit: default_gas,
|
||
gas_price: 1,
|
||
}
|
||
}
|
||
|
||
/// 设置发送方
|
||
pub fn from(mut self, from: [u8; 32]) -> Self {
|
||
self.from = Some(from);
|
||
self
|
||
}
|
||
|
||
/// 设置接收方
|
||
pub fn to(mut self, to: [u8; 32]) -> Self {
|
||
self.to = Some(to);
|
||
self
|
||
}
|
||
|
||
/// 设置金额
|
||
pub fn amount(mut self, amount: u64) -> Self {
|
||
self.amount = amount;
|
||
self
|
||
}
|
||
|
||
/// 设置资产GNACS
|
||
pub fn asset_gnacs(mut self, gnacs: [u8; 6]) -> Self {
|
||
self.asset_gnacs = Some(gnacs);
|
||
self
|
||
}
|
||
|
||
/// 设置分区ID
|
||
pub fn partition_id(mut self, partition_id: [u8; 32]) -> Self {
|
||
self.partition_id = Some(partition_id);
|
||
self
|
||
}
|
||
|
||
/// 设置附加数据
|
||
pub fn data(mut self, data: Vec<u8>) -> Self {
|
||
self.data = data;
|
||
self
|
||
}
|
||
|
||
/// 设置nonce
|
||
pub fn nonce(mut self, nonce: u64) -> Self {
|
||
self.nonce = nonce;
|
||
self
|
||
}
|
||
|
||
/// 设置Gas限制
|
||
pub fn gas_limit(mut self, gas_limit: u64) -> Self {
|
||
self.gas_limit = gas_limit;
|
||
self
|
||
}
|
||
|
||
/// 设置Gas价格
|
||
pub fn gas_price(mut self, gas_price: u64) -> Self {
|
||
self.gas_price = gas_price;
|
||
self
|
||
}
|
||
|
||
/// 构建交易
|
||
pub fn build(self) -> Result<Transaction, String> {
|
||
let from = self.from.ok_or("Missing 'from' address")?;
|
||
let to = self.to.ok_or("Missing 'to' address")?;
|
||
|
||
let payload = TransactionPayload {
|
||
tx_type: self.tx_type,
|
||
from,
|
||
to,
|
||
amount: self.amount,
|
||
asset_gnacs: self.asset_gnacs,
|
||
partition_id: self.partition_id,
|
||
data: self.data,
|
||
nonce: self.nonce,
|
||
gas_limit: self.gas_limit,
|
||
gas_price: self.gas_price,
|
||
timestamp: TransactionPayload::current_timestamp(),
|
||
};
|
||
|
||
Ok(Transaction::new(payload))
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_xtzh_transfer() {
|
||
let from = [1u8; 32];
|
||
let to = [2u8; 32];
|
||
let payload = TransactionPayload::new_xtzh_transfer(from, to, 1000000, 1);
|
||
|
||
assert_eq!(payload.tx_type, TransactionType::XTZHTransfer);
|
||
assert_eq!(payload.amount, 1000000);
|
||
assert_eq!(payload.gas_limit, 21000);
|
||
}
|
||
|
||
#[test]
|
||
fn test_transaction_builder() {
|
||
let from = [1u8; 32];
|
||
let to = [2u8; 32];
|
||
|
||
let tx = TransactionBuilder::new(TransactionType::XTZHTransfer)
|
||
.from(from)
|
||
.to(to)
|
||
.amount(1000000)
|
||
.nonce(1)
|
||
.build()
|
||
.unwrap();
|
||
|
||
assert_eq!(tx.payload.amount, 1000000);
|
||
}
|
||
|
||
#[test]
|
||
fn test_transaction_hash() {
|
||
let from = [1u8; 32];
|
||
let to = [2u8; 32];
|
||
let payload = TransactionPayload::new_xtzh_transfer(from, to, 1000000, 1);
|
||
|
||
let hash1 = payload.hash();
|
||
let hash2 = payload.hash();
|
||
|
||
assert_eq!(hash1, hash2); // 相同输入应产生相同哈希
|
||
}
|
||
}
|