キーポイント
入門記事にリストされている基準を使用して、導きます。
契約の概要新しい契約を作成しましょう。Storydao.sol。そのフレームワークは次のとおりです。
SafeMathをインポートして安全な計算を再度行いますが、今回はZeppelinの所有可能な契約も使用します。これにより、誰かがストーリーを「所有」し、特定の管理者のみの機能を実行できます。簡単に言えば、StoryDaoが所有可能になるだけで十分です。
また、この契約では唯一の所有者修飾子を使用しています。関数修飾子は、基本的に機能の拡張機能とプラグインです。唯一の所有者モディファイアは次のようになります: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; } }
関数に所有者のみが追加されると、関数の本体が_に貼り付けられ、前の部分が最初に実行されます。したがって、この修飾子を使用することにより、関数はメッセージ送信者が契約の所有者であるかどうかを自動的にチェックし、それが真である場合は通常どおり継続します。そうでない場合、それはクラッシュします。
ストーリーDAOの料金やその他のパラメーターを変更する関数に唯一の所有者修飾子を使用することにより、管理者のみがこれらの変更を行うことができるようにします。
modifier onlyOwner() { require(msg.sender == owner); _; }
テスト
初期関数をテストしましょう。
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'); };
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セクションは「真の」と述べたイベントを発行します。これは、トリュフがテスト中に聞いていることです。
testStorydao.jsでは、次のコンテンツを置きますテストが正常に実行されるためには、ストーリーダオを展開したいことをトリュフに伝える必要があります。それでは、以前に書いた移行とほぼ同じコンテンツを使用して、3_Deploy_StoryDao.jsを移行中に作成しましょう。
modifier onlyOwner() { require(msg.sender == owner); _; }
および.babelrcファイルを含むファイル:
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'); };
また、トリュフの構成にバベルが要求される必要があるため、テストをコンパイルするときに使用する必要があることがわかります。
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 个条目"); } }
注:BabelはNodejsのアドオンであり、現在の世代のNodejsで次世代JavaScriptを使用できるようにするため、インポートやその他のコンテンツを書くことができます。これを理解していない場合は、それを無視して逐語的に貼り付けてください。インストール後、この問題に二度と対処する必要がない場合があります。
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]})); }) });
さあ、最終的にトリュフテストを実行します。出力はこれに似ている必要があります:
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); } };
このコースの次のセクションでは、逐語的に入力するとチュートリアルが長すぎるため、テストをスキップしますが、プロジェクトの最終ソースコードを参照してすべてのテストを確認してください。完了したばかりのプロセスには、テスト用の環境が設定されているため、さらにセットアップせずにテストを作成できます。
ホワイトリスト
ユーザーがストーリーの構築に参加できるようにするホワイトリストメカニズムを構築しましょう。以下の関数フレームワークをStoryDao.SOLに追加します:
無名の関数関数()はフォールバック関数と呼ばれます。これは、この契約にファンドが送信されたが特定の指示はありません(つまり、他の関数は特異的に呼ばれない)と呼ばれます。これにより、人々はエーテルをDAOのみに送り、ホワイトリストに登録されているかどうかに基づいて、すぐにホワイトリストまたはトークンを購入することでStoryDaoに参加できます。 ホワイトリストの関数はホワイトリストに使用され、直接呼び出すことができますが、送信者がまだホワイトリストに登録されていない場合、フォールバック関数がエーテルを受け取った後に自動的に呼び出すことを確認します。 WhitelistAddress関数は、他の契約からも呼び出されるべきであるため公開されますが、ファンドは外部住所からこのアドレスに送信されるため、フォールバック関数は外部です。この契約を呼び出す契約は、必要な関数を直接簡単に呼び出すことができます。
注:これは安全な計算のためのセーフマス関数であるため、subの代わりに - subを使用します。
結論
このチュートリアルでは、DAOの最初の部分を構築しましたが、まだやるべきことがたくさんあります。ご期待ください:次のセクションでは、ストーリーにコンテンツを追加することを扱います! イーサリアムダップとホワイトリストの構築に関するFAQ
スマート契約は自己執行契約であり、その契約条件はコードに直接記述されます。彼らは、ブロックチェーン上のビジネスロジックの実行を自動化するため、Dappsで重要な役割を果たします。展開されたら、変更したり改ざんしたりすることができないため、透明性、セキュリティ、および不変性を確保します。 テストは、その機能とセキュリティを確保するためのDAPP開発の重要な部分です。トリュフやガナッシュなどのツールを使用してテストできます。トリュフは、イーサリアムに開発環境、テストフレームワーク、アセットパイプラインを提供しますが、Ganacheを使用すると、テスト用のプライベートイーサリアムブロックチェーンを作成できます。 daoは分散型の自律組織を表します。これは、透明性があり、組織メンバーによって管理され、中央政府の影響を受けないコンピュータープログラムとしてエンコードされたルールで表される組織タイプです。 DAOの金融取引とルールはブロックチェーンに保持されているため、DAPPになります。 DAPPのセキュリティにはさまざまなプラクティスが含まれることを確認します。これには、安全なスマートコントラクトの作成、徹底的なテスト、セキュリティ監査の実行、ソフトウェアと依存関係を最新の状態に保つことが含まれます。また、安全なコーディングのためにベストプラクティスに従い、ブロックチェーンスペースの最新のセキュリティの脆弱性と脅威に遅れないことも重要です。 Metamaskは、ブラウザから直接Ethereumブロックチェーンと対話できるブラウザ拡張機能です。また、イーサリアムとERC-20トークンを管理するためのイーサリアムウォレットとしても機能します。ユーザーが完全なイーサリアムノードを実行せずにDAPPと対話できるようにユーザーフレンドリーなインターフェイスをユーザーに提供するため、DAPP開発において重要です。 DAPPを開発およびテストしたら、Ethereum MainNetまたはTestNetに展開できます。これには、スマートコントラクトをコンパイルし、ブロックチェーンに展開し、DAPPをそれらの契約に接続することが含まれます。トリュフやインフラなどのツールを使用して、このプロセスを完了できます。 DAPP開発はいくつかの課題に直面しています。これには、Ethereum Networkのスケーラビリティの問題の処理、DAPPのセキュリティの確保、取引の揮発性ガス価格の管理、ユーザーフレンドリーなインターフェイスの提供が含まれます。また、急速に進化するブロックチェーンテクノロジーと規制に注意する必要があります。 以上がビルディングイーサリアムダップ:ストーリーダオのホワイトリストとテストの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。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;
}
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
イーサリアムDAPPを構築するための前提条件は何ですか?
DAPPSにおけるスマートコントラクトの役割は何ですか?
私のdappをテストする方法は?
DAOとは何ですか?また、DAPPとどう関係していますか?
私のdappのセキュリティを確保する方法は?
メタマスクとは何ですか?また、なぜDAPP開発において重要なのですか?
私のdappを展開する方法は?
DAPP開発の課題は何ですか?
展開後にDAPPを更新するにはどうすればよいですか?
ブロックチェーン上のスマートコントラクトが不変であるため、展開後のDAPPSの更新は困難な場合があります。ただし、データとロジックを異なる契約に分離したり、委任されたコールを使用して契約をアップグレードすることにより、アップグレード可能な契約を設計できます。 DAPPの設計段階でのアップグレードと変更の計画は非常に重要です。