乐闻世界logo
搜索文章和话题

面试题手册

什么是 DNS 及其工作原理

什么是 DNSDNS(Domain Name System,域名系统) 是互联网的一项核心服务,它作为将域名和 IP 地址相互映射的分布式数据库,使得用户可以通过易于记忆的域名(如 www.example.com)访问网站,而无需记住复杂的数字 IP 地址(如 192.0.2.1)。DNS 的工作原理DNS 解析过程通常包含以下步骤:1. 浏览器缓存检查浏览器首先检查自身缓存中是否有该域名的解析记录如果有且未过期,直接返回 IP 地址2. 操作系统缓存检查浏览器缓存未命中时,检查操作系统缓存(如 Windows 的 hosts 文件)Linux 系统会检查 /etc/hosts 文件3. 本地 DNS 服务器查询向配置的本地 DNS 服务器(通常是 ISP 提供或公司内部的 DNS 服务器)发起查询本地 DNS 服务器会检查自身缓存4. 递归查询过程如果本地 DNS 服务器没有缓存记录,会进行递归查询:步骤 A:根域名服务器查询本地 DNS 向根域名服务器(Root Name Server)查询根服务器返回负责该顶级域(TLD)的服务器地址步骤 B:顶级域服务器查询本地 DNS 向 TLD 服务器(如 .com、.org 服务器)查询TLD 服务器返回该域名的权威 DNS 服务器地址步骤 C:权威 DNS 服务器查询本地 DNS 向权威 DNS 服务器查询权威服务器返回最终的 IP 地址记录5. 结果返回与缓存本地 DNS 将结果返回给客户端客户端和本地 DNS 都会根据 TTL(生存时间)缓存该记录DNS 查询类型| 查询类型 | 说明 ||---------|------|| 递归查询 | DNS 服务器代替客户端完成全部查询工作 || 迭代查询 | DNS 服务器返回最佳答案,客户端继续查询 || 反向查询 | 通过 IP 地址查询对应的域名 |关键概念A 记录:将域名映射到 IPv4 地址AAAA 记录:将域名映射到 IPv6 地址CNAME 记录:域名别名记录MX 记录:邮件交换记录NS 记录:域名服务器记录TTL:生存时间,决定缓存有效期
阅读 0·3月6日 21:56

什么是区块链技术?请解释其核心特点和去中心化原理

什么是区块链技术?区块链(Blockchain) 是一种分布式账本技术,通过密码学方法将数据区块按时间顺序链接成链式结构,实现去中心化的数据存储和验证。核心特点去中心化(Decentralization)没有单一控制节点,数据分布在网络中的所有节点上每个节点都保存完整的账本副本消除了单点故障风险不可篡改性(Immutability)一旦数据被写入区块,几乎不可能被修改通过哈希指针和共识机制保证数据完整性任何篡改都会导致后续所有区块的哈希值变化透明性(Transparency)所有交易记录对网络参与者公开可见每个节点都可以验证交易的有效性提高了系统的信任度安全性(Security)使用密码学技术保护数据通过共识机制防止恶意攻击51%攻击难度极高去中心化原理传统中心化系统:用户 → 中心服务器 → 数据库区块链去中心化系统:用户A → 节点网络 ← 用户B ↓ ↓ ↓ 节点1 节点2 节点3 ↓ ↓ ↓ 完整账本副本(分布式存储)工作流程交易发起:用户创建并广播交易交易验证:网络节点验证交易合法性区块打包:矿工/验证者将交易打包成区块共识达成:网络通过共识机制确认区块区块添加:新区块被添加到链上,全网同步应用场景加密货币(Bitcoin、Ethereum)供应链管理数字身份认证智能合约去中心化金融(DeFi)面试要点理解分布式账本与中心化数据库的区别掌握哈希函数在区块链中的作用了解不同共识机制的优缺点能够解释拜占庭将军问题及其解决方案
阅读 0·3月6日 21:55

什么是智能合约?请解释 Solidity 智能合约的执行原理和常见安全漏洞

