Solana 代币 2022 — Transfer Hook
从零到英雄的 Solana 代币 2022 — Transfer Hook
Token 2022 计划引入了几项令人兴奋的扩展,增强了铸造和代币账户的功能。在这些功能中,我个人最喜欢的是Transfer Hook (转账钩子) 。
想象时间
让我们戴上想象的帽子,想象一下这个美好的场景:你是一个 NFT 项目的所有者。你向持有者发放代币,以奖励他们将 NFT 抵押给你。你将你的项目视为一个“封闭社区”,这意味着只有你的 NFT 持有者才能持有你的代币。在没有转账钩子之前,你必须构建自己的程序来处理转账,以强制执行这种行为。但有了转账钩子,这就非常简单了!你只需要构建一个转账钩子程序,验证转账的目标地址是否确实是白名单地址之一,如果不是,则停止交易。另一个例子?假设你希望用户在每次进行代币转账时支付额外费用……没问题!这也可以通过构建一个处理转账的钩子来实现!
现在,在 NFT 项目的背景下,这些例子可能看起来有些愚蠢,但想象一下更大的图景,这样的钩子可以帮助遵守监管要求,例如强制执行持有期或设置代币的最大持有量。
Transfer Hook — 它到底是什么?
顾名思义,Transfer Hook 扩展与代币转账密切相关。每当一个铸造配置了 Transfer Hook 时,每次在该铸造上执行转账指令时,都会自动触发一个指令。如果这个概念看起来很熟悉,那是因为它确实如此——这个流程与 web2 中的 webhooks 工作方式非常相似。
转账钩子指令可以访问一个变量,即转账金额,但我们可以提供一个额外的账户(extra-account-meta-list
),其中包含指令可以使用的其他自定义账户。我们很快会详细讨论这个账户。
虽然转账钩子确实可以访问初始转账账户,但需要注意的是,它们作为只读账户传递,这意味着发送者的签名权限不会扩展到Transfer Hook程序。
最后,当使用 Anchor 编写Transfer Hook程序时,需要一个回退指令来手动匹配原生程序指令鉴别器并调用Transfer Hook指令。这是因为 Token 2022 计划是一个原生程序,因此我们需要“桥接”其原生接口与 Anchor。
是时候动手了
好了,话不多说——让我们构建一些东西!我们将构建一个“鲸鱼警报”转账钩子 🐋!对于每次代币转账,转账钩子指令将比较转账金额与预定义的值(例如,10,000 代币)。如果等于或大于该值,我们将更新一个账户,记录最新的鲸鱼详情,并发出一个事件供客户端处理。
⚒️ 你将需要安装 Solana CLI 工具和 Anchor。如果你还没有安装,请查看 Anchor 文档 以获取安装说明。
第一步:转账钩子程序
让我们设计我们的转账钩子程序。我们的转账钩子程序首先需要一个指令,可以在转账完成后调用。我们将使用 Anchor 构建我们的程序,然而,Token 2022 计划是一个原生 Solana 程序——因此我们需要另一个指令:一个指令来匹配指令鉴别器到转账钩子的 execute
接口,并在匹配时调用 transfer_hook
指令。所以目前有两个指令。
当我们的钩子被调用时,它会将转账金额与一个值进行比较——但这个值来自哪里?当然,我们可以在程序中硬编码这个值——但如果明天我们决定要增加/减少这个值呢?我们需要重新部署程序。更好的处理方式是将比较值保存在一个账户中。要将额外的账户传递给转账钩子,我们使用 extra_account_meta_list
账户。这是一个 PDA 账户,存储转账钩子在调用时可以使用的账户。extra_account_meta_list
账户将始终使用硬编码字符串 extra-account-metas 和代币的铸造地址作为种子。
现在你可能会问自己,我们如何创建和初始化这个 extra_account_meta_list
账户?这就是我们程序的第三个也是最后一个指令——initialize_extra_account_meta_list
指令的作用。
在完成我们程序的高层设计后,让我们开始实际编写转账钩子程序 🎉。
- 创建一个新的 Anchor 项目并在你喜欢的 IDE 中打开它:
anchor init transfer-hook-whale
- 安装 anchor-spl, spl-transfer-hook-interface 和 spl_tlv_account_resolution crates。这些 crates 包含帮助函数和接口,使我们在处理 SPL 代币和扩展指令时更加轻松。
cd programs/transfer-hook-whale
cargo add anchor-spl spl-transfer-hook-interface spl_tlv_account_resolution
- 从 Anchor 0.30 开始,我们需要告诉 Anchor 为我们使用的 crates 生成类型定义,因此我们需要告诉它为 anchor-spl crate 生成类型定义。打开 Cargo.toml 文件,位于 programs/transfer-hook-whale 文件夹内,并按如下方式更新
idl-build
:
[features]
...
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
- 打开 lib.rs 并添加以下
use
语句:
use anchor_lang::system_program::{create_account, CreateAccount};
use anchor_spl::{associated_token::AssociatedToken,token_interface::{Mint, TokenInterface},
};
use spl_transfer_hook_interface::instruction::TransferHookInstruction;use spl_tlv_account_resolution::{account::ExtraAccountMeta, seeds::Seed, state::ExtraAccountMetaList,
};
use spl_transfer_hook_interface::instruction::ExecuteInstruction;
一个转账钩子程序通常由三个指令组成:
- initialize_extra_account_meta — 一个创建账户(
extra_account_meta_list
)的指令,该账户保存转账钩子可以使用的额外账户列表。
在我们的例子中,这个账户将保存最新“鲸鱼”地址和金额的详细信息。 - transfer_hook — 钩子本身。这是每次发生代币转账时实际调用的指令。
- fallback — Token 2022 计划是一个原生程序,但我们使用 Anchor 构建我们的程序,因此我们需要添加一个回退指令,该指令将指令鉴别器匹配到转账钩子的
execute
接口,并在匹配时调用我们的transfer_hook
指令。
实现 initialize_extra_account_meta 指令
- 在你的 lib.rs 底部,添加以下账户。此账户将保存最新“鲸鱼”转账的详细信息:
#[account]
pub struct WhaleAccount {pub whale_address: Pubkey,pub transfer_amount: u64
}
- 接下来,我们定义
initialize_extra_account_meta
指令执行所需的所有账户。在LatestWhaleAccount
上方添加以下代码片段:
#[derive(Accounts)]
pub struct InitializeExtraAccountMeta<'info> {#[account(mut)]pub payer: Signer<'info>,/// CHECK: ExtraAccountMetaList Account, must use these exact seeds#[account(mut, seeds=[b"extra-account-metas", mint.key().as_ref()], bump)]pub extra_account_meta_list: AccountInfo<'info>,pub mint: InterfaceAccount<'info, Mint>,#[account(init, seeds=[b"whale_account"], bump, payer=payer, space=8+32+8)]pub latest_whale_account: Account<'info, WhaleAccount>,pub token_program: Interface<'info, TokenInterface>,pub associated_token_program: Program<'info, AssociatedToken>,pub system_program: Program<'info, System>,
}
🧑🏫 提示:
- 将保存额外账户的账户 (
extra_account_meta_list
) 必须有一个非常特定的种子用于其 PDA:字节串 extra-account-metas 和代币的mint
账户的公钥。 - 将保存鲸鱼详细信息的账户 (
latest_whale_account
) 将有一个简单的种子用于其 PDA:字符串 whale_account 的字节。这意味着我们铸造的所有代币将共享同一个账户。 - 我们必须为指令提供代币程序、关联代币程序和系统程序,因为它在运行时需要使用它们。
- 接下来,让我们添加
initialize_extra_account_meta
指令。用以下内容替换默认的initialize
指令:
pub fn initialize_extra_account(ctx: Context<InitializeExtraAccountMeta>) -> Result<()> {// 这是我们需要的额外账户的向量。在我们的例子中// 只有一个账户 - 鲸鱼详细信息账户。let account_metas = vec![ExtraAccountMeta::new_with_seeds(&[Seed::Literal {bytes: "whale_account".as_bytes().to_vec(),}],false,true,)?];// 计算账户大小和租金let account_size = ExtraAccountMetaList::size_of(account_metas.len())? as u64;let lamports = Rent::get()?.minimum_balance(account_size as usize);// 从上下文中获取铸造账户公钥。let mint = ctx.accounts.mint.key();// ExtraAccountMetaList PDA 的种子。let signer_seeds: &[&[&[u8]]] = &[&[b"extra-account-metas",&mint.as_ref(),&[ctx.bumps.extra_account_meta_list],]];// 创建 ExtraAccountMetaList 账户create_account(CpiContext::new(ctx.accounts.system_program.to_account_info(),CreateAccount {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.extra_account_meta_list.to_account_info(),},).with_signer(signer_seeds),lamports,account_size,ctx.program_id,)?;// 使用额外账户初始化 ExtraAccountMetaList 账户ExtraAccountMetaList::init::<ExecuteInstruction>(&mut ctx.accounts.extra_account_meta_list.try_borrow_mut_data()?,&account_metas,)?;Ok(())
}
这里有很多内容,让我们逐步理解:
- 首先我们声明一个
ExtraAccountMeta
账户的向量 (account_metas
),指定我们将使用的不同额外账户。这些可以从种子、公钥等指定。在我们的例子中,我们只需要一个额外账户——鲸鱼详细信息账户。 - 我们计算账户大小,并基于此计算租金所需的 lamports 数量。
- 我们指定需要签名的种子为
extra_account_meta_list
PDA。 - 我们调用
create_account
CPI 实际创建extra_account_meta_list
账户,提供所有需要的数据(付款人、签名者种子、账户大小等)。 - 最后,我们用在步骤 1 中声明的额外账户向量初始化新创建的账户。
实现 transfer_hook 指令
我们达到了程序的核心——transfer_hook
指令!这是实际操作发生的地方,因为每当进行交易时都会调用此指令。
- 我们首先为指令添加传入账户。在 lib.rs 中
InitializeExtraAccountMeta
下方添加以下内容:
#[derive(Accounts)]
pub struct TransferHook<'info> {#[account(token::mint = mint, token::authority = owner)]pub source_token: InterfaceAccount<'info, TokenAccount>,pub mint: InterfaceAccount<'info, Mint>,#[account(token::mint = mint)]pub destination_token: InterfaceAccount<'info, TokenAccount>,/// CHECK: source token account owner, /// can be SystemAccount or PDA owned by another program pub owner: UncheckedAccount<'info>,/// CHECK: ExtraAccountMetaList Account,#[account(seeds = [b"extra-account-metas", mint.key().as_ref()],bump)]pub extra_account_meta_list: UncheckedAccount<'info>,#[account(mut, seeds=[b"whale_account"], bump)]pub latest_whale_account: Account<'info, WhaleAccount>,
}
🧑🏫 提示:
- 这里指定的账户顺序很重要:
- 前四个账户是代币转账所需的账户(源、铸造、目标和所有者——按此顺序),
- 第五个账户是
ExtraAccountMetaList
账户的地址。 - 剩余的账户是
ExtraAccountMetaList
账户所需的额外账户。
- 注意我们在这里传递的约束——它们是为了帮助我们确保传递的账户确实是我们期望的账户(即正确的铸造账户,正确的所有者等)。
- 现在我们可以继续进行实际指令。在
initialize_extra_account
指令下方添加以下代码片段:
pub fn transfer_hook(ctx: Context<TransferHook>, amount: u64) -> Result<()> {msg!(&format!("Transfer hook fired for an amount of {}", amount));if amount >= 1000 * (u64::pow(10, ctx.accounts.mint.decimals as u32)) {// 我们有一个鲸鱼!ctx.accounts.latest_whale_account.whale_address = ctx.accounts.owner.key();ctx.accounts.latest_whale_account.transfer_amount = amount;emit!(WhaleTransferEvent {whale_address: ctx.accounts.owner.key(),transfer_amount: amount});}Ok(())
}
🦁 这里没有太多疯狂的事情:
- 我们检查转账金额是否大于或等于 1000 代币(这是我选择的任意金额,你可以选择其他金额)。
- 如果金额确实是 1000 或更多,我们更新鲸鱼详细信息账户的新地址和金额。
- 最后,我们发出一个名为
WhaleTransferEvent
的事件,供客户端(例如 Discord 机器人、Web 应用程序等)监听。
- 在 lib.rs 底部添加实际的事件声明:
#[event]
pub struct WhaleTransferEvent {pub whale_address: Pubkey,pub transfer_amount: u64,
}
实现 fallback 指令
最后但同样重要的是,fallback
指令。在 transfer_hook
指令之后添加以下代码片段:
pub fn fallback<'info>(program_id: &Pubkey,accounts: &'info [AccountInfo<'info>],data: &[u8],
) -> Result<()> {let instruction = TransferHookInstruction::unpack(data)?;// 匹配指令识别符以执行 transfer hook 接口指令// token2022 程序在代币转移时调用此指令match instruction {TransferHookInstruction::Execute { amount } => {let amount_bytes = amount.to_le_bytes();// 在我们的程序中调用自定义 transfer hook 指令__private::__global::transfer_hook(program_id, accounts, &amount_bytes)}_ => return Err(ProgramError::InvalidInstructionData.into()),}
}
fallback 指令取自 Solana 的hello-world transfer hook 示例 。
虽然看起来很复杂,但这个指令其实相当简单:
- 解包指令数据并将其转换为
TransferHookInstruction
。 - 匹配
TransferHookInstruction
枚举。 - 如果是
Execute
指令,获取转移的金额并调用transfer_hook
指令。 - 如果不是
Execute
指令,则返回无效指令数据的错误。
继续将程序部署到 Devnet 🎉
第 2 步:铸造
好的,我们的程序已经部署,现在我们可以将注意力转向创建一个实际使用它的代币铸造。
- 使用以下命令创建一个铸造:
spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --transfer-hook <your transfer hook program id>
🧑🏫 提示:
- 注意命令中的程序 id?那是 Token 2022 程序的地址。我们必须指定它以便铸造实际使用新标准。
- 为铸造注册 transfer hook 就像在
spl-token
命令中添加--transfer-hook <address>
子命令一样简单。
- 为你配置的钱包创建代币账户:
spl-token create-account <the token address from previous step>
- 向你的钱包铸造一些代币:
spl-token mint <the token address from previous step> 3000
第 3 步:初始化 ExtraAccountMeta 账户
如果你尝试转移代币(例如使用 spl-token transfer
命令),你将遇到 AccountNotFound
错误。这是因为我们从未初始化我们的老朋友 ExtraAccountMetaList
我们在程序中创建了一个指令来初始化账户,initialize_extra_account
,所以现在是调用它的最佳时机。
我创建了这个小的 TypeScript 代码来调用它,但你可以随意使用任何你喜欢的方法:
import { readFile } from "fs/promises";
import * as anchor from "@coral-xyz/anchor";
// 这两个文件是从构建的 Anchor 程序 Target/idl 和 Target/types 文件夹中复制的。
import { TransferHookWhale } from "./program/transfer_hook_whale";
import idl from './program/transfer_hook_whale.json';import { TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";
import "dotenv/config";const kpFile = ".<your keypair file>";
const mint = new anchor.web3.PublicKey("<your mint public address>")const main = async () => {if (!process.env.SOLANA_RPC) {console.log("Missing required env variables");return;}console.log("💰 Reading wallet...");const keyFile = await readFile(kpFile);const keypair: anchor.web3.Keypair = anchor.web3.Keypair.fromSecretKey(new Uint8Array(JSON.parse(keyFile.toString())));const wallet = new anchor.Wallet(keypair);console.log("☕️ Setting provider and program...");const connection = new anchor.web3.Connection(process.env.SOLANA_RPC);const provider = new anchor.AnchorProvider(connection, wallet, {});anchor.setProvider(provider);const program = new anchor.Program<TransferHookWhale>(idl as TransferHookWhale, provider);console.log("🪝 Initializing transfer hook accounts");const [extraAccountMetaListPDA] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from("extra-account-metas"), mint.toBuffer()],program.programId);const [whalePDA] = anchor.web3.PublicKey.findProgramAddressSync([Buffer.from("whale_account")], program.programId);const initializeExtraAccountMetaListInstruction = await program.methods.initializeExtraAccount().accounts({mint,extraAccountMetaList: extraAccountMetaListPDA,latestWhaleAccount: whalePDA,systemProgram: anchor.web3.SystemProgram.programId,tokenProgram: TOKEN_2022_PROGRAM_ID,associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,}).instruction();const transaction = new anchor.web3.Transaction().add(initializeExtraAccountMetaListInstruction);const tx = await anchor.web3.sendAndConfirmTransaction(connection, transaction, [wallet.payer], {commitment: "confirmed",});console.log("Transaction Signature:", tx);
}main().then(() => {console.log("done!");process.exit(0);
}).catch((e) => {console.log("Error: ", e);process.exit(1);
});
💈 完整项目在GitHub
第 4 步:让转移开始!
就是这样,朋友们!随着 transfer hook 程序的部署,配置使用它的铸造,以及 ExtraAccountMetaList
账户的初始化,我们终于可以开始使用我们的 hook 了:
transfer hook 正在运行,更多相关信息,,https://t.me/gtokentool
相关文章:

Solana 代币 2022 — Transfer Hook
从零到英雄的 Solana 代币 2022 — Transfer Hook Token 2022 计划引入了几项令人兴奋的扩展,增强了铸造和代币账户的功能。在这些功能中,我个人最喜欢的是Transfer Hook (转账钩子) 。 想象时间 让我们戴上想象的帽子…...
网络爬虫中的反爬虫技术:突破限制,获取数据
,网络爬虫已成为获取大量网络数据的重要工具。然而,随着爬虫技术的发展,许多网站也开始采用各种反爬虫措施来保护自己的数据。作为爬虫开发者,我们需要不断更新我们的技术,以应对这些反爬虫措施。本文将详细介绍一些常…...
【ROS2】cv_bridge:ROS图像消息和OpenCV的cv::Mat格式转换库
1、简述 cv_bridge可以实现ROS图像消息(sensor_msgs::msg::Image)和OpenCV的cv::Mat格式的转换。 cv_bridge支持各种常见的图像编码格式,包括JPEG、PNG、BMP等。 2、互转 1)cv::Mat转sensor_msgs::Image cv::Mat image; sensor_msgs::ImagePtr pMsg = cv_bridge::CvIma…...

【Web.路由】——URL生成
前几篇文章介绍了路由的相关知识,包括原理,模板和约束。围绕的一个点就是URL,那么URL是如何生成的呢? 在 ASP.NET CORE应用程序中,使用了一个叫 LinkGenerator的链接生成器来生成URL的。 LinkGenerator是一个中间件组件…...
使用 Java 实现从搜索引擎批量下载图片
在进行一些数据收集、图像处理或研究工作时,我们可能需要从网络上批量下载高质量的图片。本文将介绍如何使用 Java 和 Jsoup 库,从搜索引擎中抓取图片,解析详情页并过滤出高质量图片。通过以下几个步骤,您可以自动化这个图片收集的…...
基于Matlab GUI的说话人识别测试平台
基于Matlab GUI的说话人识别测试平台 摘 要:为了克服在Matlab中语音处理工具箱的不足,设计出基于Matlab图形用户界面(GUI)的说话人识别测试平台。系统框架设计:特征参数采用美尔倒谱系数及差分美尔倒谱系数,…...

