Solana 代币 2022 — Transfer Hook

news2024/11/5 16:36:17

从零到英雄的 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 指令的作用。

在完成我们程序的高层设计后,让我们开始实际编写转账钩子程序 🎉。

  1. 创建一个新的 Anchor 项目并在你喜欢的 IDE 中打开它:
anchor init transfer-hook-whale
  1. 安装 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
  1. 从 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"]
  1. 打开 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 指令

  1. 在你的 lib.rs 底部,添加以下账户。此账户将保存最新“鲸鱼”转账的详细信息:
#[account]
pub struct WhaleAccount {
    pub whale_address: Pubkey,
    pub transfer_amount: u64
}
  1. 接下来,我们定义 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 的字节。这意味着我们铸造的所有代币将共享同一个账户。
  • 我们必须为指令提供代币程序、关联代币程序和系统程序,因为它在运行时需要使用它们。
  1. 接下来,让我们添加 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(())
}

这里有很多内容,让我们逐步理解:

  1. 首先我们声明一个 ExtraAccountMeta 账户的向量 (account_metas),指定我们将使用的不同额外账户。这些可以从种子、公钥等指定。在我们的例子中,我们只需要一个额外账户——鲸鱼详细信息账户。
  2. 我们计算账户大小,并基于此计算租金所需的 lamports 数量。
  3. 我们指定需要签名的种子为 extra_account_meta_list PDA。
  4. 我们调用 create_account CPI 实际创建 extra_account_meta_list 账户,提供所有需要的数据(付款人、签名者种子、账户大小等)。
  5. 最后,我们用在步骤 1 中声明的额外账户向量初始化新创建的账户。

实现 transfer_hook 指令

我们达到了程序的核心——transfer_hook 指令!这是实际操作发生的地方,因为每当进行交易时都会调用此指令。

  1. 我们首先为指令添加传入账户。在 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 账户所需的额外账户。
  • 注意我们在这里传递的约束——它们是为了帮助我们确保传递的账户确实是我们期望的账户(即正确的铸造账户,正确的所有者等)。
  1. 现在我们可以继续进行实际指令。在 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 应用程序等)监听。
  1. 在 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 示例 。

虽然看起来很复杂,但这个指令其实相当简单:

  1. 解包指令数据并将其转换为 TransferHookInstruction
  2. 匹配 TransferHookInstruction 枚举。
  3. 如果是 Execute 指令,获取转移的金额并调用 transfer_hook 指令。
  4. 如果不是 Execute 指令,则返回无效指令数据的错误。

继续将程序部署到 Devnet 🎉

第 2 步:铸造

好的,我们的程序已经部署,现在我们可以将注意力转向创建一个实际使用它的代币铸造。

  1. 使用以下命令创建一个铸造:
spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --transfer-hook <your transfer hook program id>

🧑‍🏫 提示

  • 注意命令中的程序 id?那是 Token 2022 程序的地址。我们必须指定它以便铸造实际使用新标准。
  • 为铸造注册 transfer hook 就像在 spl-token 命令中添加 --transfer-hook <address> 子命令一样简单。
  1. 为你配置的钱包创建代币账户:
spl-token create-account <the token address from previous step>
  1. 向你的钱包铸造一些代币:
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 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2230881.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Edge浏览器设置优化

依次点击右上角的三个点-“设置”。 在“外观”设置项中&#xff0c;关闭“显示选项卡操作菜单”、“在垂直选项卡中隐藏标题栏”、“在标题栏中显示个人资料图标”&#xff0c;选择“不显示独立搜索框”。 在“选择要在工具栏上显示的按钮”&#xff0c;开启“下载按钮”。 重…

电脑软件:推荐一款免费且实用的电脑开关机小工具

目录 一、软件简介 二、软件功能 三、软件特点 四、使用说明 五、软件下载 今天给大家推荐一款免费且实用的电脑开关机小工具KShutdown&#xff0c;有需要的朋友可以下载试一下&#xff01; 一、软件简介 KShutdown是一款精巧且实用的定时自动关机小工具&#xff0c;对于…

Python Matplotlib 子图绘制

Python 中的子图绘制 在数据可视化中&#xff0c;展示多个图表在同一个画布上是常见的需求&#xff0c;这样可以更直观地比较不同数据集之间的关系。Python 中的 Matplotlib 库为我们提供了强大的功能来实现这一点。在本篇文章中&#xff0c;我们将详细介绍如何使用 Matplotli…

透明加密技术是什么?透明加密技术的原理与应用实践(内含代表性软件分享)

触目惊心&#xff01;10大典型间谍案例回顾 张某离职前搜集大量文件资料&#xff0c;甚至拆开电脑主机拷贝文件 私自存有5200份文件资料 其中标注绝密级的59份 机密级848份 秘密级541份 在当今这个信息化高速发展的时代&#xff0c;透明加密技术已不容忽视。那么&#xff…

rhce:web服务器

web服务器简介 服务器端&#xff1a;此处使用 nginx 提供 web 服务&#xff0c; RPM 包获取&#xff1a; http://nginx.org/packages/ /etc/nginx/ ├── conf.d #子配置文件目录 ├── default.d ├── fastcgi.conf ├── fastcgi.conf.default ├── fastcgi_params #用…

后端:Spring、Spring Boot-实例化Bean依赖注入(DI)