什么是智能合约?智能合约(Smart Contract) 是一种运行在区块链上的自动执行的计算机程序,当预设条件被满足时,合约会自动执行相应的操作,无需第三方中介。核心特点自动执行:条件触发后自动运行,无需人工干预不可篡改:部署后代码无法修改,保证执行结果可信透明公开:合约代码对所有网络参与者可见去信任化:通过代码而非信任来执行协议Solidity 智能合约执行原理1. 合约部署流程// 示例:简单的存储合约pragma solidity ^0.8.0;contract SimpleStorage { uint256 private storedData; event DataChanged(uint256 newValue); function set(uint256 x) public { storedData = x; emit DataChanged(x); } function get() public view returns (uint256) { return storedData; }}部署步骤:编译:Solidity 代码编译为 EVM 字节码交易创建:创建部署交易,包含字节码矿工打包:交易被矿工打包进区块合约创建:EVM 执行创建操作,分配合约地址状态存储:合约代码和初始状态存储在区块链上2. EVM 执行模型交易发起 → Gas 费用检查 → EVM 执行 ↓ 栈(Stack)操作 内存(Memory)操作 存储(Storage)操作 ↓ 状态更新或回滚 ↓ 交易收据生成Gas 机制:每笔操作消耗 Gas,防止无限循环Gas Price × Gas Limit = 最大交易费用Gas 不足时交易回滚,但已消耗的 Gas 不退还3. 合约调用方式| 调用方式 | 特性 | 使用场景 ||---------|------|---------|| call | 不创建新上下文,可指定 Gas | 调用外部合约 || delegatecall | 在当前合约上下文执行 | 代理合约模式 || staticcall | 只读调用,不修改状态 | 查询操作 |常见安全漏洞1. 重入攻击(Reentrancy Attack)漏洞代码:function withdraw() public { uint256 amount = balances[msg.sender]; require(amount > 0); (bool success, ) = msg.sender.call{value: amount}(""); // 外部调用 require(success); balances[msg.sender] = 0; // 状态更新在后}攻击原理:攻击者合约在接收 ETH 时触发 fallback 函数fallback 函数再次调用 withdraw形成递归调用,重复提取资金防护措施:function withdraw() public { uint256 amount = balances[msg.sender]; require(amount > 0); balances[msg.sender] = 0; // 先更新状态 (bool success, ) = msg.sender.call{value: amount}(""); require(success);}2. 整数溢出/下溢(Integer Overflow/Underflow)漏洞代码:function transfer(address to, uint256 amount) public { balances[msg.sender] -= amount; // 可能下溢 balances[to] += amount; // 可能溢出}防护措施:使用 Solidity 0.8.0+(内置溢出检查)或使用 SafeMath 库using SafeMath for uint256;function transfer(address to, uint256 amount) public { balances[msg.sender] = balances[msg.sender].sub(amount); balances[to] = balances[to].add(amount);}3. 访问控制漏洞漏洞代码:function destroy() public { selfdestruct(payable(msg.sender)); // 任何人可调用}防护措施:address public owner;modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _;}function destroy() public onlyOwner { selfdestruct(payable(owner));}4. 前端运行攻击(Front-running)攻击原理:攻击者监控内存池(mempool)中的待处理交易支付更高 Gas Price 抢先执行类似交易常见于 DEX 交易和拍卖场景防护措施:提交哈希承诺,延迟揭示使用提交-揭示模式(Commit-Reveal)安全最佳实践使用成熟的安全库:OpenZeppelin Contracts代码审计:部署前进行专业安全审计形式化验证:使用工具验证合约逻辑测试覆盖:编写全面的单元测试和集成测试权限最小化:遵循最小权限原则紧急暂停:实现可暂停功能应对紧急情况面试要点理解 EVM 的执行模型和 Gas 机制掌握重入攻击的原理和防护方法了解常见的安全漏洞类型熟悉 OpenZeppelin 等安全库的使用能够设计安全的合约架构
阅读 0·3月6日 21:55

区块链共识机制有哪些?详细对比 PoW、PoS 和 DPoS 的优缺点

