小试牛刀-区块链Solana多签账户

目录

1.什么是多签账户

2.多签账户的特点

2.1 多个签名者

2.2 最小签名要求

2.3 常见应用场景

3.多签账户实现

3.1 账户的创建

3.1.1 创建新账户

3.1.2 获取创建和初始账户事务

3.1.3 账户的签名

3.2 代币转移操作


Welcome to Code Block's blog

本篇文章主要介绍了

[小试牛刀-Solana多签账户]
❤博主广交技术好友,喜欢文章的可以关注一下❤

1.什么是多签账户

        在 Solana 区块链中,多签账户(Multisig Account)是一种智能合约账户,允许多个签名者共同管理和控制账户上的资产或操作。这种机制增强了账户的安全性和灵活性,特别适用于需要多个权限共同批准的操作场景,如资产管理、资金转移、或项目治理。                               

2.多签账户的特点

2.1 多个签名者

        多签账户通常指定多个签名者(即一组公钥),这些签名者可以是个人账户、智能合约账户或其他实体。

        每个签名者都有权批准账户的操作,但只有在达到预定义的签名数量时,操作才会生效。

2.2 最小签名要求

        多签账户设置了最小签名数(M)的要求,即在 N 个签名者中,至少需要 M 个签名才能执行账户的任何操作。

        这种机制通常被称为 M-of-N 签名方案,例如 2-of-3 签名意味着需要三个人中的两个人签署才能批准操作。

2.3 常见应用场景

        项目治理:在去中心化自治组织(DAO)中,使用多签账户来管理资金或项目决策。

        资产托管:增加资金管理的安全性,避免单一账户被攻击或失控。

        合作项目:需要多个合作方共同控制账户,确保所有操作都有多个利益相关方的同意。

3.多签账户实现

3.1 账户的创建

3.1.1 创建新账户

        首先创建一个新的账户,作为要创建多签账户,同时需要其公钥和私钥参与签名,实现代码如下:

function generateSolanaWalletNacl(): { publicKey: string, privateKey: string } {const keyPair = nacl.sign.keyPair();const publicKeyBase58 = bs58.encode(Buffer.from(keyPair.publicKey)).toString();const privateKeyBase58 = bs58.encode(Buffer.from(keyPair.secretKey)).toString();return { publicKey: publicKeyBase58, privateKey: privateKeyBase58 };
}

3.1.2 获取创建和初始账户事务

        这里multisigPubKey为上面创建的新账户的公钥,payer我们使用另一个账户(已有或创建)的公钥,保证payer中存在可以创建账户的费用,然后获取创建和初始化事务,代码如下:

export async function createMultisigAccount(payer:string,multisigPubKey:PublicKey): Promise<Transaction> {const fromPubkey =new PublicKey(payer);//签名者列表const signers = [fromPubkey,multisigPubKey];// 获取多签账户所需的最小余额以免租金const lamports = await getMinimumBalanceForRentExemptMultisig(connection);// 创建多签账户的指令const createAccountInstruction = SystemProgram.createAccount({//这个账户的拥有者fromPubkey: fromPubkey,//多签账户公钥newAccountPubkey: multisigPubKey,//免租金最小金额lamports,space: 355, // 多签账户的空间大小//属于的程序地址,这里使用SPL程序programId: TOKEN_PROGRAM_ID,});// 初始化多签账户的指令const initializeMultisigInstruction = createInitializeMultisigInstruction(//多签账户multisigPubKey,//签名者列表signers,//最小签名数量,这里是2requiredSigners,//属于的程序地址,这里使用SPL程序TOKEN_PROGRAM_ID);//创建transaction并添加创建和初始化指令const transaction = new Transaction().add(createAccountInstruction,initializeMultisigInstruction);//最近的区块hashtransaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;//付费者为拥有者transaction.feePayer=fromPubkey;console.log("签名者:"+signers)console.log("multisigPubKey:"+multisigPubKey)return transaction;
}

