搜索
首页科技周边IT业界建造以太坊dapps:白名单和测试一个故事dao

Building Ethereum DApps: Whitelisting & Testing a Story DAO

关键要点

  • Story DAO 利用 OpenZeppelin 的 Ownable 合约确保只有所有者才能执行管理功能,从而增强了对 DApp 操作的安全性和控制。
  • Story DAO 合约具有可调整的费用和持续时间参数,并设有安全措施以防止未经授权的更改,确保只有所有者才能修改关键设置。
  • Story DAO 中的白名单管理通过需要付费的功能来实现,允许根据发送者的贡献自动和有条件地访问。
  • 综合测试策略,包括 Solidity 和 JavaScript 测试,对于验证 Story DAO 的功能和安全性至关重要,确保在部署之前稳健运行。
  • Story DAO 的部署过程通过 Truffle 简化,具有特定的迁移脚本和配置,方便从开发环境到生产环境的顺利过渡。

本教程系列的第三部分介绍了使用以太坊构建 DApp,我们构建并将令牌部署到以太坊测试网络 Rinkeby。在本部分中,我们将开始编写 Story DAO 代码。

我们将使用介绍性文章中列出的条件来指导我们。

合约概述

让我们创建一个新的合约 StoryDao.sol,其框架如下:

pragma solidity ^0.4.24;

import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";

contract StoryDao is Ownable {
    using SafeMath for uint256;

    mapping(address => bool) whitelist;
    uint256 public whitelistedNumber = 0;
    mapping(address => bool) blacklist;
    event Whitelisted(address addr, bool status);
    event Blacklisted(address addr, bool status);

    uint256 public daofee = 100; // 百分之几,即 100 为 1%
    uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币

    event SubmissionCommissionChanged(uint256 newFee);
    event WhitelistFeeChanged(uint256 newFee);

    uint256 public durationDays = 21; // 故事章节持续时间(天)
    uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目)

    function changedaofee(uint256 _fee) onlyOwner external {
        require(_fee <= 1000); // 限制最大费用为 10%
        daofee = _fee;
        emit SubmissionCommissionChanged(_fee);
    }

    function changewhitelistfee(uint256 _fee) onlyOwner external {
        require(_fee > 0); // 确保费用大于 0
        whitelistfee = _fee;
        emit WhitelistFeeChanged(_fee);
    }


    function changeDurationDays(uint256 _days) onlyOwner external {
        require(_days >= 1);
        durationDays = _days;
    }

    function changeDurationSubmissions(uint256 _subs) onlyOwner external {
        require(_subs > 99);
        durationSubmissions = _subs;
    }
}

我们导入 SafeMath 以再次进行安全计算,但这次我们还使用 Zeppelin 的 Ownable 合约,它允许某人“拥有”故事并执行某些仅限管理员的功能。简单地说,我们的 StoryDao 是 Ownable 就足够了;随意检查合约以了解其工作原理。

我们还使用此合约中的 onlyOwner 修饰符。函数修饰符基本上是函数的扩展、插件。onlyOwner 修饰符如下所示:

modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}

当 onlyOwner 添加到函数时,该函数的主体将粘贴到 _; 部分所在的位置,并且之前的部分将首先执行。因此,通过使用此修饰符,该函数会自动检查消息发送者是否是合约的所有者,然后如果属实则照常继续。如果不是,则会崩溃。

通过在更改 Story DAO 的费用和其他参数的函数上使用 onlyOwner 修饰符,我们确保只有管理员才能进行这些更改。

测试

让我们测试初始函数。

如果不存在,请创建文件夹 test。然后在其中创建文件 TestStoryDao.sol 和 TestStoryDao.js。由于 Truffle 中没有本地方法来测试异常,因此还使用以下内容创建 helpers/expectThrow.js:

export default async promise => {
    try {
      await promise;
    } catch (error) {
      const invalidOpcode = error.message.search('invalid opcode') >= 0;
      const outOfGas = error.message.search('out of gas') >= 0;
      const revert = error.message.search('revert') >= 0;
      assert(
        invalidOpcode || outOfGas || revert,
        'Expected throw, got \'' + error + '\' instead',
      );
      return;
    }
    assert.fail('Expected throw not received');
  };