区块链共识机制概述共识机制(Consensus Mechanism) 是区块链网络中节点就区块有效性达成一致意见的算法,是区块链安全性和去中心化的核心保障。主要共识机制类型1. PoW(Proof of Work,工作量证明)原理:矿工通过计算复杂的哈希难题来竞争记账权最先找到正确哈希值的矿工获得奖励比特币(Bitcoin)采用的共识机制优点:✅ 安全性极高,51%攻击成本巨大✅ 去中心化程度高,任何人都可以参与挖矿✅ 经过比特币长期验证,技术成熟稳定缺点:❌ 能源消耗巨大,不环保❌ 交易处理速度慢(比特币约 7 TPS)❌ 算力集中化风险(矿池垄断)PoW 挖矿流程:交易收集 → 构建区块头 → 计算 Nonce ↓SHA-256 哈希计算 ↓哈希值 < 目标难度? ↓是 → 广播区块,获得奖励否 → 修改 Nonce,重新计算2. PoS(Proof of Stake,权益证明)原理:验证者根据持有的代币数量和时长获得记账权不需要消耗算力,而是"质押"代币以太坊 2.0(Ethereum 2.0)采用的机制优点:✅ 能源效率高,环保✅ 交易速度快,可扩展性好✅ 经济安全性(攻击者需要持有大量代币)缺点:❌ "富者愈富"问题,财富集中化❌ 初始分配不公平可能影响安全性❌ 长程攻击(Long-range Attack)风险PoS 验证流程:质押代币 → 成为验证者 ↓系统随机选择出块者 ↓验证交易并打包区块 ↓获得区块奖励(交易费) ↓作恶惩罚:削减质押资金3. DPoS(Delegated Proof of Stake,委托权益证明)原理:代币持有者投票选举出一定数量的代表(见证人)由选出的代表轮流记账EOS、TRON 等采用此机制优点:✅ 交易速度极快(EOS 可达 3000+ TPS)✅ 能源效率极高✅ 治理机制更灵活缺点:❌ 中心化程度较高(只有少量节点出块)❌ 投票参与度可能较低❌ 见证人可能串通作恶三种机制对比表| 特性 | PoW | PoS | DPoS ||------|-----|-----|------|| 代表项目 | Bitcoin | Ethereum 2.0 | EOS || 能源消耗 | 极高 | 低 | 极低 || 交易速度 | 慢(7 TPS) | 中(1000+ TPS) | 快(3000+ TPS) || 去中心化 | 高 | 中高 | 中 || 安全性 | 极高 | 高 | 中 || 硬件要求 | 专用矿机 | 普通服务器 | 高性能服务器 |其他共识机制PBFT(实用拜占庭容错):联盟链常用,如 Hyperledger FabricPoA(权威证明):基于身份验证,适合私有链PoH(历史证明):Solana 采用,通过时间戳提高效率面试要点理解不同共识机制的适用场景能够分析安全性与效率的权衡了解以太坊从 PoW 转向 PoS 的原因掌握拜占庭容错的基本概念
阅读 0·3月6日 21:55

区块链面临哪些安全威胁?详解 51% 攻击、双花攻击和智能合约漏洞

