今日、怠zyな人だけがブロックチェーン上で別の役に立たないプロジェクトを起動しません。このレッスンでは、実用的なアプリケーションを実行する方法を説明します。 例として、デジタル署名、Swarm分散ストレージ、およびEthereumベースのスマートコントラクトのみを使用するnpmなどのパッケージレジストリを取り上げます。
注意これは、監査を必要とする契約の試用版です。
やる気
ブロックチェーン上のコードレジストリには、通常よりも多くの利点があります:
- デジタル署名による確認。
- グローバルアクセス。
- トークン化
各項目を個別に検討してください。
デジタル署名検証
これは、信頼できる環境を構築する上で重要なポイントです。 コードは、信頼できる開発者によって署名されている必要があります。 ブロックチェーンの場合、これは自動的に行われます。 現在、必要に応じて、大規模なリポジトリが攻撃され、そのリポジトリ内のコードが偽造されている場合、そのリポジトリを見つけることはほとんどできません。 ブロックチェーンリポジトリは、確認済みのハッシュ量を保存することにより、なりすましから保護します。
グローバルアクセス
ここではすべてが明らかに分散ストレージであるため、データへのグローバルな無修正アクセスが可能になると思います。
トークン化
1人以上の参加者または組織でさえ、パッケージを管理および所有できます。 現在、ほとんどの場合、すべてのアカウントとデータは従業員に登録されているため、そのようなリソースへの権利の譲渡は却下時に問題になる可能性があります。
トークン化により、パッケージを所有する会社は、それを貸借対照表に入れ、無形資産に入力し、より哀れに売ることができます。 また、会社自体を売却する場合、トークン化された資産は簡単に考慮され、価格に含まれます。
テクノロジー
このソリューションを実装するには、EthereumブロックチェーンスマートコントラクトとSwarm分散ファイルシステムを使用します。 Swarmを知らない人にとっては、それはipfsのような分散型ファイルストレージであり、イーサリアム開発者だけのものです。 Swarmのローンチは今年の第4四半期に予定されており、コードが少し変更される可能性があることにすぐに気付きますが、一般的な概念は変わりません。
swarmのファイルはディレクトリとして保存されます。 各ディレクトリには、マニフェスト(ディレクトリ内のすべてのファイルのリスト)が含まれています。 マニフェストは、基本的にファイルパスとそのハッシュの配列を持つJSONです。 各ディレクトリにはbzzアドレスが割り当てられます。これは、マニフェストにリストされているファイルのメルケルツリーの最上部です。 ファイルの識別には、 メッセージの削除による攻撃から保護されたハッシュサムが使用されます 。このため、ハッシュサムを取得する前に、長さの値がバイトオーダーが低から高の64ビット数としてデータの先頭に追加されます。
アルゴリズム
- パッケージは(名前で)スマートコントラクトに登録され、ハッシュ名を取得します。
- コードがswarmにロードされ、bzzアドレスが取得されます。
- パッケージの新しいバージョンが、受信したbzzアドレスでスマートコントラクトに登録されます。
実装
データモデル
バージョンストレージは、名前付きブランチなしでsemverの原則に従って実行されます( alpha
、 rc0
などはサポートされていません)。 すべてのパッケージはリストに保存されます。キーはパッケージの名前で、値はバージョンツリーです。 次のようになります。
// struct Package { address owner; // uint8 latestMajor; // mapping(uint8 => Major) majors; } // , . struct Major { // uint8 latestMinor; // mapping(uint8 => Minor) minors; } // , . struct Minor { // uint16 latestBuild; // . mapping(uint16 => Build) builds; } // bzz-. struct Build { // bzz- bytes32 bzz; // bool isPublished; }
契約メモリ
契約には、3つのリストが使用されます。
// . mapping(bytes32 => Package) packages; // . mapping(bytes32 => bytes) names; // . mapping(bytes32 => address) transfers;
パッケージ登録
パッケージの名前とハッシュを変換するだけでなく、パッケージの名前と所有者を関連付けるには、パッケージの登録が必要です。 名前のハッシュは、任意の長さの名前を持つパッケージでの作業が常に同じ時間がかかる/同じ量のガスを消費するために必要です:
function register(bytes _name) public returns(bytes32) { // , require(_name.length > 0); // bytes32 name = resolve(_name); // , require(packages[name].owner == address(0)); // packages[name] = Package(msg.sender, 0); // names[name] = _name; return name; }
JSからの呼び出し:
const Web3 = require('web3'); const {abi} = require('./contract.js'); const web3 = new Web3(new Web3('https://rinkeby.infura.io/')); // Initialize contract instance const reg = new web3.eth.Contract(abi, '0x57147069B117fD911Da6c43F3fBdC54a7A7D8C1d'); reg.methods.register('hello_world').send() .then((hash) => console.log(hash));
Swarmにダウンロード
データはHTTPプロトコルを使用してSqarmにロードされ、tarアーカイブにファイルを配置してディレクトリをダウンロードできます。
tar -c * | curl -H "Content-Type: application/x-tar" --data-binary @- http://localhost:8500/bzz:/
その結果、次のように、メルケルツリーの32ビットの頂点(bzzアドレス)を取得します。
1e0e21894d731271e50ea2cecf60801fdc8d0b23ae33b9e808e5789346e3355e
swarmからファイルを取得するには、ファイルのリストを取得する必要があります 。
curl -s http://localhost:8500/bzz-list:/ccef599d1a13bed9989e424011aed2c023fce25917864cd7de38a761567410b8/ | jq . > { "common_prefixes": [ "dir1/", "dir2/", "dir3/" ], "entries" : [ { "path": "file.txt", "contentType": "text/plain", "size": 9, "mod_time": "2017-03-12T15:19:55.112597383Z", "hash": "94f78a45c7897957809544aa6d68aa7ad35df695713895953b885aca274bd955" } ] }
実験には、サイトhttps://swarm-gateways.org/を使用できます 。
新しいバージョンを登録する
新しいバージョンを登録するには、名前のハッシュ、バージョン番号(メジャー、マイナー、ビルド)およびbzzアドレスを指定してregisterメソッドを呼び出すだけです。
function publish(bytes32 _package, uint8 _major, uint8 _minor, uint16 _build, bytes32 _bzz) public { Package storage package = packages[_package]; // . require(package.owner == msg.sender); // require(hasBuild(_package, _major, _minor, _build) == false); // package.majors[_major].minors[_minor].builds[_build] = Build(_bzz, true); Major memory major = package.majors[_major]; Minor memory minor = package.majors[_major].minors[_minor]; // if (package.latestMajor < _major) { package.latestMajor = _major; } // . if (major.latestMinor < _minor) { package.majors[_major].latestMinor = _minor; } // . if (minor.latestBuild < _build) { package.majors[_major].minors[_minor].latestBuild = _build; } emit Published(_package, _major, _minor, _build); }
reg.methods.publish(hash, 0, 1, 0, bzz).send();
権利の譲渡
上記のように、パッケージの所有権を転送することができます。この機能を実装するメソッド: transfer
およびreceive
。 transfer
メソッドは新しい所有者を割り当て、その後、新しい所有者はreceive
メソッドを呼び出してパッケージを受け取ることができます。
receive
呼び出しの前に、パッケージ管理は現在の所有者によって保持されます。 これにより、誤ったアクションから保護され、受信側による確認も必要になります。これにより、双方向の転送トランザクションを行うことができます。誰もあなたの同意なしに所有権を課すことはできません。
契約方法
- 登録-契約を登録します。
- resolve-名前をハッシュに変換します。
- 公開-新しいバージョンの登録。
Published
イベントを発生させます。 - 非公開-バージョンの取り消し。
Unpublished
イベントを発生させます。 - getOwner-パッケージの現在の所有者を返します。
- hasBuild-最後に公開されたビルドのステータスを返します。
- isPublished-バージョンのステータスを返します。
- getLatestMajorVersion-パッケージの最後のメジャーバージョンの番号を取得します。
- getLatestMinorVersion-指定されたメジャーバージョンのパッケージの最後のマイナーバージョンの番号を取得します。
- getLatestBuildVersion-指定されたマイナーバージョンのパッケージの最後のビルドの番号を取得します。
- transfer-パッケージを新しい所有者に転送します。
- receive-新しい所有者がパケットを受信します。
制限事項
このスキームは、メジャーバージョンまたはマイナーバージョンを取り消す可能性を排除します。 特定のビルドのみを撤回することが可能です。 マイナーバージョンの最新ビルドも思い出せません。 これを行うには、まず新しいものを公開してから、以前のビルドを呼び出す必要があります。 これにより、最新の最新バージョンを再割り当てするためにバージョンツリーを反復処理する必要がなくなり、消費されるガスの量が削減されます。
おわりに
この形式では、パッケージのすべてのバージョンがハッシュによってグローバルに識別可能になります。これにより、たとえば、CVE番号をコードの特定のバージョンにバインドできます。 そして、ブロックチェーンと分散構造を使用することで、コードの変更はユーザーに確実に届きます。
このコードを改善する方法、問題やPRを残す方法、アイデアをお持ちの方、お楽しみに、星をつけていただければ幸いです。
参照資料
- githubのリポジトリ 。
- 写真: マールテン・ファン・デン・ホイベル 。
注意事項と備考
ユーザーStanislavLの コメント内の質問への回答:
なぜ現在よりも少ない数のバージョンを追加する機能が必要なのですか?
パッケージの一部のバージョンはLTSをサポートしているため、古いバージョンと新しいバージョンがリリースに組み込まれる可能性が非常に高くなります。
複数所有の可能性はありません。
この機能は、開発者のグループごとに大きく異なる可能性があるため、所有者となるサードパーティの契約に実装する必要があります。 これにより、レジストリコードが簡素化され、単一の実装へのバインドがなくなります。
次のビルドを登録する際にガスを燃やす(=お金を使う)ポイントは何ですか?
Semverは、パッケージコード自体がビルドによってバージョン管理されていると想定しています;メジャーブランチとマイナーブランチはインターフェイスを示します。 つまり、隣接するビルド間のコントラクトコードは100%書き換えることができますが、インターフェイスは同じままです。
イベントイベントTransfered(bytes32 indexed package)
ます。
しかしOwnershipChanged
ません。 また、パッケージだけでなく、所有者の候補者の住所も追加します。
Transfered
イベントはOwnershipChanged
です。受信側がreceiveを呼び出し、所有者が変更されたときに呼び出されます。 このデータはトランザクションから取得できるため、候補者の住所は追加のガスを消費しないようにログに記録されません。