注意:Solidity 测试通常用于测试低级、基于合约的函数,即智能合约的内部结构。JS 测试通常用于测试合约是否可以从外部正确交互,这是我们的最终用户将要执行的操作。

在 TestStoryDao.sol 中,放入以下内容:

pragma solidity ^0.4.24;

import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";

contract StoryDao is Ownable {
    using SafeMath for uint256;

    mapping(address => bool) whitelist;
    uint256 public whitelistedNumber = 0;
    mapping(address => bool) blacklist;
    event Whitelisted(address addr, bool status);
    event Blacklisted(address addr, bool status);

    uint256 public daofee = 100; // 百分之几,即 100 为 1%
    uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币

    event SubmissionCommissionChanged(uint256 newFee);
    event WhitelistFeeChanged(uint256 newFee);

    uint256 public durationDays = 21; // 故事章节持续时间(天)
    uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目)

    function changedaofee(uint256 _fee) onlyOwner external {
        require(_fee <= 1000); // 限制最大费用为 10%
        daofee = _fee;
        emit SubmissionCommissionChanged(_fee);
    }

    function changewhitelistfee(uint256 _fee) onlyOwner external {
        require(_fee > 0); // 确保费用大于 0
        whitelistfee = _fee;
        emit WhitelistFeeChanged(_fee);
    }


    function changeDurationDays(uint256 _days) onlyOwner external {
        require(_days >= 1);
        durationDays = _days;
    }

    function changeDurationSubmissions(uint256 _subs) onlyOwner external {
        require(_subs > 99);
        durationSubmissions = _subs;
    }
}

这将检查 StoryDao 合约是否以正确的费用和持续时间数字正确部署。第一行确保它通过从已部署地址列表中读取它来部署,并且最后一节进行一些断言——检查声明是真还是假。在我们的例子中,我们将数字与已部署合约的初始值进行比较。每当它是“真”时,Assert.equals 部分都会发出一个事件,说明“真”,这就是 Truffle 在测试时正在监听的。

在 TestStoryDao.js 中,放入以下内容:

modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}

为了使我们的测试能够成功运行,我们还需要告诉 Truffle 我们希望部署 StoryDao——因为它不会为我们这样做。因此,让我们在迁移中使用几乎与我们之前编写的迁移相同的 content 创建 3_deploy_storydao.js:

export default async promise => {
    try {
      await promise;
    } catch (error) {
      const invalidOpcode = error.message.search('invalid opcode') >= 0;
      const outOfGas = error.message.search('out of gas') >= 0;
      const revert = error.message.search('revert') >= 0;
      assert(
        invalidOpcode || outOfGas || revert,
        'Expected throw, got \'' + error + '\' instead',
      );
      return;
    }
    assert.fail('Expected throw not received');
  };

此时,我们还应该更新(或创建,如果不存在)项目文件夹根目录中的 package.json 文件,其中包含我们到目前为止需要的依赖项,以及将来可能需要的依赖项:

pragma solidity ^0.4.24;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/StoryDao.sol";

contract TestStoryDao {
    function testDeploymentIsFine() public {
        StoryDao sd = StoryDao(DeployedAddresses.StoryDao());

        uint256 daofee = 100; // 百分之几,即 100 为 1%
        uint256 whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币

        uint256 durationDays = 21; // 故事章节持续时间(天)
        uint256 durationSubmissions = 1000; // 故事章节持续时间(条目)

        Assert.equal(sd.daofee(), daofee, "初始 DAO 费用应为 100");
        Assert.equal(sd.whitelistfee(), whitelistfee, "初始白名单费用应为 0.01 以太币");
        Assert.equal(sd.durationDays(), durationDays, "初始天数持续时间应设置为 3 周");
        Assert.equal(sd.durationSubmissions(), durationSubmissions, "初始提交持续时间应设置为 1000 个条目");
    }
}

