利用时间锁和哈希锁实现一个哈希时间锁合约

  • 木西
  • 发布于 2天前
  • 阅读 47

前言本文主要利用哈希时间锁合约(HashTimeLockedContract,HTLC)是一种去中心化的合约机制,通过结合时间锁和哈希锁,实现了条件支付的功能,包含了开发、测试、部署全部流程;区块链上的时间锁定义:一种去中心化的合约机制,通过时间锁和哈希锁的结合从而实现。工作流程创

前言

本文主要利用哈希时间锁合约(Hash Time Locked Contract,HTLC)是一种去中心化的合约机制,通过结合时间锁和哈希锁,实现了条件支付的功能,包含了开发、测试、部署全部流程;

区块链上的时间锁

定义:一种去中心化的合约机制,通过时间锁和哈希锁的结合从而实现。<br>

工作流程
  • 创建交易,并加入到时间锁队列。
  • 在交易的锁定期满后,执行交易。
  • 后悔了,取消时间锁队列中的某些交易。

场景:主要用在DeFiDAO中,可以提高项目的安全性;

功能:智能合约的某些功能锁定一段时间,大大减少项目方rug pull和黑客攻击的机会,增加去中心化应用的安全性;

合约开发

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import "hardhat/console.sol";
contract Timelock{
    // 事件
    // 交易取消事件
    event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature,  bytes data, uint executeTime);
    // 交易执行事件
    event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature,  bytes data, uint executeTime);
    // 交易创建并进入队列 事件
    event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint executeTime);
    // 修改管理员地址的事件
    event NewAdmin(address indexed newAdmin);

    // 状态变量
    address public admin; // 管理员地址
    uint public constant GRACE_PERIOD = 7 days; // 交易有效期,过期的交易作废
    uint public delay; // 交易锁定时间 (秒)
    mapping (bytes32 => bool) public queuedTransactions; // txHash到bool,记录所有在时间锁队列中的交易

    // onlyOwner modifier
    modifier onlyOwner() {
        require(msg.sender == admin, "Timelock: Caller not admin");
        _;
    }

    // onlyTimelock modifier
    modifier onlyTimelock() {
        require(msg.sender == address(this), "Timelock: Caller not Timelock");
        _;
    }

    /**
     * @dev 构造函数,初始化交易锁定时间 (秒)和管理员地址
     */
    constructor(uint delay_) {
        delay = delay_;
        admin = msg.sender;
    }

    /**
     * @dev 改变管理员地址,调用者必须是Timelock合约。
     */
    function changeAdmin(address newAdmin) public onlyTimelock {
        admin = newAdmin;

        emit NewAdmin(newAdmin);
    }

    /**
     * @dev 创建交易并添加到时间锁队列中。
     * @param target: 目标合约地址
     * @param value: 发送eth数额
     * @param signature: 要调用的函数签名(function signature)
     * @param data: call data,里面是一些参数
     * @param executeTime: 交易执行的区块链时间戳
     *
     * 要求:executeTime 大于 当前区块链时间戳+delay
     */
    function queueTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public onlyOwner returns (bytes32) {
        // 检查:交易执行时间满足锁定时间
        require(executeTime >= getBlockTimestamp() + delay, "Timelock::queueTransaction: Estimated execution block must satisfy delay.");
        // 计算交易的唯一识别符:一堆东西的hash
        bytes32 txHash = getTxHash(target, value, signature, data, executeTime);
        // 将交易添加到队列
        queuedTransactions[txHash] = true;

        emit QueueTransaction(txHash, target, value, signature, data, executeTime);
        return txHash;
    }

    /**
     * @dev 取消特定交易。
     *
     * 要求:交易在时间锁队列中
     */
    function cancelTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public onlyOwner{
        // 计算交易的唯一识别符:一堆东西的hash
        bytes32 txHash = getTxHash(target, value, signature, data, executeTime);
        // 检查:交易在时间锁队列中
        require(queuedTransactions[txHash], "Timelock::cancelTransaction: Transaction hasn't been queued.");
        // 将交易移出队列
        queuedTransactions[txHash] = false;

        emit CancelTransaction(txHash, target, value, signature, data, executeTime);
    }

    /**
     * @dev 执行特定交易。
     *
     * 要求:
     * 1. 交易在时间锁队列中
     * 2. 达到交易的执行时间
     * 3. 交易没过期
     */
    function executeTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public payable onlyOwner returns (bytes memory) {
        bytes32 txHash = getTxHash(target, value, signature, data, executeTime);
        // 检查:交易是否在时间锁队列中
        require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
        // 检查:达到交易的执行时间
        require(getBlockTimestamp() >= executeTime, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
        // 检查:交易没过期
       require(getBlockTimestamp() &lt;= executeTime + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale.");
        // 将交易移出队列
        queuedTransactions[txHash] = false;

        // 获取call data
        bytes memory callData;
        if (bytes(signature).length == 0) {
            callData = data;
        } else {
// 这里如果采用encodeWithSignature的编码方式来实现调用管理员的函数,请将参数data的类型改为address。不然会导致管理员的值变为类似"0x0000000000000000000000000000000000000020"的值。其中的0x20是代表字节数组长度的意思.
            callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
        }
        // 利用call执行交易
        (bool success, bytes memory returnData) = target.call{value: value}(callData);

        require(success, "Timelock::executeTransaction: Transaction execution reverted.");

        emit ExecuteTransaction(txHash, target, value, signature, data, executeTime);

        return returnData;
    }

    /**
     * @dev 获取当前区块链时间戳
     */
    function getBlockTimestamp() public view returns (uint) {
        return block.timestamp;
    }

    /**
     * @dev 将一堆东西拼成交易的标识符
     */
    function getTxHash(
        address target,
        uint value,
        string memory signature,
        bytes memory data,
        uint executeTime
    ) public pure returns (bytes32) {
        return keccak256(abi.encode(target, value, signature, data, executeTime));
    }
}
# 编译指令
# npx hardhat compile

合约测试

说明:1.获取当前区块时间来模拟延迟执行,2.通过ethers.utils.defaultAbiCoder.encode(["address"], [newAdmin])方法获取hash或者使用在线工具生成abiHash

const {ethers,getNamedAccounts,deployments} = require("hardhat");
const { assert,expect } = require("chai");
describe("Timelock",function(){
    let Timelock;//合约地址
    let firstAccount//第一个账户
    let secondAccount//第二个账户
    let addr1;
    let addr2;
    beforeEach(async function(){
        await deployments.fixture(["Timelock"]);
        [addr1,addr2]=await ethers.getSigners();
        firstAccount=(await getNamedAccounts()).firstAccount;
        secondAccount=(await getNamedAccounts()).secondAccount;
        const TimelockDeployment = await deployments.get("Timelock");
        Timelock = await ethers.getContractAt("Timelock",TimelockDeployment.address);//已经部署的合约交互

    });
    describe("时间锁",function(){
        it("时间锁测试",async function(){
            // 获取当前区块时间
    const currentTime = await ethers.provider.getBlock("latest").then(block => block.timestamp);

    // 预定执行时间:当前时间 + 100s
    const executeTime = currentTime + 100;
    console.log("执行时间",executeTime)
            //时间未到执行报错
            // console.log('时间未到报错',await Timelock.changeAdmin())
           console.log("admin的地址",await Timelock.admin());
        //放入时间对列里
        let TimelockAddress=Timelock.address;//合约地址
        let value=0;//代币数量
        const newAdmin = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
        //生成hash值 方法1
        let data=ethers.utils.defaultAbiCoder.encode(["address"], [newAdmin]);//生成hash
        console.log(data)
        //生成hash值 方法2 借助在线工具生成
        let data1="0xec86135600000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8";//可以通过在线工具生成,newAdmin
        let changeAdmin="changeAdmin(address)";//方法
        //添加到时间队列里 参数说明:合约地址,代币数量,方法,hash,执行时间
        await Timelock.queueTransaction(TimelockAddress,value,changeAdmin,data,executeTime)
        console.log("添加到时间队列里")
        //模拟100s后修改管理员
        await ethers.provider.send("evm_increaseTime", [100]);
        await ethers.provider.send("evm_mine", []);
        //执行交易参数说明:合约地址,代币数量,方法,hash,执行时间
        await Timelock.executeTransaction(TimelockAddress,value,changeAdmin,data,executeTime)
        console.log("修改后admin的地址",await Timelock.admin());
        })
    })
})
# 测试指令
# npx hardhat test ./test/xxx.js

合约部署

module.exports = async function ({getNamedAccounts,deployments}) {
  const  firstAccount= (await getNamedAccounts()).firstAccount;
  const  secondAccount= (await getNamedAccounts()).secondAccount;
  const {deploy,log}=deployments;
  const Timelock=await deploy("Timelock",{
    from:firstAccount,
    args: [100],//参数 锁定时间
    log: true,
  })
  console.log('TokenLocker合约地址',Timelock.address)
}
module.exports.tags = ["all", "Timelock"];
# 部署指令
# npx hardhat deploy

总结

以上就是时间锁合约从开发、测试、部署全过程以及对时间锁概念以及使用场景等相关介绍;

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
江湖只有他的大名,没有他的介绍。