区块链安全概述区块链虽然具有密码学保护,但仍面临多种安全威胁。理解这些威胁对于开发和运营安全的区块链应用至关重要。主要安全威胁分类区块链安全威胁├── 网络层攻击│ ├── 51% 攻击│ ├── 双花攻击│ └── Eclipse 攻击├── 智能合约漏洞│ ├── 重入攻击│ ├── 整数溢出│ ├── 访问控制漏洞│ └── 前端运行攻击└── 应用层攻击 ├── 钓鱼攻击 ├── 私钥泄露 └── 交易所被黑1. 51% 攻击(Majority Attack)攻击原理当攻击者控制网络超过 50% 的算力(PoW)或质押代币(PoS)时,可以:阻止交易确认撤销已确认交易阻止其他矿工挖矿正常网络: 51% 攻击:┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐│ 20% │ │ 30% │ │ 50% │ │ 20% │ │ 30% │ │ 50% │ ← 攻击者控制│ 矿工A│ │ 矿工B│ │ 矿工C│ │ 矿工A│ │ 矿工B│ │ 攻击者│└─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ ↓ 可以决定哪个区块被确认实际案例| 时间 | 项目 | 损失 ||------|------|------|| 2018-05 | Bitcoin Gold | 1800 万美元 || 2019-01 | Ethereum Classic | 110 万美元 || 2020-08 | Ethereum Classic | 再次遭受攻击 |防护措施✅ 增加网络总算力/质押量✅ 提高确认数要求✅ 实施检查点机制✅ 采用混合共识机制2. 双花攻击(Double Spending)攻击原理同一笔资金被花费两次,利用区块链确认延迟实现。双花攻击流程:攻击者 ──┬──→ 商家A:支付 10 BTC 购买商品(交易A) │ └──→ 商家B:支付同样的 10 BTC(交易B)步骤:1. 广播交易A到网络2. 商家A看到交易后发货3. 攻击者快速广播交易B4. 攻击者控制矿工优先打包交易B5. 交易A被孤立,成为孤儿交易6. 攻击者获得商品,资金未实际支出攻击类型| 类型 | 描述 | 难度 ||------|------|------|| Race Attack | 同时广播两个冲突交易 | 低 || Finney Attack | 矿工自己挖到区块后广播 | 中 || Vector76 | 结合 Race 和 Finney | 中 || 51% Attack | 控制多数算力重组链 | 高 |防护措施✅ 等待多个确认(比特币建议 6 个确认)✅ 使用支付处理器检测异常✅ 高价值交易要求更多确认3. Eclipse 攻击(日蚀攻击)攻击原理攻击者控制受害节点的所有网络连接,使其只能看到攻击者控制的区块链视图。正常网络连接: Eclipse 攻击后:┌─────┐ ┌─────┐│ 节点 │──┬── 诚实节点1 │ 节点 │── 攻击者节点1│ │ ├── 诚实节点2 → │ │── 攻击者节点2│ │ └── 诚实节点3 │ │── 攻击者节点3└─────┘ └─────┘ (看到真实网络) (只能看到攻击者的链)攻击后果矿工在孤立链上浪费算力商家接受无效交易双花攻击更容易实施防护措施✅ 限制单一 IP 连接数✅ 随机选择对等节点✅ 使用种子节点硬编码4. 智能合约安全漏洞重入攻击(Reentrancy)The DAO 事件(2016):损失 360 万 ETH// 漏洞代码function withdraw() public { uint256 amount = balances[msg.sender]; require(amount > 0); (bool success, ) = msg.sender.call{value: amount}(""); // 外部调用 require(success); balances[msg.sender] = 0; // 状态更新在后}// 攻击合约contract Attacker { function attack() external { victim.withdraw(); // 触发重入 } receive() external payable { if (address(victim).balance >= amount) { victim.withdraw(); // 递归调用 } }}防护措施:检查-生效-交互(Checks-Effects-Interactions)模式使用重入锁(ReentrancyGuard)先更新状态再转账整数溢出/下溢// 漏洞代码(Solidity < 0.8)function transfer(address to, uint256 amount) public { balances[msg.sender] -= amount; // 下溢! balances[to] += amount;}// 攻击:amount > balances[msg.sender] 时// balances[msg.sender] 变成极大值防护措施:使用 Solidity 0.8+(内置检查)使用 SafeMath 库5. 前端运行攻击(Front-running)攻击原理攻击者监控内存池(mempool),发现有利可图的交易后,支付更高 Gas Price 抢先执行。攻击流程:1. 用户广播交易:在 DEX 上购买代币A Gas Price: 20 Gwei2. 攻击者监控到交易3. 攻击者广播相同交易 Gas Price: 30 Gwei ← 更高,优先打包4. 攻击者交易先执行,代币价格上涨5. 用户交易后执行,以更高价格买入6. 攻击者卖出获利防护措施✅ 使用提交-揭示模式(Commit-Reveal)✅ 设置滑点保护✅ 使用 Flashbots 等私有内存池安全最佳实践开发层面代码审计:部署前进行专业审计形式化验证:使用工具验证关键逻辑测试覆盖:单元测试 + 集成测试 + 模糊测试使用标准库:OpenZeppelin Contracts漏洞赏金:建立 Bug Bounty 计划运营层面多签钱包:关键操作需要多方确认时间锁:重要操作延迟执行监控告警:实时监控异常交易应急响应:制定紧急暂停和资金恢复方案面试要点理解 51% 攻击的原理和后果掌握双花攻击的类型和防护熟悉智能合约常见漏洞及防护措施了解前端运行攻击的机制知道区块链安全最佳实践能够分析真实安全事件(如 The DAO)
阅读 0·3月6日 21:55

