GTokenTool全网最好的代币发行工具平台
当前位置:首页 >> solana教程 >> Solana 如何限制外部注册和质押

Solana 如何限制外部注册和质押

admin solana教程 70

限制参与

  1. 限制外部validator注册

  2. 限制外部质押->奖励分配

genesis初始化

从genesis初始化逻辑,切入validator注册和质押逻辑

--bootstrap-validator "$SOLANA_CONFIG_DIR"/bootstrap-validator/identity.json
                        "$SOLANA_CONFIG_DIR"/bootstrap-validator/vote-account.json
                        "$SOLANA_CONFIG_DIR"/bootstrap-validator/stake-account.json

支持多个validator,每三个一组,分别对应,且不能重复

  • identity_pubkey

  • vote_pubkey

  • stake_pubkey

let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);

初始化配置

let bootstrap_validator_lamports =
        value_t_or_exit!(matches, "bootstrap_validator_lamports", u64); // 分配给引导验证器的 Lamport 数量

let bootstrap_validator_stake_lamports = rent_exempt_check( // 分配给引导验证者质押账户的 lampor 数量
    &matches,
    "bootstrap_validator_stake_lamports",
    rent.minimum_balance(StakeStateV2::size_of()),
)?;

let bootstrap_stake_authorized_pubkey =
pubkey_of(&matches, "bootstrap_stake_authorized_pubkey"); // 包含授权管理引导验证者权益的公钥的文件路径 [默认值:--bootstrap-validator IDENTITY_PUBKEY]

初始化

let commission = value_t_or_exit!(matches, "vote_commission_percentage", u8);

    let mut bootstrap_validator_pubkeys_iter = bootstrap_validator_pubkeys.iter();
    loop {
        let Some(identity_pubkey) = bootstrap_validator_pubkeys_iter.next() else {
            break;
        };
        let vote_pubkey = bootstrap_validator_pubkeys_iter.next().unwrap();
        let stake_pubkey = bootstrap_validator_pubkeys_iter.next().unwrap();

        genesis_config.add_account( //添加账户,并初始增加bootstrap_validator_lamports数量余额
            *identity_pubkey,
            AccountSharedData::new(bootstrap_validator_lamports, 0, &system_program::id()),
        );

        let vote_account = vote_state::create_account_with_authorized( // 初始化账户和余额,并VoteInit初始化VoteSigner
            identity_pubkey,
            identity_pubkey,
            identity_pubkey,
            commission,
            VoteState::get_rent_exempt_reserve(&genesis_config.rent).max(1),
        );

        genesis_config.add_account(
            *stake_pubkey,
            stake_state::create_account( // 
                bootstrap_stake_authorized_pubkey
                    .as_ref()
                    .unwrap_or(identity_pubkey),
                vote_pubkey,
                &vote_account,
                &genesis_config.rent,
                bootstrap_validator_stake_lamports,
            ),
        );

        genesis_config.add_account(*vote_pubkey, vote_account);
    }

Validator初始化逻辑

pub fn create_account_with_authorized(
    node_pubkey: &Pubkey,
    authorized_voter: &Pubkey,
    authorized_withdrawer: &Pubkey,
    commission: u8,
    lamports: u64,
) -> AccountSharedData {
    let mut vote_account = AccountSharedData::new(lamports, VoteState::size_of(), &id()); // 初始化账户和余额

    let vote_state = VoteState::new( // 初始化vote_state
        &VoteInit {
            node_pubkey: *node_pubkey,
            authorized_voter: *authorized_voter,
            authorized_withdrawer: *authorized_withdrawer,
            commission,
        },
        &Clock::default(),
    );

    VoteState::serialize( // 序列化状态
        &VoteStateVersions::Current(Box::new(vote_state)),
        vote_account.data_as_mut_slice(),
    )
    .unwrap();

    vote_account
}

质押逻辑

fn do_create_account(
    authorized: &Pubkey,
    voter_pubkey: &Pubkey,
    vote_account: &AccountSharedData,
    rent: &Rent,
    lamports: u64,
    activation_epoch: Epoch,
) -> AccountSharedData {
    let mut stake_account = AccountSharedData::new(lamports, StakeStateV2::size_of(), &id()); // 初始化账户和余额

    let vote_state = vote_state::from(vote_account).expect("vote_state"); // 获取投票状态

    let rent_exempt_reserve = rent.minimum_balance(stake_account.data().len()); // genesis_config.rent 需要预留的余额

    stake_account
        .set_state(&StakeStateV2::Stake(
            Meta {
                authorized: Authorized::auto(authorized),
                rent_exempt_reserve,
                ..Meta::default()
            },
            new_stake(
                lamports - rent_exempt_reserve, // 总余额 - 需要预留的,等于本次可质押的
                voter_pubkey,
                &vote_state,
                activation_epoch,
            ),
            StakeFlags::empty(),
        ))
        .expect("set_state");

    stake_account
}

