Maison > Périphériques technologiques > Industrie informatique > Construire Ethereum Dapps: Liste blanche et test d'une histoire Dao

Construire Ethereum Dapps: Liste blanche et test d'une histoire Dao

Lisa Kudrow
Libérer: 2025-02-16 12:24:15
original
175 Les gens l'ont consulté

Building Ethereum DApps: Whitelisting & Testing a Story DAO

Points clés

  • Story Dao utilise le contrat propriétaire d'Openzeppelin pour s'assurer que seul le propriétaire peut effectuer des fonctions de gestion, améliorant ainsi la sécurité et le contrôle des opérations DAPP.
  • Les contrats DAO de l'histoire ont des paramètres de frais et de durée réglables et sont équipés de mesures de sécurité pour empêcher les modifications non autorisées, garantissant que seul le propriétaire peut modifier les paramètres critiques.
  • La gestion de la liste blanche dans Story Dao est mise en œuvre par le biais de fonctionnalités payantes qui permettent un accès automatique et conditionnel en fonction de la contribution de l'expéditeur.
  • Les stratégies de test intégrées, y compris la solidité et les tests JavaScript, sont essentielles pour vérifier la fonctionnalité et la sécurité de l'histoire DAO, garantissant une opération robuste avant le déploiement.
  • Le processus de déploiement de l'histoire DAO est simplifié par la truffe, avec des scripts et configurations de migration spécifiques, ce qui facilite une transition en douceur de l'environnement de développement vers l'environnement de production.

La troisième partie de cette série de didacticiels décrit la construction d'un DAPP en utilisant Ethereum, où nous construisons et déploiez le jeton sur le réseau de test Ethereum Rinkeby. Dans cette section, nous commencerons à écrire l'histoire Dao Code.

Nous nous guiderons en utilisant les critères répertoriés dans l'article d'introduction.

Présentation du contrat

Créons un nouveau contrat storydao.sol, dont le cadre est le suivant:

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;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Nous importons Safemath pour effectuer à nouveau des calculs sûrs, mais cette fois, nous utilisons également le contrat propriétaire de Zeppelin, qui permet à quelqu'un de «posséder» l'histoire et d'exécuter certaines fonctions administratrices uniquement. Autrement dit, il suffit que notre Storydao soit propriétaire; n'hésitez pas à vérifier le contrat pour comprendre comment cela fonctionne.

Nous utilisons également le seul modificateur de propriétaire dans ce contrat. Les modificateurs de fonction sont essentiellement des extensions et des plug-ins pour les fonctions. Le seul modificateur de propriétaire ressemble à ceci:

modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Lorsque uniquement est ajouté à la fonction, le corps de la fonction sera collé à l'endroit où se trouve la partie _; Par conséquent, en utilisant ce modificateur, la fonction vérifie automatiquement si l'expéditeur de messages est propriétaire du contrat, puis se poursuit comme d'habitude s'il est vrai. Sinon, il se bloquera.

En utilisant le seul modificateur de propriétaire uniquement sur les fonctions qui modifient les frais et autres paramètres de l'histoire DAO, nous nous assurons que seuls les administrateurs peuvent apporter ces modifications.

Tester

Testons la fonction initiale.

s'il n'existe pas, créez un test de dossier. Créez ensuite les fichiers TestStorydao.sol et TestStoryDao.js. Puisqu'il n'y a pas de méthode native dans la truffe pour tester les exceptions, les aides / windthrow.js sont également créés en utilisant les éléments suivants:

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');
  };
Copier après la connexion
Copier après la connexion

Remarque: Les tests de solidité sont généralement utilisés pour tester les fonctions basées sur les contrats de bas niveau, c'est-à-dire la structure interne des contrats intelligents. Les tests JS sont souvent utilisés pour tester si un contrat peut interagir correctement de l'extérieur, ce que feront nos utilisateurs finaux.

dans TestStoryDao.Sol, mettez le contenu suivant:

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;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Cela vérifiera que le contrat Storydao est correctement déployé avec les chiffres de frais et de durée corrects. La première ligne garantit qu'elle est déployée en la lisant à partir de la liste d'adresses déployée, et la dernière section fait des affirmations - Vérifiez si la déclaration est vraie ou fausse. Dans notre exemple, nous comparons le nombre à la valeur initiale du contrat déployé. Chaque fois qu'il est "vrai", la section ASSERT.Equals publiera un événement qui stipule "vrai", ce que la truffe écoute pendant les tests.

Dans TestStorydao.js, mettez le contenu suivant:

modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Pour que nos tests fonctionnent avec succès, nous devons également dire à Truffle que nous voulons déployer Storydao - car cela ne le fera pas pour nous. Créons donc 3_deploy_storydao.js dans la migration en utilisant presque le même contenu que la migration que nous avons écrite avant:

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');
  };
Copier après la connexion
Copier après la connexion

À ce stade, nous devons également mettre à jour (ou créer, sinon) le fichier package.json dans la racine du dossier du projet, contenant les dépendances dont nous avons besoin jusqu'à présent, et ce dont nous pouvons avoir besoin à l'avenir:

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 个条目");
    }
}
Copier après la connexion
Les fichiers

et .babelrc contenant les éléments suivants:

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]}));
    })
});
Copier après la connexion

Nous devons également exiger Babel dans la configuration de la truffe afin qu'il sache qu'il doit être utilisé lors de la compilation de tests.

Remarque: Babel est un module complémentaire à NodeJS qui nous permet d'utiliser le JavaScript de nouvelle génération dans la génération actuelle de NodeJS, afin que nous puissions écrire des importations et d'autres contenus. Si vous ne comprenez pas cela, ignorez-le et collez-le textuellement. Après l'installation, vous n'aurez peut-être plus jamais à traiter ce problème.

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);
  }
};
Copier après la connexion

Maintenant, exécutez enfin le test de truffe. La sortie doit être similaire à ceci:

Building Ethereum DApps: Whitelisting & Testing a Story DAO

Pour plus d'informations sur les tests, consultez ce tutoriel, que nous avons préparé spécifiquement pour couvrir les tests de contrats intelligents.

Dans la section suivante de ce cours, nous sauterons les tests car les taper textuellement rendra le tutoriel trop long, mais veuillez vous référer au code source final du projet pour vérifier tous les tests. Le processus que nous venons de terminer a un environnement configuré pour les tests, afin que vous puissiez rédiger les tests sans autre configuration.

liste blanche

Créons un mécanisme de liste blanche qui permet aux utilisateurs de participer à la construction d'histoires. Ajoutez le cadre de fonction suivant à 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"
  }
}
Copier après la connexion

Fonction sans nom () est appelée une fonction de secours, qui est appelée lorsque les fonds sont envoyés à ce contrat mais aucune instruction spécifique (c'est-à-dire aucune autre fonction n'est spécifiquement appelée). Cela permet aux gens de rejoindre Storydao en envoyant de l'éther à DAO uniquement et à la liste blanche ou à l'achat de jetons immédiatement en fonction de leur liste blanche ou non.

La fonction WhitelistSender est utilisée pour la liste blanche et peut être appelée directement, mais nous nous assurerons que la fonction de secours l'appelle automatiquement après avoir reçu de l'éther, à condition que l'expéditeur ne soit pas encore liste à blanc. La fonction WhitelistAddress est déclarée publique car elle doit également être appelée à partir d'autres contrats, tandis que la fonction de secours est externe car les fonds ne sont envoyés que des adresses externes à cette adresse. Le contrat qui appelle ce contrat peut facilement appeler directement les fonctions requises.

Faisons d'abord la fonction de secours.

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;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Nous vérifions si l'expéditeur n'est pas encore liste à blanc et délégue l'appel à la fonction WhiteListAddress. Notez que nous avons commenté la fonction Buytokens parce que nous ne l'avons pas encore.

Ensuite, traitons avec la liste blanche.

modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Notez que cette fonction accepte une adresse comme argument, plutôt que de l'extraire d'un message (d'une transaction). Cela a l'avantage supplémentaire de pouvoir les autres si quelqu'un ne peut pas se permettre de rejoindre le DAO, par exemple.

Nous commençons par quelques chèques de santé mentale: l'expéditeur doit être liste à blanc ou sur liste noire (interdite) et doit avoir envoyé suffisamment de frais pour payer. Si ces conditions sont satisfaisantes, l'adresse est ajoutée à la liste blanche, un événement de liste blanche est émis, et enfin, si le nombre d'éthers envoyés est supérieur au nombre d'éthers requis pour payer les frais de liste blanche, le reste sera utilisé pour acheter les jetons.

Remarque: nous utilisons Sub au lieu de - Soustraire, car il s'agit de la fonction SafeMath pour les calculs sûrs.

Tant que les utilisateurs envoient 0,01 éther ou plus d'éther au contrat Storydao, ils peuvent désormais eux-mêmes ou les autres.

Conclusion

Dans ce tutoriel, nous avons construit la partie initiale de DAO, mais il y a encore beaucoup de travail à faire. Restez à l'écoute: dans la section suivante, nous traiterons de l'ajout de contenu à l'histoire!

FAQ sur la construction de Dapps Ethereum et de listes blanches

Quelles sont les conditions préalables à la construction d'un Dapp Ethereum?

Avant de commencer à construire un Dapp Ethereum, vous devez avoir une compréhension de base de la technologie de la blockchain, des contrats Ethereum et intelligents. Vous devez également être familier avec les langages de programmation tels que JavaScript et Solidity, qui sont utilisés pour rédiger des contrats intelligents sur Ethereum. De plus, vous devez installer des outils tels que Node.js, Truffle, Ganache et Metamask qui sont essentiels au développement et au test des DAPP.

Comment fonctionne le processus de liste blanche dans DAPP?

Les listes blanches sont des mesures de sécurité dans les DAPP qui restreignent l'accès à certaines caractéristiques ou zones de l'application. Il s'agit de créer une liste d'adresses approuvées qui permettent une interaction avec les DAPP. Seules les transactions initiées à partir de ces adresses seront acceptées, tandis que d'autres seront rejetées. Cela aide à prévenir l'accès non autorisé et l'activité malveillante.

Quel est le rôle des contrats intelligents dans les DAPP?

Les contrats intelligents sont des contrats auto-exécutés et leurs conditions d'accord sont directement rédigées dans le code. Ils jouent un rôle essentiel dans les DAPP car ils automatisent l'exécution de la logique métier sur la blockchain. Ils garantissent la transparence, la sécurité et l'immuabilité car une fois déployés, ils ne peuvent pas être modifiés ou falsifiés.

Comment tester mon DAPP?

Les tests sont une partie cruciale du développement DAPP pour assurer sa fonctionnalité et sa sécurité. Vous pouvez utiliser des outils comme la truffe et la ganache pour tester. Truffle fournit à Ethereum un environnement de développement, un cadre de test et un pipeline d'actifs, tandis que Ganache vous permet de créer une blockchain privée Ethereum pour les tests.

Qu'est-ce que DAO et comment cela a-t-il à voir avec DAPP?

DAO représente une organisation autonome décentralisée. Il s'agit d'un type d'organisation représenté par des règles codées comme des programmes informatiques transparents, contrôlés par les membres de l'organisation et non affectés par le gouvernement central. Les transactions et les règles financières de DAO sont maintenues sur la blockchain, ce qui en fait un DAPP.

Comment assurer la sécurité de mon DAPP?

Assurer que la sécurité du DAPP implique une variété de pratiques. Cela comprend la rédaction de contrats intelligents sécurisés, les tests approfondis, la réalisation d'audits de sécurité et la maintenance des logiciels et des dépendances à jour. Il est également important de suivre les meilleures pratiques pour le codage sécurisé et de se tenir au courant des dernières vulnérabilités et menaces de sécurité dans l'espace de la blockchain.

Qu'est-ce que Metamask et pourquoi est-il important dans le développement du DAPP?

Metamask est une extension de navigateur qui vous permet d'interagir avec la blockchain Ethereum et les DAPPS directement à partir de votre navigateur. Il peut également servir de portefeuille Ethereum pour gérer vos jetons Ethereum et ERC-20. Il est important dans le développement DAPP car il fournit aux utilisateurs une interface conviviale afin que les utilisateurs puissent interagir avec votre DAPP sans exécuter un nœud Ethereum complet.

Comment déployer mon DAPP?

Une fois que vous avez développé et testé votre DAPP, vous pouvez le déployer dans le mainnet Ethereum ou TestNet. Cela implique de compiler vos contrats intelligents, de les déployer sur la blockchain et de connecter vos DAPP à ces contrats. Vous pouvez terminer ce processus à l'aide d'outils tels que Truffle et Infura.

Quels sont les défis du développement du DAPP?

DAPP Development est confronté à certains défis. Cela comprend la gestion des problèmes d'évolutivité du réseau Ethereum, d'assurer la sécurité des DAPP, de gérer les prix volatils du gaz pour les transactions et de fournir une interface conviviale. Cela nécessite également de garder un œil sur les technologies et les réglementations de la blockchain en évolution rapide.

Comment mettre à jour mon DAPP après le déploiement?

La mise à jour des DAPP après déploiement peut être difficile car les contrats intelligents sur la blockchain sont immuables. Cependant, vous pouvez concevoir des contrats améliorables en séparant les données et la logique en différents contrats ou la mise à niveau de contrats à l'aide d'appels délégués. La planification des mises à niveau et des changements pendant la phase de conception du DAPP est très importante.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal