Ethereum : Proof of Existence(公証)のサンプルコード

**【本文】**
**Proof of Existence**は、Factom等で聞き馴染みのある通り、決して目新しいものではないが、公証したい事実（契約内容でも画像データの所有権でも何でも良いが。）をハッシュ化し、ブロックチェーン上に保存しておき（Ethereumの場合はスマートコントラクトを利用してステートに保持、等）、実際に公証する必要が生じた際に、再度、公証したい事実の真正性をハッシュ値の突合せで確認するシンプルな仕組みで実現できる。
（※尤も、実際のビジネスユースではより複雑化するが。）

上手く他の仕掛けと一緒に用いることで、例えば、数年後にある取引（もしくは遺言のようなものでもよいか。）を履行するとした場合に、実際の契約内容（ドキュメント）が同一のものであった場合に、スマートコントラクトを通じて契約履行する、と言う使い方に用いることが出来る。

今回、簡単なサンプルコードを用いて、簡易的なProof of Existenceをコードベースで紹介する。

**備考）**
Proof of Existenceは、__ドキュメントのハッシュ値をブロックチェーン上に保持しておくことで、ブロックチェーンへのサイズ軽量化にも寄与__する点に留意。
実際のドキュメントは、当面は各個人に委ねられるものの、何れはIPFSのようなところに保持するのが何れ主流になってくるのではないか。
（※現実世界では、ドキュメントの保管そのものに課題がありそう。）

【今回の環境構成】
---

自端末ローカル環境上で構築

+ truffle
+ Infura　※ローカル試行では、Ganache
+ (metamask) ※今回は使用しない
+ Rinkeby (Ethereumのテストネットワーク)


【実際のコード】
---

+ ***notarize***関数を通じて、インプットしたドキュメントのハッシュ値を取り、ステートに保***存
+ ***checkNotarizedDoc***関数を通じて、公証したいドキュメントが、既にステートに保持されているドキュメントのハッシュ値と一致するかを検証（一致：true　不一致：false）
+ ***getNotraizedInfo***関数を通じて、既にステートに保持されているドキュメントのハッシュ値がどの時点でブロックチェーン上に記録(blockNumber)されているか確認

**Proof of Existenceのコントラクトコード**


```javascript:PoEContract.sol
pragma solidity ^0.5.0;

contract PoEContract {

  struct NotarizedRecord {
    uint blockTime;
    uint blockNumber;
  }

  mapping (bytes32 => bool) private proofs;
  mapping (bytes32 => NotarizedRecord) private notarizedInfo;
  
  // store a proof of existence in the contract state
  function storeProof(bytes32 proof) 
    internal 
  {
    proofs[proof] = true;
    NotarizedRecord memory newRecord = NotarizedRecord(block.timestamp, block.number);
    notarizedInfo[proof] = newRecord;
  }
  
  // calculate and store the proof for a document
  function notarize(string calldata document) 
    external 
  { 
    bytes32 proof = proofFor(document);
    storeProof(proof);
  }
  
  // helper function to get a document's hash
  function proofFor(string memory document) 
    pure 
    public 
    returns (bytes32) 
  {
    return keccak256(bytes(document));
  }
  
  // check notarized document or not
  function checkNotarizedDoc(string memory document) 
    public 
    view 
    returns (bool) 
  {
    bytes32 proof = proofFor(document);
    return hasProof(proof);
  }

  // returns true if proof is stored
  function hasProof(bytes32 proof)
    internal 
    view 
    returns(bool) 
  {
     return proofs[proof];
  }

  function getNotarizedInfo (string memory document) 
    public 
    view 
    returns(uint, uint) 
  {
    bytes32 proof = proofFor(document);
    return (notarizedInfo[proof].blockTime, notarizedInfo[proof].blockNumber);
  }
}
```

+ Infuraを利用してRinkebyにPoEContractをデプロイ
+ truffle consoleでRinkebyに繋ぐ。

```javascript:console(truffle-Rinkeby)
truffle(rinkeby)> var poe = await PoEContract.at(PoEContract.address)
```

+ デプロイ後、任意のドキュメント（何でもいいから適当な文章）を直接インプット

```javascript:truffle(console)
truffle(rinkeby)> poe.notarize('This is Proof of Existance Test! xxx')
```
(※実際には、***xxx***には2020/10/28の記事を掲載したサイトのURLを指定している）

```javascript:実行結果
truffle(rinkeby)> poe.notarize('This is Proof of Existance Test! xxx')
{
  tx: '0xc58ad20ef73f108c898d9b1c2fb32b4f966632eb5a62790017090d81d037ef55',
  receipt: {
    blockHash: '0x94732185a8b1c7abf73b8216bdbe4f9e0e2f4991b55262b4801d8f62b2ec3151',
    blockNumber: 7447437,
    contractAddress: null,
    cumulativeGasUsed: 911008,
    from: '0x74ee80cda234c227b7614bc37bb2ca2119ac2d6f',
    gasUsed: 84300,
    logs: [],
    logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    status: true,
    to: '0x0ba2e559cca812e1785380b6977c3c0898d8b682',
    transactionHash: '0xc58ad20ef73f108c898d9b1c2fb32b4f966632eb5a62790017090d81d037ef55',
    transactionIndex: 17,
    rawLogs: []
  },
  logs: []
}
```

+ 実際に、Rinkeby(Etherscan)より確認

https://rinkeby.etherscan.io/tx/0xc58ad20ef73f108c898d9b1c2fb32b4f966632eb5a62790017090d81d037ef55

![poe_ebi2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167054/459f19cd-8008-99e7-5006-8940e751aeab.png)

+ 重要なポイントとして、Proof of Existenceは、ドキュメントそのものをブロックチェーン上に記録するないしステートとして保持するのではなく、ドキュメントのハッシュ値をステートに保存し、次回以降、ドキュメントを公証したい場合に、再度、インプットとなるドキュメントのハッシュ値とステートで保持するハッシュ値が一致するかどうかでドキュメントの真正性を確認することに留意

```javascript:console(truffle-Rinkeby)
truffle(rinkeby)> poe.checkNotarizedDoc('This is Proof of Existance Test! xxx')
true
```

+ 上記のように同じドキュメントをインプットした場合は、ステートに保持されたハッシュ値と一致し、trueが返る

```javascript:console(truffle-Rinkeby)
truffle(rinkeby)> poe.checkNotarizedDoc('This is Proof of Existance Test xxx')
false
```

+ 一文字でも異なる場合、ステートに保持されたハッシュ値が一致せず、falseが返る
+ 因みに、PoEContractでは、Notarize(公証)された時点でのblockNumberも記録している為、タイムスタンプとしての役割も果たす。

```javascript:console(truffle-Rinkeby)
truffle(rinkeby)> poe.getNotarizedInfo('This is Proof of Existance Test! xxx')
Result {
  '0': BN {
    negative: 0,
    words: [ 60365474, 23, <1 empty item> ],
    length: 2,
    red: null
  },
  '1': BN {
    negative: 0,
    words: [ 7447437, <1 empty item> ],
    length: 1,
    red: null
  }
}
```

+ ステートに保持しているblockNumberは'7447437'とあり、Rinkeby上でドキュメントが公証された際のblockNumberと一致している


以上

後記）
よく見たら、Existenceの綴り誤ってましたが、ご愛敬と言うことでw
**Existance → Existence**