综合分析

对应代码,Validator的注册和质押流程如下

  • Validator->VoteState::new(VoteInit)->VoteState::serialize

  • StakeStateV2::Stake

在对应下面的流程图

验证者注册与委托流程图

节点之间Vote消息是如何传递的

Validator

core/src/validator.rs

pub fn new(
        mut node: Node,
        identity_keypair: Arc<Keypair>,
        ledger_path: &Path,
        vote_account: &Pubkey,
        authorized_voter_keypairs: Arc<RwLock<Vec<Arc<Keypair>>>>,
        cluster_entrypoints: Vec<ContactInfo>,
        config: &ValidatorConfig,
        should_check_duplicate_instance: bool,
        rpc_to_plugin_manager_receiver: Option<Receiver<GeyserPluginManagerRequest>>,
        start_progress: Arc<RwLock<ValidatorStartProgress>>,
        socket_addr_space: SocketAddrSpace,
        use_quic: bool,
        tpu_connection_pool_size: usize,
        tpu_enable_udp: bool, // 关注参数
        admin_rpc_service_post_init: Arc<RwLock<Option<AdminRpcRequestMetadataPostInit>>>,
    ) -> Result<Self, String> {
    ...
    let (tpu, mut key_notifies) = Tpu::new(
            &cluster_info,
            &poh_recorder,
            entry_receiver,
            retransmit_slots_receiver,
            TpuSockets {
                transactions: node.sockets.tpu,
                transaction_forwards: node.sockets.tpu_forwards,
                vote: node.sockets.tpu_vote,
                broadcast: node.sockets.broadcast,
                transactions_quic: node.sockets.tpu_quic,
                transactions_forwards_quic: node.sockets.tpu_forwards_quic,
            },
            &rpc_subscriptions,
            transaction_status_sender,
            entry_notification_sender,
            blockstore.clone(),
            &config.broadcast_stage_type,
            exit,
            node.info.shred_version(),
            vote_tracker,
            bank_forks.clone(),
            verified_vote_sender,
            gossip_verified_vote_hash_sender,
            replay_vote_receiver,
            replay_vote_sender,
            bank_notification_sender.map(|sender| sender.sender),
            config.tpu_coalesce,
            duplicate_confirmed_slot_sender,
            &connection_cache,
            turbine_quic_endpoint_sender,
            &identity_keypair,
            config.runtime_config.log_messages_bytes_limit,
            &staked_nodes,
            config.staked_nodes_overrides.clone(),
            banking_tracer,
            tracer_thread,
            tpu_enable_udp, // 关注参数
            &prioritization_fee_cache,
            config.block_production_method.clone(),
            config.generator_config.clone(),
        );

core/src/tpu.rs

impl Tpu {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        cluster_info: &Arc<ClusterInfo>,
        poh_recorder: &Arc<RwLock<PohRecorder>>,
        entry_receiver: Receiver<WorkingBankEntry>,
        retransmit_slots_receiver: Receiver<Slot>,
        sockets: TpuSockets,
        subscriptions: &Arc<RpcSubscriptions>,
        transaction_status_sender: Option<TransactionStatusSender>,
        entry_notification_sender: Option<EntryNotifierSender>,
        blockstore: Arc<Blockstore>,
        broadcast_type: &BroadcastStageType,
        exit: Arc<AtomicBool>,
        shred_version: u16,
        vote_tracker: Arc<VoteTracker>,
        bank_forks: Arc<RwLock<BankForks>>,
        verified_vote_sender: VerifiedVoteSender,
        gossip_verified_vote_hash_sender: GossipVerifiedVoteHashSender,
        replay_vote_receiver: ReplayVoteReceiver,
        replay_vote_sender: ReplayVoteSender,
        bank_notification_sender: Option<BankNotificationSender>,
        tpu_coalesce: Duration,
        duplicate_confirmed_slot_sender: DuplicateConfirmedSlotsSender,
        connection_cache: &Arc<ConnectionCache>,
        turbine_quic_endpoint_sender: AsyncSender<(SocketAddr, Bytes)>,
        keypair: &Keypair,
        log_messages_bytes_limit: Option<usize>,
        staked_nodes: &Arc<RwLock<StakedNodes>>,
        shared_staked_nodes_overrides: Arc<RwLock<HashMap<Pubkey, u64>>>,
        banking_tracer: Arc<BankingTracer>,
        tracer_thread_hdl: TracerThread,
        tpu_enable_udp: bool,
        prioritization_fee_cache: &Arc<PrioritizationFeeCache>,
        block_production_method: BlockProductionMethod,
        _generator_config: Option<GeneratorConfig>, /* vestigial code for replay invalidator */
    ) -> (Self, Vec<Arc<dyn NotifyKeyUpdate + Sync + Send>>) {
    ...
    let fetch_stage = FetchStage::new_with_sender(
            transactions_sockets,
            tpu_forwards_sockets,
            tpu_vote_sockets,
            exit.clone(),
            &packet_sender,
            &vote_packet_sender,
            &forwarded_packet_sender,
            forwarded_packet_receiver,
            poh_recorder,
            tpu_coalesce,
            Some(bank_forks.read().unwrap().get_vote_only_mode_signal()),
            tpu_enable_udp,
        );
    ...
    let (gossip_vote_sender, gossip_vote_receiver) =
            banking_tracer.create_channel_gossip_vote();
        let cluster_info_vote_listener = ClusterInfoVoteListener::new(
            exit.clone(),
            cluster_info.clone(),
            gossip_vote_sender,
            poh_recorder.clone(),
            vote_tracker,
            bank_forks.clone(),
            subscriptions.clone(),
            verified_vote_sender,
            gossip_verified_vote_hash_sender,
            replay_vote_receiver,
            blockstore.clone(),
            bank_notification_sender,
            duplicate_confirmed_slot_sender,
        );

core/src/cluster_info_vote_listener.rs

pub fn new(
        exit: Arc<AtomicBool>,
        cluster_info: Arc<ClusterInfo>,
        verified_packets_sender: BankingPacketSender,
        poh_recorder: Arc<RwLock<PohRecorder>>,
        vote_tracker: Arc<VoteTracker>,
        bank_forks: Arc<RwLock<BankForks>>,
        subscriptions: Arc<RpcSubscriptions>,
        verified_vote_sender: VerifiedVoteSender,
        gossip_verified_vote_hash_sender: GossipVerifiedVoteHashSender,
        replay_votes_receiver: ReplayVoteReceiver,
        blockstore: Arc<Blockstore>,
        bank_notification_sender: Option<BankNotificationSender>,
        duplicate_confirmed_slot_sender: DuplicateConfirmedSlotsSender,
    ) -> Self {
        let (verified_vote_label_packets_sender, verified_vote_label_packets_receiver) =
            unbounded();
        let (verified_vote_transactions_sender, verified_vote_transactions_receiver) = unbounded();
        let listen_thread = {
            let exit = exit.clone();
            let bank_forks = bank_forks.clone();
            Builder::new()
                .name("solCiVoteLstnr".to_string())
                .spawn(move || {
                    let _ = Self::recv_loop(
                        exit,
                        &cluster_info,
                        &bank_forks,
                        verified_vote_label_packets_sender,
                        verified_vote_transactions_sender,
                    );
                })
                .unwrap()
        };
        let bank_send_thread = {
            let exit = exit.clone();
            Builder::new()
                .name("solCiBankSend".to_string())
                .spawn(move || {
                    let _ = Self::bank_send_loop(
                        exit,
                        verified_vote_label_packets_receiver,
                        poh_recorder,
                        &verified_packets_sender,
                    );
                })
                .unwrap()
        };

        let send_thread = Builder::new()
            .name("solCiProcVotes".to_string())
            .spawn(move || {
                let _ = Self::process_votes_loop(
                    exit,
                    verified_vote_transactions_receiver,
                    vote_tracker,
                    bank_forks,
                    subscriptions,
                    gossip_verified_vote_hash_sender,
                    verified_vote_sender,
                    replay_votes_receiver,
                    blockstore,
                    bank_notification_sender,
                    duplicate_confirmed_slot_sender,
                );
            })
            .unwrap();

        Self {
            thread_hdls: vec![listen_thread, send_thread, bank_send_thread],
        }
    }
fn process_votes_loop(
        exit: Arc<AtomicBool>,
        gossip_vote_txs_receiver: VerifiedVoteTransactionsReceiver,
        vote_tracker: Arc<VoteTracker>,
        bank_forks: Arc<RwLock<BankForks>>,
        subscriptions: Arc<RpcSubscriptions>,
        gossip_verified_vote_hash_sender: GossipVerifiedVoteHashSender,
        verified_vote_sender: VerifiedVoteSender,
        replay_votes_receiver: ReplayVoteReceiver,
        blockstore: Arc<Blockstore>,
        bank_notification_sender: Option<BankNotificationSender>,
        duplicate_confirmed_slot_sender: DuplicateConfirmedSlotsSender,
    ) -> Result<()> {
        let mut confirmation_verifier =
            OptimisticConfirmationVerifier::new(bank_forks.read().unwrap().root());
        let mut latest_vote_slot_per_validator = HashMap::new();
        let mut last_process_root = Instant::now();
        let duplicate_confirmed_slot_sender = Some(duplicate_confirmed_slot_sender);
        let mut vote_processing_time = Some(VoteProcessingTiming::default());
        loop {
            if exit.load(Ordering::Relaxed) {
                return Ok(());
            }

            let root_bank = bank_forks.read().unwrap().root_bank();
            if last_process_root.elapsed().as_millis() > DEFAULT_MS_PER_SLOT as u128 {
                let unrooted_optimistic_slots = confirmation_verifier
                    .verify_for_unrooted_optimistic_slots(&root_bank, &blockstore);
                // SlotVoteTracker's for all `slots` in `unrooted_optimistic_slots`
                // should still be available because we haven't purged in
                // `progress_with_new_root_bank()` yet, which is called below
                OptimisticConfirmationVerifier::log_unrooted_optimistic_slots(
                    &root_bank,
                    &vote_tracker,
                    &unrooted_optimistic_slots,
                );
                vote_tracker.progress_with_new_root_bank(&root_bank);
                last_process_root = Instant::now();
            }
            let confirmed_slots = Self::listen_and_confirm_votes(
                &gossip_vote_txs_receiver,
                &vote_tracker,
                &root_bank,
                &subscriptions,
                &gossip_verified_vote_hash_sender,
                &verified_vote_sender,
                &replay_votes_receiver,
                &bank_notification_sender,
                &duplicate_confirmed_slot_sender,
                &mut vote_processing_time,
                &mut latest_vote_slot_per_validator,
            );
            match confirmed_slots {
                Ok(confirmed_slots) => {
                    confirmation_verifier
                        .add_new_optimistic_confirmed_slots(confirmed_slots.clone(), &blockstore);
                }
                Err(e) => match e {
                    Error::RecvTimeout(RecvTimeoutError::Disconnected) => {
                        return Ok(());
                    }
                    Error::ReadyTimeout => (),
                    _ => {
                        error!("thread {:?} error {:?}", thread::current().name(), e);
                    }
                },
            }
        }
    }
let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);0
let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);1
let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);2
let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);3