3.1.3 账户的签名

        账户创建时需要签名者列表中的用户分别对transaction进行签名,(是对同一transaction进行签名),如果是拥有者公私钥在本地,可以将公私钥生成Keypair,可使用以下方式签名:

const signer1=Keypair.generate()
const signer2=Keypair.generate()
//签名者1和签名者2 的keypair
transaction.sign(signer1,signer2)

        但若是外部签名,如walletconnect连接的钱包用户作为签名者,则需要使用以下方式进行签名(重点):

首先,获取本地公私钥的签名,注意这里并没有对transaction进行签名,只是获取签名:

export async function getLocalSign(transaction:Transaction,signer:Keypair){const signature = nacl.sign.detached(transaction.serializeMessage(), signer.secretKey);return signature;
}

然后,walletconnet用户连接钱包对创建和初始化账户事务签名后,会获取到一个signature,将这个signature和本地公私钥获取的signature添加到transaction中,代码如下:

    //钱包签名const result =await sendWcTransaction(signClient,session,transaction);//本地签名const signature = nacl.sign.detached(transaction.serializeMessage(), bs58.decode(privateKey));transaction.addSignature(transaction.feePayer,//这里返回的是字符串,所以需要转换Buffer.from(bs58.decode(result.signature)))//<>!transaction.addSignature(multisigPubKey,//本地获取的是uint8array,无需转换Buffer.from(signature))

为保证签名的有效性,需要先验证签名,验证方式如下:

    //验证签名const valid = transaction.verifySignatures();if (!valid){//验证未通过则不进行操作,或打印return ;}

验证通过后,就可以通过以下代码将要进行的创建事务提交到链上:

export async function sendTransaction(transaction:Transaction){try{const txId =await connection.sendRawTransaction(transaction.serialize())return txId;}catch(error){console.error('Exception occurred:', error);return 'error';}
}

链上截图:

可以看到这里创建并初始化了一个Multisig账户,即多签账户,并且Signers有两个地址,这样就完成了多签账户的创建.              

3.2 代币转移操作

        多签账户操作的转移和创建类似,同样需要两个或多个账户对transaction进行签名,获取transaction返回的[transaction,senderKeypair]分别是事务和本地的keyPair,方便后续签名使用,代码如下:

export async function getMultisigTransferTransaction(ownerPublicKey:string,senderPublicKey: string,privateKey: string,drawPublicKey: string,tokenAmount: number,drawData: string
): Promise<[Transaction,Keypair]> {const ownerPubKey = new PublicKey(ownerPublicKey);const senderPubkey = new PublicKey(senderPublicKey);const drawPubkey = new PublicKey(drawPublicKey);const tokenMintAddress = BOGGY_TOKEN_MINT;const senderKeypair = Keypair.fromSecretKey(Uint8Array.from(bs58.decode(privateKey)));try {const sourceTokenAccount = await getAssociatedTokenAddress(tokenMintAddress,senderPubkey);const destTokenAccount = await getAssociatedTokenAddress(tokenMintAddress,drawPubkey);const transferInstruction = createTransferInstruction(sourceTokenAccount,destTokenAccount,senderPubkey,tokenAmount * 1e9,[senderPubkey,ownerPubKey],TOKEN_PROGRAM_ID);// 构造 Memo 指令const memoInstruction = new TransactionInstruction({keys: [{ pubkey: ownerPubKey,  isSigner: true , isWritable:false },{ pubkey: senderPubkey, isSigner: true, isWritable: false }],programId: MEMO_PROGRAM_ID,data: Buffer.from(drawData,'utf-8')});// 创建 compute unit price 指令,提高交易速度const computeUnitPriceInstruction = ComputeBudgetProgram.setComputeUnitPrice({microLamports: 7500,});const computeUnitLimitInstruction=ComputeBudgetProgram.setComputeUnitLimit({units:200000})const transaction = new Transaction().add(computeUnitPriceInstruction,computeUnitLimitInstruction,transferInstruction,memoInstruction);transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;transaction.feePayer = ownerPubKey;return [transaction,senderKeypair];} catch (error) {console.error('Exception occurred:', error);}
}

签名和发送的步骤和创建时签名是一样的,这里就不重复写了.

区块链内容感兴趣可以查看我的专栏:小试牛刀-区块链

感谢您的关注和收藏!!!!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode_sql_day16(601.体育馆的人流量)

描述&#xff1a;601. 体育馆的人流量 - 力扣&#xff08;LeetCode&#xff09; 编写解决方案找出每行的人数大于或等于 100 且 id 连续的三行或更多行记录。 返回按 visit_date 升序排列 的结果表。 输入Stadium表: ----------------------------- | id | visit_date | peop…

电子电气架构 --- 车辆模式管理

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

CUDA-MODE 第一课: 如何在 PyTorch 中 profile CUDA kernels

我的课程笔记&#xff0c;欢迎关注&#xff1a;https://github.com/BBuf/how-to-optim-algorithm-in-cuda/tree/master/cuda-mode 第一课: 如何在 PyTorch 中 profile CUDA kernels 这里是课程规划&#xff0c;有三位讲师 Andreas, Thomas, Mark&#xff0c;然后大概2周出一个 …

Elasticsearch:用例、架构和 6 个最佳实践

1. 什么是 Elasticsearch&#xff1f; Elasticsearch 是一个开源分布式搜索和分析引擎&#xff0c;专为处理大量数据而设计。它建立在 Apache Lucene 之上&#xff0c;并由Elastic 支持。Elasticsearch 用于近乎实时地存储、搜索和分析结构化和非结构化数据。 Elasticsearch 的…

4.3.2 C++ 平面拟合的实现

4.3.2 C 平面拟合的实现 参考教程&#xff1a; gaoxiang12/slam_in_autonomous_driving: 《自动驾驶中的SLAM技术》对应开源代码 (github.com) Eigen打印输出_打印eigen矩阵-CSDN博客 1. 编写 Plane fitting 1.1 创建文件夹 通过终端创建一个名为Plane_fitting的文件夹以保…

文件操作与IO(下)

✨个人主页&#xff1a; 不漫游-CSDN博客 目录 前言 流对象 InputStream OutputStream 运用 在控制台进行输入并写入文件 进行普通文件的复制 前言 之前的文章文件操作与IO&#xff08;上&#xff09;已经介绍了文件系统的相关操作&#xff0c;这次的主角是文件内容的相关…

SpringBoot 框架学习笔记(七):Thymeleaf、拦截器 和 文件上传实现(解决了文件重名 和 按日期分目录存放问题)

1 Thymeleaf 1.1 基本介绍 &#xff08;1&#xff09;官方文档&#xff1a;Tutorial: Using Thymeleaf &#xff08;2&#xff09;Thymeleaf 是什么 Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎&#xff0c;可完全替代 JSPThymeleaf 是一个 java 类库&#xf…

.net core webapi 自定义异常过滤器

1.定义统一返回格式 namespace webapi;/// <summary> /// 统一数据响应格式 /// </summary> public class Results<T> {/// <summary>/// 自定义的响应码&#xff0c;可以和http响应码一致&#xff0c;也可以不一致/// </summary>public int Co…

vue 打包时候的分包

export default defineConfig({plugins: [vue()],resolve: {alias: {: fileURLToPath(new URL(./src/, import.meta.url))}},// 分包&#xff0c;node_modules中的单独打包成名字为vendor的js文件build: {rollupOptions: {manualChunks(id) {if (id.includes(node_modules)) {r…

EF8 学习过程中的问题和解决方案

一、varchar类型字段如果为null 无法使用contains来判断是否包含字符串 1. 有问题的代码&#xff1a; contractList _dbcontext.contractHeads.Where(u > u.code.Contains(queryStr) || u.name.Contains(queryStr) || u.companyName.Contains(queryStr) || u.customerNa…

uniapp开启数据压缩的坑-SpringBoot-gzip

1、服务器配置 服务端开启的数据压缩配置 server:port: ${port:8881}servlet:# 应用上下文路径context-path: /orderserverundertow:threads:io: 4worker: 500buffer-size: 2048# 开启Gzip压缩&#xff0c;compression:# 开启压缩enabled: true# 对json格式内容进行压缩mime-…

KCTF 闯关游戏:1 ~ 7 关

前言 看雪CTF平台是一个专注于网络安全技术竞赛的在线平台&#xff0c;它提供了一个供网络安全爱好者和技术专家进行技术交流、学习和竞技的环境。CTF&#xff08;Capture The Flag&#xff0c;夺旗赛&#xff09;是网络安全领域内的一种流行竞赛形式&#xff0c;起源于1996年…

嵌入式全栈开发学习笔记---数据结构(排序算法)

目录 排序的分类 稳定排序与不稳定排序 内部排序和外部排序 算法的复杂性 常见的排序算法 直接插入排序 希尔排序 快速排序 简单选择排序 堆排序 归并排序 基数排序 常见的排序总结 到目前为止&#xff0c;数据结构的线性结构和树状结构就都讲完了&#xff0c;本节…

使用 MongoDB 构建 AI:Flagler Health 的 AI 旅程如何彻底改变患者护理

Flagler Health 致力于为慢性病患者提供支持&#xff0c;为其匹配合适的医生以提供合适的护理。 通常&#xff0c;身患严重病痛的患者面临的选择有限&#xff0c;他们往往需要长期服用阿片类药物&#xff0c;或寻求成本高昂的侵入性外科手术干预。遗憾的是&#xff0c;后一种方…

SQL语句创建数据库(增删查改)

SQL语句 一.数据库的基础1.1 什么是数据库1.2 基本使用1.2.1 连接服务器1.2.2 使用案例 1.2 SQL分类 二.库的操作2.1 创建数据库2.2 创建数据库示例2.3 字符集和校验规则2.3.1 查看系统默认字符集以及校验规则2.3.2查看数据库支持的字符集2.3.3查看数据库支持的字符集校验规则2…

Android系统Android.bp文件详解

文章目录 1. 基本语法结构2. 常见模块类型3. 模块属性常见属性包括&#xff1a; 4. 具体示例5. 高级功能5.1. 条件编译5.2. 变量定义与使用5.3. 模块继承 6. 总结 Android.bp 是 Android 构建系统&#xff08;Android Build System&#xff09;中的配置文件&#xff0c;用于描述…

go之命令行工具urfave-cli

一、urfave/cli urfave/cli 是一个声明性的、简单、快速且有趣的包&#xff0c;用于用 Go 构建命令行工具。 二、快速使用 2.1 引入依赖 go get github.com/urfave/cli/v2 2.2 demo package mainimport ("fmt""log""os""github.com/ur…

OpenCV图像滤波(9)getGaussianKernel()函数的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 功能描述 cv::getGaussianKernel() 是 OpenCV 中的一个函数&#xff0c;用于生成一维高斯核。这种核通常用于实现高斯模糊滤波器&#xff0c;该滤波器可以…

备考CISSP,看这一篇就够了!(附备考资料下载)

作者在2023年发布过一篇博文《不报辅导班一次性通过CISSP经验分享》&#xff0c;后台收到很多备考小伙伴的私信咨询&#xff0c;我就基于大家经常问的问题整理了此文章为大家答疑解惑&#xff0c;同时附上备考过程中作者收集到的全部资源&#xff08;见文末&#xff09;&#x…

EasyCVR视频汇聚平台云计算技术核心优势:高效、灵活与可扩展性深度解读

随着科技的飞速发展和社会的不断进步&#xff0c;视频监控已经成为现代社会治安防控、企业管理等场景安全管理中不可或缺的一部分。在这一背景下&#xff0c;EasyCVR视频汇聚平台凭借其强大的云计算技术&#xff0c;展现出了卓越的性能和广泛的应用前景。本文将深入解析EasyCVR…