DNS 解析过程中的递归查询和迭代查询有什么区别

核心区别递归查询(Recursive Query) 和 迭代查询(Iterative Query) 是 DNS 解析过程中的两种不同查询方式,主要区别在于由谁来负责完成完整的查询过程。递归查询详解定义递归查询是指 DNS 客户端要求 DNS 服务器必须返回最终的查询结果,如果该服务器不知道答案,它会代替客户端向其他服务器查询,直到获得最终结果。特点客户端只需发一次请求:客户端只需要向本地 DNS 服务器发送一次查询请求服务器承担全部工作:本地 DNS 服务器负责完成所有的后续查询返回最终结果:客户端收到的是最终的 IP 地址,而不是中间引用工作流程客户端 → 本地 DNS 服务器 → 根服务器 → 本地 DNS 服务器 ↓ 顶级域服务器 → 本地 DNS 服务器 ↓ 权威服务器 → 本地 DNS 服务器 ↓ 返回 IP 给客户端实际应用场景主机向本地 DNS 服务器的查询通常是递归查询这是最常见的 DNS 查询方式迭代查询详解定义迭代查询是指 DNS 服务器返回它能够提供的最佳答案,如果不知道完整答案,会返回一个可能知道答案的服务器地址,由客户端(或请求方服务器)继续查询。特点服务器只提供指引:返回的是"去问那个服务器"的指引查询方需要多次请求:需要多次向不同服务器发起查询返回引用信息:返回的是 NS 记录或其他参考信息工作流程本地 DNS → 根服务器 → 返回 .com 服务器地址本地 DNS → .com 服务器 → 返回 example.com 权威服务器地址 本地 DNS → 权威服务器 → 返回最终 IP 地址实际应用场景DNS 服务器之间的查询通常是迭代查询根服务器、顶级域服务器通常只支持迭代查询对比总结| 特性 | 递归查询 | 迭代查询 ||------|----------|----------|| 查询责任 | 服务器负责完成全部查询 | 客户端/请求方负责继续查询 || 返回内容 | 最终结果(IP 地址) | 最佳答案或参考信息 || 请求次数 | 客户端只发 1 次请求 | 可能需要多次请求 || 服务器负载 | 较高 | 较低 || 典型场景 | 客户端→本地 DNS | DNS 服务器之间 |实际 DNS 解析中的结合使用在真实的 DNS 解析过程中,两种查询方式是结合使用的:┌─────────┐ 递归查询 ┌─────────────┐ 迭代查询 ┌──────────┐│ 客户端 │ ───────→ │ 本地 DNS 服务器│ ←───────→ │ 根服务器 │└─────────┘ └─────────────┘ └──────────┘ │ 迭代查询 ┌──────────┐ └────────→ │ 顶级域服务器│ │ └──────────┘ │ 迭代查询 ┌──────────┐ └────────→ │ 权威服务器│ └──────────┘客户端到本地 DNS:使用递归查询本地 DNS 到各级服务器:使用迭代查询为什么这样设计?递归查询:减轻客户端负担,客户端不需要了解 DNS 的复杂结构迭代查询:分散服务器负载,防止根服务器和顶级域服务器过载缓存机制:本地 DNS 缓存结果,减少重复查询
阅读 0·3月6日 21:55