core/src/consensus/vote_stake_tracker.rs

let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);4

为指定账户增加质押,并返回是否为首次加入

综合分析

validator 启动时会启动gossip通信,接收和转发收到的质押信息

Vote账户如何创建

查看一下cli工具命令代码
cli/src/cli.rs

let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);5

创建Vote账户交易的组装和发送

let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);6
let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);7

sdk/program/src/system_instruction.rs

let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);8

总结

本想直接禁用底层的执行方法,和EVM还不一样,对应方法是通用的

let bootstrap_validator_pubkeys = pubkeys_of(&matches, "bootstrap_validator").unwrap();
assert_eq!(bootstrap_validator_pubkeys.len() % 3, 0);9

// TODO 还没分析完,先保存,稍后再继续

附加

    solana-cli 中与质押和验证者有关的参数

    参数解释
    close-vote-account关闭投票账户并提取所有剩余资金
    create-stake-account创建质押账户
    create-stake-account-checked创建一个质押账户,检查作为签名者的提现权限
    create-vote-account创建投票账户
    deactivate-stake从质押账户中取消委托质押
    delegate-stake将质押委托给投票账户
    split-stake复制一个质押账户,将代币拆分到两个账户中
    stake-account显示质押账户的内容
    stake-authorize为给定的质押账户授权新的签名密钥对
    stake-authorize-checked为给定的质押账户授权新的签名密钥对,检查作为签名者的权限
    stake-history显示质押历史记录
    stake-minimum-delegation获取质押最低委托额度
    stake-set-lockup设置质押账户锁仓
    stake-set-lockup-checked设置质押账户的 Lockup,检查作为签名者的新权限
    stakes显示质押账户信息
    validator-info发布~获取 Solana 上的验证者信息
    validators显示当前验证器的摘要信息
    verify-offchain-signature验证链下消息签名
    vote-account显示投票账户的内容
    vote-authorize-voter为给定的投票账户授权新的投票签名密钥对
    vote-authorize-voter-checked为给定的投票账户授权新的投票签名密钥对,检查作为签名者的新权限
    vote-authorize-withdrawer为给定的投票账户授权新的提款签名密钥对
    vote-authorize-withdrawer-checked为给定的投票账户授权新的提款签名密钥对,检查作为签名者的新权限
    vote-update-commission更新投票账户的佣金
    vote-update-validator更新投票账户的验证者身份
    wait-for-max-stake等待任何一个节点的最大股权降至总股权的一定百分比以下。
    withdraw-from-nonce-account从 nonce 账户中提取 SOL
    withdraw-from-vote-account将投票账户中的 lamport 提现到指定账户
    withdraw-stake从质押账户中提取未质押的 SOL


    作者:GTokenTool一键发币平台

    交流群:https://t.me/+Kz4u3xoDpFo3ZWY1

    同类推荐