文章目录 1. 实例化Bean2. 使用FactoryBean3. 依赖注入(DI)3.1 AutoWired 属性注入(查找顺序&#xff1a;先类型&#xff0c;后名字)3.2 AutoWired 在构造函数&参数上的使用3.3 Inject和Resource 进行依赖注入3.4 Value 进行注入 1. 实例化Bean 默认使用无参构造函数&…

Android——横屏竖屏

系统配置变更的处理机制 为了避免横竖屏切换时重新加载界面的情况&#xff0c;Android设计了一中配置变更机制&#xff0c;在指定的环境配置发生变更之时&#xff0c;无需重启活动页面&#xff0c;只需执行特定的变更行为。该机制的视线过程分为两步&#xff1a; 修改 Androi…

ubuntu openmpi安装(超简单)

openmpi安装 apt update apt install openmpi-bin openmpi-common libopenmpi-dev安装到此完毕 测试一下&#xff0c;success !

基于DCT的数字水印算法

摘要 数字水印技术近年来得到了较大的发展&#xff0c;基于变换域的水印技术是目前研究的热点。数字水印是利用数字作品中普遍存在的冗余数据和随机性&#xff0c;把标识版权的水印信息嵌入到数字作品中&#xff0c;从而可以起到保护数字作品的版权或其完整性的一种技术。 一个…

【Linux指令】---获取进程的PID

获取进程的PID getpid()函数

李红《复变函数与积分变换》第五版课后习题答案PDF

《复变函数与积分变换(第五版)学习辅导与习题全解》是与《复变函数与积分变换(第五版)》(华中科技大学数学与统计学院)配套的学习辅导书&#xff0c; 全书共八章&#xff1a;复数与复变函数&#xff0c; 解析函数&#xff0c;复变函数的积分&#xff0c; 解析函数的级数表示&am…

Zypher Network:全栈式 Web3 游戏引擎,服务器抽象叙事的引领者

近期&#xff0c;《黑神话&#xff1a;悟空》的爆火不仅让 AAA 游戏重回焦点&#xff0c;也引发了玩家与开发者的热议。Web2 游戏的持续成功导致部分 Web3 玩家们的倒戈&#xff0c;对比之下 Web3 游戏存在生命周期短且商业模式难以明确的问题&#xff0c;尤其在当前加密市场环…

【Linux】利用 <信号量> 实现 <生产者-消费者模型-线程同步 >(思维导图&代码演示&思路解析)

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Lin…

「Mac畅玩鸿蒙与硬件20」鸿蒙UI组件篇10 - Canvas 组件自定义绘图

Canvas 组件在鸿蒙应用中用于绘制自定义图形,提供丰富的绘制功能和灵活的定制能力。通过 Canvas,可以创建矩形、圆形、路径、文本等基础图形,为鸿蒙应用增添个性化的视觉效果。本篇将介绍 Canvas 组件的基础操作,涵盖绘制矩形、圆形、路径和文本的实例。 关键词 Canvas 组件…

spark-on-k8s 介绍

spark-on-k8s 介绍 摘要 最近一段时间都在做与spark相关的项目&#xff0c;主要是与最近今年比较火的隐私计算相结合&#xff0c;主要是在机密计算领域使用spark做大数据分析、SQL等业务&#xff0c;从中也了解到了一些spark的知识&#xff0c;现在做一个简单的总结&#xff…

【运动的&足球】足球场地区域图像分割系统源码&数据集全套:改进yolo11-RFAConv

改进yolo11-ContextGuidedDown等200全套创新点大全&#xff1a;足球场地区域图像分割系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.28 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展…

C语言 | Leetcode C语言题解之第525题连续数组

题目&#xff1a; 题解&#xff1a; struct HashTable {int key, val;UT_hash_handle hh; };int findMaxLength(int* nums, int numsSize) {int maxLength 0;struct HashTable* hashTable NULL;struct HashTable* tmp malloc(sizeof(struct HashTable));tmp->key 0, tm…

Java JUC(四) 自定义线程池实现与原理分析

目录 一. 阻塞队列 BlockingQue 二. 拒绝策略 RejectPolicy 三. 线程池 ThreadPool 四. 模拟运行 在 Java基础&#xff08;二&#xff09; 多线程编程 中&#xff0c;我们简单介绍了线程池 ThreadPoolExecutor 的核心概念与基本使用。在本文中&#xff0c;我们将基于前面学…

金华迪加 现场大屏互动系统 mobile.do.php 任意文件上传漏洞复现

0x01 产品简介 金华迪加现场大屏互动系统是一种集成了先进技术和创意设计的互动展示解决方案,旨在通过大屏幕和多种交互方式,为观众提供沉浸式的互动体验。该系统广泛应用于各类活动、展览、会议等场合,能够显著提升现场氛围和参与者的体验感。 0x02 漏洞概述 金华迪加 现…

2024年系统架构师---下午题目真题

1. 数据仓库架构风格的优缺点&#xff1a; 优点&#xff1a; 1&#xff09;数据统一保存在中央数据仓库&#xff0c;数据处理流程相对独立&#xff0c;支持交互式处理。 缺点&#xff1a; 1&#xff09;仓库风格不支持并行&#xff0c;效率低。 2&#xff09;仓库风格容错性和健…