Solidity 中 delegatecall 和 call 的区别是什么?代理合约如何实现?

delegatecall 和 call 是 Solidity 中用于合约间调用的两个重要底层函数,理解它们的区别对于实现代理合约和可升级合约至关重要。1. call 的基本用法call 是最常用的低级调用函数,在目标合约的上下文中执行代码。contract CallExample { // 使用 call 发送 ETH function sendEther(address payable recipient) public payable { (bool success, ) = recipient.call{value: msg.value}(""); require(success, "Transfer failed"); } // 使用 call 调用函数 function callFunction(address target, uint256 value) public returns (bool) { // 编码函数调用:function setValue(uint256) bytes memory data = abi.encodeWithSignature("setValue(uint256)", value); (bool success, bytes memory returnData) = target.call(data); require(success, "Call failed"); return success; } // 使用 call 调用并获取返回值 function callAndGetResult(address target) public view returns (uint256) { bytes memory data = abi.encodeWithSignature("getValue()"); (bool success, bytes memory returnData) = target.staticcall(data); require(success, "Call failed"); return abi.decode(returnData, (uint256)); }}2. delegatecall 的基本用法delegatecall 在调用者的上下文中执行目标合约的代码,保持 msg.sender 和 msg.value 不变。contract DelegatecallExample { uint256 public value; // 这个变量会被目标合约代码修改 // 使用 delegatecall 执行逻辑合约代码 function executeLogic(address logicContract, uint256 newValue) public { bytes memory data = abi.encodeWithSignature("setValue(uint256)", newValue); (bool success, ) = logicContract.delegatecall(data); require(success, "Delegatecall failed"); }}// 逻辑合约contract LogicContract { uint256 public value; // 注意:这个变量布局必须与代理合约一致 function setValue(uint256 newValue) public { value = newValue; // 修改的是调用者(代理合约)的 storage } function getValue() public view returns (uint256) { return value; }}3. call 与 delegatecall 对比| 特性 | call | delegatecall || ---------- | ------------- | -------------- || 执行上下文 | 目标合约 | 调用者合约 || msg.sender | 当前合约地址 | 原始调用者地址 || msg.value | 传递的值 | 原始调用值 || storage 访问 | 目标合约的 storage | 调用者合约的 storage || 代码执行位置 | 目标合约地址 | 目标合约地址 || 使用场景 | 普通调用、转账 | 代理合约、库合约 |4. 代理合约实现基础代理合约( EIP-1967 )contract Proxy { // EIP-1967 标准存储槽 // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; // bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) bytes32 private constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; constructor(address _implementation) { _setImplementation(_implementation); _setAdmin(msg.sender); } modifier onlyAdmin() { require(msg.sender == _getAdmin(), "Not admin"); _; } // 获取实现合约地址 function _getImplementation() internal view returns (address impl) { assembly { impl := sload(IMPLEMENTATION_SLOT) } } // 设置实现合约地址 function _setImplementation(address _implementation) internal { assembly { sstore(IMPLEMENTATION_SLOT, _implementation) } } // 获取管理员地址 function _getAdmin() internal view returns (address adm) { assembly { adm := sload(ADMIN_SLOT) } } // 设置管理员地址 function _setAdmin(address _admin) internal { assembly { sstore(ADMIN_SLOT, _admin) } } // 升级实现合约 function upgradeTo(address _newImplementation) public onlyAdmin { _setImplementation(_newImplementation); } // 获取当前实现地址(便于查询) function implementation() public view returns (address) { return _getImplementation(); } // 回退函数:将所有调用委托给实现合约 fallback() external payable { _delegate(_getImplementation()); } receive() external payable { _delegate(_getImplementation()); } // 核心委托逻辑 function _delegate(address _implementation) internal { assembly { // 复制 msg.data calldatacopy(0, 0, calldatasize()) // 执行 delegatecall let result := delegatecall( gas(), _implementation, 0, calldatasize(), 0, 0 ) // 复制返回数据 returndatacopy(0, 0, returndatasize()) // 根据结果返回或回滚 switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } }}逻辑合约示例// 逻辑合约 V1contract LogicV1 { uint256 public value; address public implementation; // 与代理合约存储布局一致 address public admin; function setValue(uint256 _value) public { value = _value; } function getValue() public view returns (uint256) { return value; }}// 逻辑合约 V2(升级版本)contract LogicV2 { uint256 public value; address public implementation; address public admin; // 新增功能 function setValue(uint256 _value) public { value = _value * 2; // 新逻辑:值翻倍 } function getValue() public view returns (uint256) { return value; } // 新增函数 function increment() public { value++; }}5. 透明代理模式透明代理解决了代理合约和实现合约函数选择器冲突的问题。contract TransparentProxy { bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; bytes32 private constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; constructor(address _implementation, address _admin) { _setImplementation(_implementation); _setAdmin(_admin); } modifier ifAdmin() { if (msg.sender == _getAdmin()) { _; } else { _fallback(); } } // 管理员函数(只有管理员可以调用) function upgradeTo(address _newImplementation) external ifAdmin { _setImplementation(_newImplementation); } function admin() external ifAdmin returns (address) { return _getAdmin(); } function implementation() external ifAdmin returns (address) { return _getImplementation(); } // 修改管理员 function changeAdmin(address _newAdmin) external ifAdmin { _setAdmin(_newAdmin); } // 非管理员调用会执行到这里 function _fallback() internal { _delegate(_getImplementation()); } fallback() external payable { _fallback(); } receive() external payable { _fallback(); } // 存储操作函数 function _getImplementation() internal view returns (address impl) { assembly { impl := sload(IMPLEMENTATION_SLOT) } } function _setImplementation(address _implementation) internal { assembly { sstore(IMPLEMENTATION_SLOT, _implementation) } } function _getAdmin() internal view returns (address adm) { assembly { adm := sload(ADMIN_SLOT) } } function _setAdmin(address _admin) internal { assembly { sstore(ADMIN_SLOT, _admin) } } function _delegate(address _implementation) internal { assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } }}6. UUPS 代理模式UUPS(Universal Upgradeable Proxy Standard)将升级逻辑放在实现合约中。// UUPS 代理contract UUPSProxy { bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; constructor(address _implementation) { _setImplementation(_implementation); } function _getImplementation() internal view returns (address impl) { assembly { impl := sload(IMPLEMENTATION_SLOT) } } function _setImplementation(address _implementation) internal { assembly { sstore(IMPLEMENTATION_SLOT, _implementation) } } fallback() external payable { _delegate(_getImplementation()); } receive() external payable { _delegate(_getImplementation()); } function _delegate(address _implementation) internal { assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } }}// UUPS 实现合约接口interface IUUPS { function upgradeTo(address newImplementation) external; function proxiableUUID() external view returns (bytes32);}// UUPS 实现合约contract UUPSImplementation is IUUPS { bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; uint256 public value; address public implementation; address public owner; modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } constructor() { owner = msg.sender; } function upgradeTo(address newImplementation) external onlyOwner { _authorizeUpgrade(newImplementation); _upgradeTo(newImplementation); } function proxiableUUID() external pure returns (bytes32) { return IMPLEMENTATION_SLOT; } function _authorizeUpgrade(address newImplementation) internal virtual { // 可以在这里添加额外的升级验证逻辑 } function _upgradeTo(address newImplementation) internal { assembly { sstore(IMPLEMENTATION_SLOT, newImplementation) } } function setValue(uint256 _value) public { value = _value; }}7. 存储布局注意事项// 错误的存储布局示例contract WrongLayoutV1 { uint256 public value; // slot 0 address public owner; // slot 1}contract WrongLayoutV2 { address public owner; // slot 0 - 错误!应该是 slot 1 uint256 public value; // slot 1 - 错误!应该是 slot 0 uint256 public newValue; // slot 2}// 正确的存储布局contract CorrectLayoutV1 { uint256 public value; // slot 0 address public owner; // slot 1}contract CorrectLayoutV2 { uint256 public value; // slot 0 - 保持相同 address public owner; // slot 1 - 保持相同 uint256 public newValue; // slot 2 - 新增变量放在最后}8. 最佳实践使用标准存储槽:遵循 EIP-1967 标准避免存储冲突保持存储布局一致:升级时不能改变已有变量的顺序使用 OpenZeppelin:使用经过审计的标准实现测试升级流程:在测试网上充分测试升级过程考虑钻石模式:对于超大型合约,考虑使用 EIP-2535 钻石标准// 使用 OpenZeppelin 的代理import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";// 推荐:使用 UUPS 模式import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";import "@openzeppelin/contracts/access/Ownable.sol";contract MyContract is UUPSUpgradeable, Ownable { function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}}​
阅读 0·3月6日 21:55

cURL 如何处理 Cookie 和会话管理?

Cookie 管理是 cURL 在处理需要身份验证的 Web 应用时的关键功能。cURL 提供了多种方式来发送、接收和持久化 Cookie。发送 Cookie# 方式一:使用 -b 参数curl -b "session_id=abc123" https://api.example.com/profile# 方式二:使用 -H 设置 Cookie 头curl -H "Cookie: session_id=abc123" https://api.example.com/profile# 发送多个 Cookiecurl -b "session_id=abc123; user_id=456; theme=dark" \ https://api.example.com/profile# 从文件读取 Cookiecurl -b cookies.txt https://api.example.com/profile接收和保存 Cookie# 保存服务器返回的 Cookie 到文件curl -c cookies.txt https://api.example.com/login# 同时发送和接收 Cookiecurl -b cookies.txt -c cookies.txt \ -d "username=admin&password=123456" \ https://api.example.com/login# 显示详细 Cookie 信息curl -v https://api.example.com/login 2>&1 | grep "Set-Cookie"Cookie 文件格式cURL 使用 Netscape 格式的 Cookie 文件:# Netscape HTTP Cookie File# https://curl.haxx.se/docs/http-cookies.html# This file was generated automatically by curl.example.com TRUE / FALSE 0 session_id abc123.example.com TRUE / FALSE 1735689600 user_id 456字段说明:域名(Domain)是否包含子域名(TRUE/FALSE)路径(Path)是否安全连接(TRUE/FALSE)过期时间(Unix 时间戳,0 表示会话 Cookie)Cookie 名称Cookie 值完整登录流程示例# 步骤 1:访问登录页面,获取初始 Cookiecurl -c cookies.txt -b cookies.txt \ https://api.example.com/login# 步骤 2:提交登录表单,保存会话 Cookiecurl -c cookies.txt -b cookies.txt \ -X POST \ -d "username=admin&password=123456" \ https://api.example.com/login# 步骤 3:使用会话 Cookie 访问受保护页面curl -b cookies.txt https://api.example.com/dashboard# 步骤 4:登出(清除会话)curl -b cookies.txt https://api.example.com/logoutCookie 相关参数| 参数 | 作用 | 示例 || ------------------------------- | ----------- | ------------------------------------ || -b 或 --cookie | 发送 Cookie | -b "name=value" 或 -b cookies.txt || -c 或 --cookie-jar | 保存 Cookie | -c cookies.txt || -j 或 --junk-session-cookies | 忽略会话 Cookie | 读取文件时跳过会话 Cookie |高级用法# 使用 Cookie Jar 进行会话管理curl -b cookies.txt -c cookies.txt -L \ -X POST \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}' \ https://api.example.com/api/login# 查看 Cookie 文件内容cat cookies.txt# 清除所有 Cookie(删除文件)rm cookies.txt# 仅保留持久化 Cookie(过滤会话 Cookie)curl -b cookies.txt -j -c persistent_cookies.txt \ https://api.example.com/data注意事项安全性:Cookie 文件包含敏感信息,注意权限设置(chmod 600)过期时间:定期清理过期的 Cookie跨域限制:Cookie 遵循同源策略,注意域名和路径匹配会话 vs 持久:会话 Cookie 在浏览器关闭后失效,持久 Cookie 有过期时间
阅读 0·3月6日 21:54