Solana中的native program
Posted mutourend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Solana中的native program相关的知识,希望对你有一定的参考价值。
1. 引言
Solana中的native program,类似于Fabric的系统合约,或以太坊的预编译合约。
每个validator节点会运行一小部分有用的native program。与第三方合约不同,native合约是作为validator的一部分进行运行的,且可随着cluster的升级而升级。
升级针对的场景有:
- 增加feature
- fix bugs
- 改进性能
- 个别指令的接口修改比较罕见。一般是增加新的指令,并将之前的指令标记为deprecated。
对于每个native program,会提供相应的program id和所支持instruction对应的description。
一笔交易中,可混合匹配不同native合约的指令,以及包含on-chain合约的指令。
Solana目前支持的native合约有:
- System Program
- Config Program
- Stake Program
- Vote Program
- BPF Loader
- Secp256k1 Program
2. System Program
System Program用于:
- 创建新账号
- 分配account data
- assign accounts to owning programs
- transfer lamports from System Program owned accounts and pay transaction fees。
对应的:
- program id为:11111111111111111111111111111111
- 指令有:
pub enum SystemInstruction {
CreateAccount {
lamports: u64,
space: u64,
owner: Pubkey,
},
Assign {
owner: Pubkey,
},
Transfer {
lamports: u64,
},
CreateAccountWithSeed {
base: Pubkey,
seed: String,
lamports: u64,
space: u64,
owner: Pubkey,
},
AdvanceNonceAccount,
WithdrawNonceAccount(u64),
InitializeNonceAccount(Pubkey),
AuthorizeNonceAccount(Pubkey),
Allocate {
space: u64,
},
AllocateWithSeed {
base: Pubkey,
seed: String,
space: u64,
owner: Pubkey,
},
AssignWithSeed {
base: Pubkey,
seed: String,
owner: Pubkey,
},
TransferWithSeed {
lamports: u64,
from_seed: String,
from_owner: Pubkey,
},
}
3. Config Program
Config Program可用于:
- add configuration data to the chain and the list of public keys that are permitted to modify it
Config Program对应的:
- Program id为:Config1111111111111111111111111111111111111
- 指令有:
–create_account
:Create a new, empty configuration account
–store
:Store new data in a configuration account。该指令的data为:a set of keys that gate access to the account 以及 the data to store in it。
4. Stake Program
Stake Program可用于:
- create and manage accounts representing stake and rewards for delegations to validators。
Stake Program对应的:
- Program id为:Stake11111111111111111111111111111111111111
- 指令有:
pub enum StakeInstruction {
Initialize(Authorized, Lockup),
Authorize(Pubkey, StakeAuthorize),
DelegateStake,
Split(u64),
Withdraw(u64),
Deactivate,
SetLockup(LockupArgs),
Merge,
AuthorizeWithSeed(AuthorizeWithSeedArgs),
InitializeChecked,
AuthorizeChecked(StakeAuthorize),
AuthorizeCheckedWithSeed(AuthorizeCheckedWithSeedArgs),
SetLockupChecked(LockupCheckedArgs),
}
5. Vote Program
Vote Program可用于:
- create and manage accounts that track validator voting state and rewards
Vote Program对应的:
- Program id为:Vote111111111111111111111111111111111111111
- 指令有:
pub enum VoteInstruction {
InitializeAccount(VoteInit),
Authorize(Pubkey, VoteAuthorize),
Vote(Vote),
Withdraw(u64),
UpdateValidatorIdentity,
UpdateCommission(u8),
VoteSwitch(Vote, Hash),
AuthorizeChecked(VoteAuthorize),
}
6. BPF Loader
BPF Loader可用于:
- deploy programs on the chain
- upgrade programs on the chain
- execute programs on the chain
BPF Loader对应的:
- Program id为:BPFLoaderUpgradeab1e11111111111111111111111
- 指令有:
#[repr(u8)]
pub enum UpgradeableLoaderInstruction {
InitializeBuffer,
Write {
offset: u32,
bytes: Vec<u8, Global>,
},
DeployWithMaxDataLen {
max_data_len: usize,
},
Upgrade,
SetAuthority,
Close,
}
The BPF Upgradeable Loader marks itself as “owner” of the executable and program-data accounts it creates to store your program. When a user invokes an instruction via a program id, the Solana runtime will load both your the program and its owner, the BPF Upgradeable Loader. The runtime then passes your program to the BPF Upgradeable Loader to process the instruction.
详细的合约部署流程参见:
Solana Deploy a Program
7. Secp256k1 Program
Secp256k1 Program用于:
- verify secp256k1 public key recovery operations (ecrecover)
Secp256k1 Program对应的:
- Program id为:KeccakSecp256k11111111111111111111111111111
- 指令有:
pub fn new_secp256k1_instruction(
priv_key: &libsecp256k1::SecretKey,
message_arr: &[u8],
) -> Instruction {
let secp_pubkey = libsecp256k1::PublicKey::from_secret_key(priv_key);
let eth_pubkey = construct_eth_pubkey(&secp_pubkey);
let mut hasher = sha3::Keccak256::new();
hasher.update(&message_arr);
let message_hash = hasher.finalize();
let mut message_hash_arr = [0u8; 32];
message_hash_arr.copy_from_slice(message_hash.as_slice());
let message = libsecp256k1::Message::parse(&message_hash_arr);
let (signature, recovery_id) = libsecp256k1::sign(&message, priv_key);
let signature_arr = signature.serialize();
assert_eq!(signature_arr.len(), SIGNATURE_SERIALIZED_SIZE);
let mut instruction_data = vec![];
instruction_data.resize(
DATA_START
.saturating_add(eth_pubkey.len())
.saturating_add(signature_arr.len())
.saturating_add(message_arr.len())
.saturating_add(1),
0,
);
let eth_address_offset = DATA_START;
instruction_data[eth_address_offset..eth_address_offset.saturating_add(eth_pubkey.len())]
.copy_from_slice(ð_pubkey);
let signature_offset = DATA_START.saturating_add(eth_pubkey.len());
instruction_data[signature_offset..signature_offset.saturating_add(signature_arr.len())]
.copy_from_slice(&signature_arr);
instruction_data[signature_offset.saturating_add(signature_arr.len())] =
recovery_id.serialize();
let message_data_offset = signature_offset
.saturating_add(signature_arr.len())
.saturating_add(1);
instruction_data[message_data_offset..].copy_from_slice(message_arr);
let num_signatures = 1;
instruction_data[0] = num_signatures;
let offsets = SecpSignatureOffsets {
signature_offset: signature_offset as u16,
signature_instruction_index: 0,
eth_address_offset: eth_address_offset as u16,
eth_address_instruction_index: 0,
message_data_offset: message_data_offset as u16,
message_data_size: message_arr.len() as u16,
message_instruction_index: 0,
};
let writer = std::io::Cursor::new(&mut instruction_data[1..DATA_START]);
bincode::serialize_into(writer, &offsets).unwrap();
Instruction {
program_id: solana_sdk::secp256k1_program::id(),
accounts: vec![],
data: instruction_data,
}
}
Secp256k1 Program处理的指令内容格式为:
pub struct SecpSignatureOffsets {
pub signature_offset: u16, // offset to [signature,recovery_id] of 64+1 bytes
pub signature_instruction_index: u8,
pub eth_address_offset: u16, // offset to eth_address of 20 bytes
pub eth_address_instruction_index: u8,
pub message_data_offset: u16, // offset to start of message data
pub message_data_size: u16, // size of message data
pub message_instruction_index: u8,
}
相应的伪代码实现为:
process_instruction() {
for i in 0..count {
// i'th index values referenced:
instructions = &transaction.message().instructions
signature = instructions[secp_signature_instruction_index].data[secp_signature_offset..secp_signature_offset + 64]
recovery_id = instructions[secp_signature_instruction_index].data[secp_signature_offset + 64]
ref_eth_pubkey = instructions[secp_pubkey_instruction_index].data[secp_pubkey_offset..secp_pubkey_offset + 32]
message_hash = keccak256(instructions[secp_message_instruction_index].data[secp_message_data_offset..secp_message_data_offset + secp_message_data_size])
pubkey = ecrecover(signature, recovery_id, message_hash)
eth_pubkey = keccak256(pubkey[1..])[12..]
if eth_pubkey != ref_eth_pubkey {
return Error
}
}
return Success
}
这将允许用户在交易中指定任意的指令data,用于signature and message data。通过设置特殊的指令 sysvar,可receive data from the transaction itself。
Solana中交易的cost为:需验证的签名数量 乘以 验签cost。
注意:
以上操作可在(至少部分)deserialization之后进行,但是所有的inputs都来源于交易data本身,因此,相对会比较容易实现交易处理的并行执行和PoH verification的并行执行。
参考资料
[1] Solana的native program
[2] Solana system program的system instruction
以上是关于Solana中的native program的主要内容,如果未能解决你的问题,请参考以下文章