-
Notifications
You must be signed in to change notification settings - Fork 0
Description
交易
交易的数据结构为:
pub struct Transaction {
pub signatures: Vec<Signature>,
pub message: Message,
}交易本身不包含指令,它包含的 Message 是指令序列的预编译表示。
Message 的构造函数负责将每个指令所需的账户列表重新排序,合并为 Solana 运行时所需的去重账户扁平列表。
signatures是签名列表,由Message.account_keys数组中的前几个账户签名, 签名个数由Message.MessageHeader.num_required_signatures确定。
签名占位符
在new_unsigned API 实现中,创建未签名的交易,签名列表也要按照message.header.num_required_signatures指定的数量构造,每个元素都使用Signature::default()创建,作为一个签名的占位符,其真实值为一个64字节的全0字节序列,使用println!打印时会转为base58编码,打印出内容1111111111111111111111111111111111111111111111111111111111111111是64个1
Message
Message结构为:
pub struct Message {
pub header: MessageHeader,
pub account_keys: Vec<Pubkey>,
pub recent_blockhash: Hash,
pub instructions: Vec<CompiledInstruction>,
}MessageHeader
MessageHeader结构为:
pub struct MessageHeader {
pub num_required_signatures: u8,
pub num_readonly_signed_accounts: u8,
pub num_readonly_unsigned_accounts: u8,
}账户根据是否签名者、是否可写分为四类:
- 可写、签名者:例如钱包主账户,需对交易签名,且其数据可被修改(余额变动)
- 只读、签名者:例如多签账户、质押授权账户等,需对交易签名,但账户数据不会变化,只读
- 可写、非签名者:例如收币账户等,不需对交易签名,但其数据可被修改(余额变动)
- 只读、非签名者:例如程序账户(系统程序账户、代币元数据账户),仅用于读取程序代码,不对交易签名
在new_with_compiled_instructions() API 实现中,对上述三个字段的赋值操作为:
- pub num_required_signatures: from_keypairs_len as u8,签名者数量
- pub num_readonly_signed_accounts: 0,已签名的只读账户数量,这里常规交易场景直接设为0;只有当多签合约相关场景中才会出现需要签名且只读的账户,因为这些账户只需要签名,其状态不会变更
- pub num_readonly_unsigned_accounts: program_ids.len() as u8,合约账户是未签名且只读账户
Presigner
pub struct Presigner {
pubkey: Pubkey,
signature: Signature,
}包含一个公钥和一个签名, 只验证签名是否和消息、公钥匹配,不持有私钥,无法生成新签名
用于提前生成并提供签名的场景:
- 离线签名:签名在安全环境下提前生成,之后在不安全环境只提供签名本身
- 多方签名:某些签名由外部系统或硬件提前生成
最大账户锁定数量
有一个常量定义如下:
/// Maximum number of accounts that a transaction may lock.
/// 128 was chosen because it is the minimum number of accounts
/// needed for the Neon EVM implementation.
pub const MAX_TX_ACCOUNT_LOCKS: usize = 128;MAX_TX_ACCOUNT_LOCKS 是交易锁定账户数量的上限,设为 128,主要是为了兼容 Neon EVM(Solana 上的以太坊兼容虚拟机实现)的需求。
简单投票交易
投票交易是 Solana 共识机制的核心部分。每个验证者节点会定期向网络广播“我认为哪些区块是有效的”——这就是投票。
简单投票交易是最常见、最基础的投票交易形式,有如下特征:
- 只有一个指令,且
program_id为Vote111111111111111111111111111111111111111 - 有 1 或 2 个签名
- 是一个 legacy 交易
签名数量分两种场景:
- 1 个签名:普通投票,验证者自己签名。
- 2 个签名:验证者账户的投票权被委托给了另一个账户(即设置了 authorized voter),需要两者都签名。
Transfer指令
transfer指令中的data数据是字节流,里面包含了transfer指令的类型值与转账的lamports值:
[0x02][lamports的8字节LE表示]
Rust SDK 中关键代码:
pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction {
let account_metas = vec![
AccountMeta::new(*from_pubkey, true),
AccountMeta::new(*to_pubkey, false),
];
Instruction::new_with_bincode(ID, &SystemInstruction::Transfer { lamports }, account_metas)
}
pub fn new_with_bincode<T: serde::Serialize>(
program_id: Pubkey,
data: &T,
accounts: Vec<AccountMeta>,
) -> Self {
let data = bincode::serialize(data).unwrap();
Self {
program_id,
accounts,
data,
}
}SystemInstruction::Transfer 是一个enum值,其索引为2,在bincode序列化时处理为字节流。