以及包含以下内容的 .babelrc 文件:

import expectThrow from './helpers/expectThrow';

const StoryDao = artifacts.require("StoryDao");

contract('StoryDao Test', async (accounts) => {
    it("should make sure environment is OK by checking that the first 3 accounts have over 20 eth", async () =>{
        assert.equal(web3.eth.getBalance(accounts[0]).toNumber() > 2e+19, true, "Account 0 has more than 20 eth");
        assert.equal(web3.eth.getBalance(accounts[1]).toNumber() > 2e+19, true, "Account 1 has more than 20 eth");
        assert.equal(web3.eth.getBalance(accounts[2]).toNumber() > 2e+19, true, "Account 2 has more than 20 eth");
    });

    it("should make the deployer the owner", async () => {
        let instance = await StoryDao.deployed();
        assert.equal(await instance.owner(), accounts[0]);
    });

    it("should let owner change fee and duration", async () => {
        let instance = await StoryDao.deployed();

        let newDaoFee = 50;
        let newWhitelistFee = 1e+10; // 1 ether
        let newDayDuration = 42;
        let newSubsDuration = 1500;

        instance.changedaofee(newDaoFee, {from: accounts[0]});
        instance.changewhitelistfee(newWhitelistFee, {from: accounts[0]});
        instance.changeDurationDays(newDayDuration, {from: accounts[0]});
        instance.changeDurationSubmissions(newSubsDuration, {from: accounts[0]});

        assert.equal(await instance.daofee(), newDaoFee);
        assert.equal(await instance.whitelistfee(), newWhitelistFee);
        assert.equal(await instance.durationDays(), newDayDuration);
        assert.equal(await instance.durationSubmissions(), newSubsDuration);
    });

    it("should forbid non-owners from changing fee and duration", async () => {
        let instance = await StoryDao.deployed();

        let newDaoFee = 50;
        let newWhitelistFee = 1e+10; // 1 ether
        let newDayDuration = 42;
        let newSubsDuration = 1500;

        await expectThrow(instance.changedaofee(newDaoFee, {from: accounts[1]}));
        await expectThrow(instance.changewhitelistfee(newWhitelistFee, {from: accounts[1]}));
        await expectThrow(instance.changeDurationDays(newDayDuration, {from: accounts[1]}));
        await expectThrow(instance.changeDurationSubmissions(newSubsDuration, {from: accounts[1]}));
    });

    it("should make sure the owner can only change fees and duration to valid values", async () =>{
        let instance = await StoryDao.deployed();

        let invalidDaoFee = 20000;
        let invalidDayDuration = 0;
        let invalidSubsDuration = 98;

        await expectThrow(instance.changedaofee(invalidDaoFee, {from: accounts[0]}));
        await expectThrow(instance.changeDurationDays(invalidDayDuration, {from: accounts[0]}));
        await expectThrow(instance.changeDurationSubmissions(invalidSubsDuration, {from: accounts[0]}));
    })
});

我们还需要在 Truffle 配置中要求 Babel,以便它知道在编译测试时应该使用它。

注意:Babel 是 NodeJS 的一个附加组件,它允许我们在当前一代 NodeJS 中使用下一代 JavaScript,因此我们可以编写 import 等内容。如果您不理解这一点,只需忽略它并逐字粘贴即可。安装后,您可能再也不用处理这个问题了。

var Migrations = artifacts.require("./Migrations.sol");
var StoryDao = artifacts.require("./StoryDao.sol");

module.exports = function(deployer, network, accounts) {
  if (network == "development") {
    deployer.deploy(StoryDao, {from: accounts[0]});
  } else {
    deployer.deploy(StoryDao);
  }
};

现在,最后运行 truffle test。输出应该类似于此:

Building Ethereum DApps: Whitelisting & Testing a Story DAO

有关测试的更多信息,请参阅本教程,我们专门准备了本教程来涵盖智能合约的测试。