Leetcode 热题100之二叉树2
1.二叉树的层序遍历 思路分析:层序遍历是逐层从左到右访问二叉树的所有节点,通常可以使用广度优先搜索(BFS)来实现。我们可以使用一个队列(FIFO)来存储每一层的节点,并逐层访问。 初始化队列&a…...

<项目代码>YOLOv8 煤矸石识别<目标检测>
YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…...

GA/T1400视图库平台EasyCVR视频分析设备平台微信H5小程序:智能视频监控的新篇章
GA/T1400视图库平台EasyCVR是一款综合性的视频管理工具,它兼容Windows、Linux(包括CentOS和Ubuntu)以及国产操作系统。这个平台不仅能够接入多种协议,还能将不同格式的视频数据统一转换为标准化的视频流,通过无需插件的…...

LVM与磁盘配额
文章目录 LVM与磁盘配额1 LVM概述1.1 名词解释1.2 LVM优势 2 LVM相关命令2.1 创建逻辑卷过程2.2 对逻辑卷扩容 3 磁盘配额3.1 磁盘配额的特点3.2 磁盘配额的命令3.3 查看配额使用情况3.4 验证磁盘配额3.5 实验 LVM与磁盘配额 1 LVM概述 1.1 名词解释 LVM:logical…...

xmuoj [蒙德里安的梦想] 状压dp个人笔记
本题是状压dp经典题目,很多人都是通过这一题开始对状压dp有所了解。 在进行讲解之前,我们先通过几个问答大致了解状压dp。 一、问答 1. 问题:什么是状压dp? 回答:状压dp即为状态压缩动态规划,何为状态压缩&#x…...
ubuntu22安装搜狗输入法不能输入中文
关闭Wayland 在/etc/gdm3/custom.conf文件内,取消注释WaylandEnable cat /etc/gdm3/custom.conf | grep WaylandEnable WaylandEnablefalse 其它步骤参考搜狗官方教程 https://pinyin.sogou.com/linux/help.php...