在本课程的后续部分中,我们将跳过测试,因为逐字输入它们会使教程过长,但请参考项目的最终源代码以检查所有测试。我们刚刚完成的过程已经为测试设置了环境,因此您可以编写测试而无需进一步设置。

白名单

现在让我们构建白名单机制,它允许用户参与构建故事。将以下函数框架添加到 StoryDao.sol:

{
  "name": "storydao",
  "devDependencies": {
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-stage-2": "^6.24.1",
    "babel-preset-stage-3": "^6.17.0",
    "babel-polyfill": "^6.26.0",
    "babel-register": "^6.23.0",
    "dotenv": "^6.0.0",
    "truffle": "^4.1.12",
    "openzeppelin-solidity": "^1.10.0",
    "openzeppelin-solidity-metadata": "^1.2.0",
    "openzeppelin-zos": "",
    "truffle-wallet-provider": "^0.0.5",
    "ethereumjs-wallet": "^0.6.0",
    "web3": "^1.0.0-beta.34",
    "truffle-assertions": "^0.3.1"
  }
}

未命名的函数 function() 被称为回退函数,当向此合约发送资金但没有特定指令(即没有专门调用另一个函数)时,就会调用此函数。这允许人们通过仅向 DAO 发送以太币来加入 StoryDao,并根据他们是否已列入白名单来立即列入白名单或购买代币。

whitelistSender 函数用于列入白名单,可以直接调用,但我们将确保回退函数在收到一些以太币后自动调用它,前提是发送者尚未列入白名单。whitelistAddress 函数被声明为 public,因为它也应该可以从其他合约调用,而回退函数是 external,因为资金只会从外部地址发送到此地址。调用此合约的合约可以轻松直接调用所需函数。

让我们首先处理回退函数。

pragma solidity ^0.4.24;

import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";
import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol";

contract StoryDao is Ownable {
    using SafeMath for uint256;

    mapping(address => bool) whitelist;
    uint256 public whitelistedNumber = 0;
    mapping(address => bool) blacklist;
    event Whitelisted(address addr, bool status);
    event Blacklisted(address addr, bool status);

    uint256 public daofee = 100; // 百分之几,即 100 为 1%
    uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币

    event SubmissionCommissionChanged(uint256 newFee);
    event WhitelistFeeChanged(uint256 newFee);

    uint256 public durationDays = 21; // 故事章节持续时间(天)
    uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目)

    function changedaofee(uint256 _fee) onlyOwner external {
        require(_fee <= 1000); // 限制最大费用为 10%
        daofee = _fee;
        emit SubmissionCommissionChanged(_fee);
    }

    function changewhitelistfee(uint256 _fee) onlyOwner external {
        require(_fee > 0); // 确保费用大于 0
        whitelistfee = _fee;
        emit WhitelistFeeChanged(_fee);
    }


    function changeDurationDays(uint256 _days) onlyOwner external {
        require(_days >= 1);
        durationDays = _days;
    }

    function changeDurationSubmissions(uint256 _subs) onlyOwner external {
        require(_subs > 99);
        durationSubmissions = _subs;
    }
}

我们检查发送者是否尚未列入白名单,并将调用委托给 whitelistAddress 函数。请注意,我们注释掉了 buyTokens 函数,因为我们还没有它。

接下来,让我们处理白名单。

modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}

请注意,此函数接受地址作为参数,而不是从消息(从事务)中提取它。这还有一个额外的好处,即如果有人负担不起加入 DAO 的费用,例如,人们可以将其他人列入白名单。

我们从一些健全性检查开始函数:发送者不得已列入白名单或列入黑名单(被禁止),并且必须已发送足够的费用来支付费用。如果这些条件令人满意,则将地址添加到白名单,发出 Whitelisted 事件,最后,如果发送的以太币数量大于支付白名单费用所需的以太币数量,则剩余部分将用于购买代币。

注意:我们使用 sub 而不是 - 来减去,因为这是用于安全计算的 SafeMath 函数。

只要用户向 StoryDao 合约发送 0.01 以太币或更多以太币,他们现在就可以将自己或其他人列入白名单。