HtmlAgilityPack 操作详解
目录 1.安装 HtmlAgilityPack 2. 示例 HTML 3. 使用 HtmlAgilityPack 进行 HTML 解析与操作 4. 代码详解 1.加载html文档 2.选择元素 3. 提取属性 4.修改属性 5.常用的几种获取元素的 XPath 写法 HtmlAgilityPack: 轻量且高效,适合进行常规的 H…...

基于SSM医院门诊互联电子病历管理系统的设计
管理员账户功能包括:系统首页,个人中心,用户管理,医生管理,项目分类管理,项目信息管理,预约信息管理,检查信息管理,系统管理 用户账号功能包括:系统首页&…...

【读书笔记/深入理解K8S】集群网络
前言 上一章讲了集群控制器的一个大概的原理,这一章讲一下集群网络。网络是集群通信的载体,因为该书是阿里云团队出品的,所以也以阿里云的集群网络方案为例,其他云厂商的网络集群方案一般来说也大同小异。所以通过本章的学习&…...

【专有网络VPC】连接公网
通过ECS实例固定公网IP、弹性公网IP、NAT网关、负载均衡使专有网络中的云资源可以访问公网(Internet)或被公网访问。 概述 专有网络是您自定义的云上私有网络。专有网络中的云资源默认无法访问公网,也无法被公网访问。您可以通过配置ECS实例…...

论文 | Legal Prompt Engineering for Multilingual Legal Judgement Prediction
这篇文章探讨了如何利用“法律提示工程”(LPE)来指导大型语言模型(LLM)进行多语言法律判决预测(LJP)。主要内容: LPE 的概念: LPE 是指通过设计特定的提示(promp…...
国科安芯抗辐照MCU和CANFD芯片发布
国科安芯科技有限公司近期发布了两款重要的芯片产品:抗辐照MCU芯片和抗辐照CANFD芯片。这两款芯片的发布标志着国科安芯在高性能、高安全性芯片产品研制方面取得了显著进展,特别是在抗辐照技术领域。 1. 抗辐照MCU芯片:国科安芯研发的AS32A4…...
C++ 并发专题 - 无锁数据结构(概述)
一:概述: 无锁数据结构是一种在多线程环境中实现线程安全的结构,它允许多个线程在没有传统锁机制的情况下并发访问和修改数据。这种设计的目标是提高程序的性能和响应性,避免锁竞争和上下文切换的开销。 二:原理&…...
NLP领域的经典算法和模型
在自然语言处理(NLP)领域,经典算法和模型众多,它们在不同任务中发挥着重要作用。以下是一些NLP领域的经典算法和模型的详细介绍: 一、基础模型 词袋模型(Bag of Words,BoW) 原理&a…...

每日Prompt:治愈动漫插画
提示词 现代都市治愈动漫插画风格,现代女子,漂亮,长直发,20岁,豆沙唇,白皙,气质,清纯现代都市背景下,夕阳西下,一位穿着白色露脐短袖,粉色工装裤…...

我爱学算法之—— 前缀和(中)
一、724. 寻找数组的中心下标 题目解析 这道题,给定数组nums,要求我们找出这个数组的中心下标。 **中心下标:**指左侧所有元素的和等于右侧所有元素的和。 如果存在多个中心数组下标,就返回最左侧的中心数组下标。 算法思路 暴…...
kubernetes jenkins pipeline优化拉取大仓库性能指定分支+深度
有时候我们历史的git仓库,提交了某个比较大的文件如果不限制 depth ,就会拉取所有的历史提交记录,这样在历史仓库比较大的时候 clone 非常之慢,而实际上我们只需要最新的代码来构建就行了,为了优化性能,我们…...

element-plus 单选组件 el-radio,选不上,又没报错,直接复制官网也不行解决方案
在使用 Vue 框架开发项目时,Element UI 是常用的组件库。最近在开发中遇到了 Element 单选框组件el-radio的双向绑定问题,直接复制element官网上的的案例下来也是不得,经过调试和探索,终于找到了解决方案,特此记录分享…...
ArkUI-X与Android桥接通信之消息通信
平台桥接用于客户端(ArkUI)和平台(Android或iOS)之间传递消息,即用于ArkUI与平台双向数据传递、ArkUI侧调用平台的方法、平台调用ArkUI侧的方法。本文主要介绍Android平台与ArkUI交互,ArkUI侧具体用法请参考…...
三级流水线是什么?
三级流水线是什么? “三级流水线” 英文名:Three-Stage Pipeline 或 Basic 3-Stage Pipeline,是计算机处理器(CPU)设计中一种基本的指令流水线技术,它将指令的执行过程划分为三个主要阶段,使得…...
RKNN开发环境搭建1-基于Ubuntu 18.04系统使用Docker安装rknn-toolkit2
目录 写在最前面Docker 方式安装rknn-toolkit2写在最前面 瑞芯微在RKNN的环境搭建方面的资料很多,但是在搭建过程中发现很多问题教程中并未提及,对初学者不友好。所以博主做了这个系列的文章,从开始搭建环境到对于RKNN Model Zoo的示例进行实践,希望能对初学者有帮助。坚持…...
元图CAD:一键解锁PDF转CAD,OCR技术赋能高效转换
在建筑、工程与制造领域,图纸的精准性与高效协作是项目成功的关键。然而,传统PDF文件中的文字和图形往往难以直接编辑,手动输入不仅耗时易错,还可能因格式问题导致信息丢失。元图CAD凭借创新的OCR文字识别技术,重新定义…...

Linux--进程的调度
1.进程切换 CPU上下⽂切换:其实际含义是任务切换, 或者CPU寄存器切换。当多任务内核决定运⾏另外的任务时, 它保存正在运⾏任务的当前状态, 也就是CPU寄存器中的全部内容。这些内容被保存在任务⾃⼰的堆栈中, ⼊栈⼯作完成后就把下⼀个将要运⾏的任务的当前状况从该…...
将HTML内容转换为Canvas图像,主流方法有效防止文本复制
HTML to Canvas 使用说明 项目概述 此项目实现了将HTML内容转换为Canvas图像的功能,可有效防止文本被复制。适用于需要保护内容的场景,如试题系统、付费内容等。 主要功能 防止复制: 将文本内容转换为Canvas图像,使用户无法选择和复制Mat…...