结论

在本教程中,我们构建了 DAO 的初始部分,但还有很多工作要做。敬请期待:在下一部分中,我们将处理向故事中添加内容!

有关构建以太坊 DApp 和白名单的常见问题解答

构建以太坊 DApp 的先决条件是什么?

在开始构建以太坊 DApp 之前,您需要对区块链技术、以太坊和智能合约有基本的了解。您还应该熟悉 JavaScript 和 Solidity 等编程语言,Solidity 用于编写以太坊上的智能合约。此外,您需要安装 Node.js、Truffle、Ganache 和 MetaMask 等工具,这些工具对于开发和测试 DApp 至关重要。

DApp 中的白名单流程如何运作?

白名单是 DApp 中用于限制对应用程序的某些功能或区域的访问的安全措施。它涉及创建一个批准的地址列表,这些地址允许与 DApp 交互。只有从这些地址发起的交易才会被接受,而其他交易将被拒绝。这有助于防止未经授权的访问和恶意活动。

智能合约在 DApp 中的作用是什么?

智能合约是自执行合约,其协议条款直接写入代码中。它们在 DApp 中发挥着至关重要的作用,因为它们可以自动化区块链上业务逻辑的执行。它们确保透明度、安全性和不可变性,因为一旦部署,它们就无法更改或篡改。

如何测试我的 DApp?

测试是 DApp 开发中至关重要的一部分,用于确保其功能和安全性。您可以使用 Truffle 和 Ganache 等工具进行测试。Truffle 为以太坊提供开发环境、测试框架和资产管道,而 Ganache 允许您创建用于测试的私有以太坊区块链。

什么是 DAO,它与 DApp 有何关系?

DAO 代表去中心化自治组织。它是一种由编码为计算机程序的规则表示的组织类型,该程序是透明的,由组织成员控制,不受中央政府的影响。DAO 的财务交易和规则保存在区块链上,这使其成为一种 DApp。

如何确保我的 DApp 的安全性?

确保 DApp 的安全性涉及多种实践。这包括编写安全的智能合约、彻底测试、进行安全审计以及保持软件和依赖项的最新状态。遵循安全编码的最佳实践并随时了解区块链领域最新的安全漏洞和威胁也很重要。

什么是 MetaMask,它在 DApp 开发中为什么很重要?

MetaMask 是一个浏览器扩展,允许您直接从浏览器与以太坊区块链和 DApp 交互。它还可以作为以太坊钱包来管理您的以太币和 ERC-20 代币。它在 DApp 开发中很重要,因为它为用户提供了一个用户友好的界面,以便用户无需运行完整的以太坊节点即可与您的 DApp 交互。

如何部署我的 DApp?

一旦您开发并测试了您的 DApp,您就可以将其部署到以太坊主网或测试网上。这涉及编译您的智能合约,将它们部署到区块链,并将您的 DApp 连接到这些合约。您可以使用 Truffle 和 Infura 等工具来完成此过程。

DApp 开发的挑战是什么?

DApp 开发面临着一些挑战。这包括处理以太坊网络的可扩展性问题、确保 DApp 的安全性、管理交易的波动性燃气价格以及提供用户友好的界面。它还需要随时了解快速发展的区块链技术和法规。

部署后如何更新我的 DApp?

部署后更新 DApp 可能具有挑战性,因为区块链上的智能合约是不可变的。但是,您可以通过将数据和逻辑分离到不同的合约中或使用委托调用来升级合约来设计可升级的合约。在 DApp 的设计阶段规划升级和更改非常重要。

以上是建造以太坊dapps:白名单和测试一个故事dao的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
安卓首家接入DeepSeek背后:看见女性力量安卓首家接入DeepSeek背后:看见女性力量Mar 12, 2025 pm 12:27 PM

中国女性科技力量在AI领域的崛起:荣耀与DeepSeek合作背后的女性故事女性在科技领域的贡献日益显着。中国科技部数据显示,女性科技工作者数量庞大,在AI算法开发中展现出独特的社会价值敏感性。本文将聚焦荣耀手机,探究其率先接入DeepSeek大模型背后的女性团队力量,展现她们如何推动科技进步,重塑科技发展价值坐标系。 2024年2月8日,荣耀正式上线DeepSeek-R1满血版大模型,成为安卓阵营首家接入DeepSeek的厂商,引发用户热烈反响。这一成功背后,女性团队成员在产品决策、技术攻坚和用户

DeepSeek'惊人”盈利:理论利润率高达545%!DeepSeek'惊人”盈利:理论利润率高达545%!Mar 12, 2025 pm 12:21 PM

DeepSeek公司在知乎发布技术文章,详细介绍了其DeepSeek-V3/R1推理系统,并首次公开关键财务数据,引发业界关注。文章显示,该系统单日成本利润率高达545%,创下全球AI大模型盈利新高。DeepSeek的低成本策略使其在市场竞争中占据优势。其模型训练成本仅为同类产品的1%-5%,V3模型训练成本仅为557.6万美元,远低于竞争对手。同时,R1的API定价仅为OpenAIo3-mini的1/7至1/2。这些数据证明了DeepSeek技术路线的商业可行性,也为AI大模型的高效盈利树立了

美的推出首款DeepSeek空调:AI语音交互 可实现40万 条指令!美的推出首款DeepSeek空调:AI语音交互 可实现40万 条指令!Mar 12, 2025 pm 12:18 PM

美的即将发布搭载DeepSeek大模型的首款空调——美的鲜净感空气机T6,发布会定于3月1日下午1点30分举行。这款空调配备先进的空气智驾系统,可根据环境智能调节温度、湿度和风速等参数。更重要的是,它集成了DeepSeek大模型,支持超过40万条AI语音指令。美的此举引发业界热议,尤其关注白电产品与大模型结合的意义。不同于传统空调简单的温度设定,美的鲜净感空气机T6能够理解更复杂、更模糊的指令,并根据家庭环境智能调节湿度等,显着提升用户体验。

2025年最佳10个最佳免费反向链接检查器工具2025年最佳10个最佳免费反向链接检查器工具Mar 21, 2025 am 08:28 AM

网站建设只是第一步:SEO与反向链接的重要性 建立网站只是将其转化为宝贵营销资产的第一步。您需要进行SEO优化,以提高网站在搜索引擎中的可见度,吸引潜在客户。反向链接是提升网站排名的关键,它向谷歌和其他搜索引擎表明您的网站权威性和可信度。 并非所有反向链接都有利:识别并避免有害链接 并非所有反向链接都有益。有害链接会损害您的排名。优秀的免费反向链接检查工具可以监控链接到您网站的来源,并提醒您注意有害链接。此外,您还可以分析竞争对手的链接策略,从中学习借鉴。 免费反向链接检查工具:您的SEO情报员

百度又一国民产品接入DeepSeek,是想开了还是跟风?百度又一国民产品接入DeepSeek,是想开了还是跟风?Mar 12, 2025 pm 01:48 PM

DeepSeek-R1赋能百度文库与网盘:深度思考与行动的完美融合短短一个月内,DeepSeek-R1已迅速融入众多平台。百度凭借大胆的战略布局,将DeepSeek作为第三方模型伙伴,整合进自身生态系统,这标志着其“大模型 搜索”生态战略的重大进展。百度搜索和文心智能体平台率先接入DeepSeek及文心大模型的深度搜索功能,为用户提供免费的AI搜索体验。同时,“百度一下,你就知道”的经典slogan回归,新版百度APP也整合了文心大模型和DeepSeek的能力,推出“AI搜索”、“全网信息提炼”

使用GO构建网络漏洞扫描仪使用GO构建网络漏洞扫描仪Apr 01, 2025 am 08:27 AM

此基于GO的网络漏洞扫描仪有效地确定了潜在的安全弱点。 它利用了GO的并发功能的速度功能,包括服务检测和漏洞匹配。让我们探索它的能力和道德

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中