LoginSignup
9
2

More than 5 years have passed since last update.

nem catapult secret lock/proof tx 異なる2つのMIJIN_TESTチェーン間でのアトミックスワップ

Last updated at Posted at 2018-06-03

公式ドキュメントにあるアトミックスワップの手順をやってみます。

公式ドキュメント

Running a node

Writing your first application

Using secret lock transaction for atomic cross-chain swap

概要

ここに異なる2つのMIJIN_TESTチェーンがあるとします。それぞれ、「Public Chain」「Private Chain」とします。

片方で、AliceからBobへXEMを送信し、もう片方で、BobからAliceへモザイクを送信します。

secretlocktx-Page-2.png

AliceとBobに信頼関係があれば、それぞれのチェーンでTransferトランザクションを送ればいいのです。

もし、そうでない場合に、信頼せずに取引が完了するようにしたい。

その時に使うのが、secret lockとsecret proofトランザクションです。

秘密の値を介して、モザイクをロックする、アンロックする

Secret Lockトランザクションは、モザイクを送信するトランザクションです。Transferトランザクションと違う点は、Secret Proofトランザクションとセットになってなければならない点です。

Secret Lockトランザクションには、秘密の値のハッシュが埋め込まれています。Secret Proofトランザクションにより、秘密の値を知っていることを証明できれば、晴れてモザイクを受け取れます。

secretlocktx-Page-2 (4).png

秘密の値は、トランザクションで教える

AliceはBobにXEMを送るSecret lockトランザクションを作成します。

同じく、BobはAliceにモザイクを送るSecret lockトランザクションを作成します。

ここで、Aliceは、自分宛てのSecret lockトランザクションを解除すると同時に、秘密の値をBobに開示します。

するとBobも、自分宛てのSecret lockトランザクションを解除できます。

これがsecret lockとsecret proofトランザクションを使ったアトミックスワップです。

secretlocktx-Page-1 (1).png

これらの図は、draw.ioで作成しました。オリジナルファイルは、githubにあります。

環境

ディレクトリ構成

<root>
├── catapult-service-bootstrap-public
│   ├── LICENSE
│   ├── README.md
│   ├── bin
│   ├── build
│   ├── clean-all
│   ├── clean-data
│   ├── data
│   ├── docker-compose-mmapv1.yml
│   ├── docker-compose.yml
│   ├── dockerfiles
│   ├── ruby
│   └── static-config
├── catapult-service-bootstrap-private
│   ├── LICENSE
│   ├── README.ja.md
│   ├── README.md
│   ├── bin
│   ├── build
│   ├── clean-all
│   ├── clean-data
│   ├── data
│   ├── docker-compose-mmapv1.yml
│   ├── docker-compose.yml
│   ├── dockerfiles
│   ├── ruby
│   └── static-config
└── nem2-sdk
    ├── node_modules
    ├── package-lock.json
    ├── secretlock1.js
    ├── secretlock2.js
    ├── secretlock3.js
    ├── secretlock4.js
    └── secretlock_disp.js

使用するアカウント

Public:
  Alice:
    private: 7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4
    public: 5D9513282B65A12A1B68DCB67DB64245721F7AE7822BE441FE813173803C512C
    address: SBWEUWON6IBHCW5IC4EI6V6SMTVJGCJWGLF57UGK
  Bob:
    private: 31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E
    public: 3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57
    address: SB2Y5ND4FDLBIO5KHXTKRWODDG2QHIN73DTYT2PC
Private:
  Alice:
    private: 8CBA73B9DF31D85DCFA4B9E1D3B7A88E5A5F32C930FCC45FE5A457619F3E6687
    public: C36F5BDDE8B2B586D17A4E6F4B999DD36EBD114023C1231E38ABCB1976B938C0
    address: SBNRDTMZ3MF4RFNQA2ZA33G74EGTINCKX2EHYYHY
  Bob:
    private: BA46F91D7BCD40B6482E138F0D3D53A29F0A6097B5C4586C6ABD81A3BA3A2DAD
    public: 1C650F49DD67EC50BFDEA40906D32CDE3C969BDF58837C7DA320829BDDE96150
    address: SCBCMLVDJBXARCOI6XSKEU3ER2L6HH7UBEPTENGQ

準備

ライブラリなど

ubuntu立ち上げ(仮想でも物理でも)
jqをインストール
gitをインストール
nodejsをインストール
dockerをインストール
docker-composeをインストール

nem2-sdkをインストール

# mkdir nem2-sdk && cd nem2-sdk
# npm i nem2-sdk

nem2-cliをインストール

# npm i -g nem2-cli

tech-bureau/catapult-service-bootstrapをインストール

publicとprivateの2つを作ります。

# git clone https://github.com/tech-bureau/catapult-service-bootstrap.git catapult-service-bootstrap-public
# cp -r catapult-service-bootstrap-public catapult-service-bootstrap-private

publicのチェーンを立ち上げます。

# cd catapult-service-bootstrap-public
# docker-compose up -d
Creating catapult-service-bootstrap-public_generate-raw-addresses_1 ... done
Creating catapult-service-bootstrap-public_db_1                     ... done
Creating catapult-service-bootstrap-public_generate-configs_1       ... done
Creating catapult-service-bootstrap-public_store-addresses_1        ... done
Creating catapult-service-bootstrap-public_init-db_1                ... done
Creating catapult-service-bootstrap-public_peer-node-1-nemgen_1     ... done
Creating catapult-service-bootstrap-public_peer-node-0-nemgen_1     ... done
Creating catapult-service-bootstrap-public_rest-gateway_1           ... done
Creating catapult-service-bootstrap-public_api-node-0-nemgen_1      ... done
Creating catapult-service-bootstrap-public_api-node-0_1             ... done
Creating catapult-service-bootstrap-public_peer-node-1_1            ... done
Creating catapult-service-bootstrap-public_peer-node-0_1            ... done
# docker-compose ps
                           Name                                         Command               State            Ports
-----------------------------------------------------------------------------------------------------------------------------
catapult-service-bootstrap-public_api-node-0-nemgen_1        bash -c /bin-mount/wait /s ...   Exit 0
catapult-service-bootstrap-public_api-node-0_1               bash -c /bin-mount/wait /s ...   Up
catapult-service-bootstrap-public_db_1                       docker-entrypoint.sh bash  ...   Up       27017/tcp
catapult-service-bootstrap-public_generate-configs_1         ash -c /bin-mount/wait /ad ...   Exit 0
catapult-service-bootstrap-public_generate-raw-addresses_1   bash -c /bin-mount/generat ...   Exit 0
catapult-service-bootstrap-public_init-db_1                  docker-entrypoint.sh bash  ...   Exit 0
catapult-service-bootstrap-public_peer-node-0-nemgen_1       bash -c /bin-mount/wait /s ...   Exit 0
catapult-service-bootstrap-public_peer-node-0_1              bash -c /bin-mount/wait /s ...   Up
catapult-service-bootstrap-public_peer-node-1-nemgen_1       bash -c /bin-mount/wait /s ...   Exit 0
catapult-service-bootstrap-public_peer-node-1_1              bash -c /bin-mount/wait /s ...   Up
catapult-service-bootstrap-public_rest-gateway_1             ash -c /bin-mount/wait /st ...   Up       0.0.0.0:3000->3000/tcp
catapult-service-bootstrap-public_store-addresses_1          ash -c /bin-mount/wait /ad ...   Exit 0

privateのほうのrest-gatewayのportsを、3100:3000にします。

# cd catapult-service-bootstrap-private
# vi docker-compose.yml
rest-gateway:
    ports:
    - "3100:3000"

privateのチェーンを立ち上げます

# docker-compose up -d
# docker-compose ps
                           Name                                          Command               State            Ports
------------------------------------------------------------------------------------------------------------------------------
catapult-service-bootstrap-private_api-node-0-nemgen_1        bash -c /bin-mount/wait /s ...   Exit 0
catapult-service-bootstrap-private_api-node-0_1               bash -c /bin-mount/wait /s ...   Up
catapult-service-bootstrap-private_db_1                       docker-entrypoint.sh bash  ...   Up       27017/tcp
catapult-service-bootstrap-private_generate-configs_1         ash -c /bin-mount/wait /ad ...   Exit 0
catapult-service-bootstrap-private_generate-raw-addresses_1   bash -c /bin-mount/generat ...   Exit 0
catapult-service-bootstrap-private_init-db_1                  docker-entrypoint.sh bash  ...   Exit 0
catapult-service-bootstrap-private_peer-node-0-nemgen_1       bash -c /bin-mount/wait /s ...   Exit 0
catapult-service-bootstrap-private_peer-node-0_1              bash -c /bin-mount/wait /s ...   Up
catapult-service-bootstrap-private_peer-node-1-nemgen_1       bash -c /bin-mount/wait /s ...   Exit 0
catapult-service-bootstrap-private_peer-node-1_1              bash -c /bin-mount/wait /s ...   Up
catapult-service-bootstrap-private_rest-gateway_1             ash -c /bin-mount/wait /st ...   Up       0.0.0.0:3100->3000/tcp
catapult-service-bootstrap-private_store-addresses_1          ash -c /bin-mount/wait /ad ...   Exit 0

0.0.0.0:3100->3000/tcpと、ホストの3100とコンテナの3000がつながっていることがわかります。

nem2-cliのプロファイル作成

# nem2-cli profile create -p 7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4 -n MIJIN_TEST -u http://localhost:3000 --profile alicepub
# nem2-cli profile create -p 31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E -n MIJIN_TEST -u http://localhost:3000 --profile bobpub
# nem2-cli profile create -p 8CBA73B9DF31D85DCFA4B9E1D3B7A88E5A5F32C930FCC45FE5A457619F3E6687 -n MIJIN_TEST -u http://localhost:3100 --profile alicepriv
# nem2-cli profile create -p BA46F91D7BCD40B6482E138F0D3D53A29F0A6097B5C4586C6ABD81A3BA3A2DAD -n MIJIN_TEST -u http://localhost:3100 --profile bobpriv

ネームスペースとモザイク作成

PrivateチェーンのBobアカウントで、foo:barモザイクを作成します。

# nem2-cli transaction namespace -n foo -r -d 1000 --profile bobpriv
Transaction announced correctly
Hash:    9673687AFE91EE61155E6013B5ACE1FBB9918AD433E3D1A302F912EE77E9542D
Signer:  1C650F49DD67EC50BFDEA40906D32CDE3C969BDF58837C7DA320829BDDE96150

# nem2-cli transaction mosaic -m bar -n foo -a 100 -t -d 0 -u 1000 --profile bobpriv
Do you want mosaic to have supply mutable? [y/n]: n
Do you want mosaic to have levy mutable? [y/n]: n
Transaction announced correctly
Hash:    E876DE6EACA8ECDC41C664B3E690F0BFDA537CB277FFEBB2E446FD4956950BC9
Signer:  1C650F49DD67EC50BFDEA40906D32CDE3C969BDF58837C7DA320829BDDE96150

コード

ライブラリ参照と固定情報

これから登場するコードには、先頭部分にこれがくっついています。

const nem2Sdk = require("nem2-sdk");
const crypto = require("crypto");
const jssha3 = require('js-sha3');

const Address = nem2Sdk.Address,
    Deadline = nem2Sdk.Deadline,
    Account = nem2Sdk.Account,
    UInt64 = nem2Sdk.UInt64,
    NetworkType = nem2Sdk.NetworkType,
    PlainMessage = nem2Sdk.PlainMessage,
    TransferTransaction = nem2Sdk.TransferTransaction,
    Mosaic = nem2Sdk.Mosaic,
    MosaicId = nem2Sdk.MosaicId,
    TransactionHttp = nem2Sdk.TransactionHttp,
    AccountHttp = nem2Sdk.AccountHttp,
    MosaicHttp = nem2Sdk.MosaicHttp,
    NamespaceHttp = nem2Sdk.NamespaceHttp,
    MosaicService = nem2Sdk.MosaicService,
    XEM = nem2Sdk.XEM,
    AggregateTransaction = nem2Sdk.AggregateTransaction,
    PublicAccount = nem2Sdk.PublicAccount,
    LockFundsTransaction = nem2Sdk.LockFundsTransaction,
    Listener = nem2Sdk.Listener,
    CosignatureTransaction = nem2Sdk.CosignatureTransaction,
    SecretLockTransaction = nem2Sdk.SecretLockTransaction,
    SecretProofTransaction = nem2Sdk.SecretProofTransaction,
    HashType = nem2Sdk.HashType;

const sha3_512 = jssha3.sha3_512;


const urlPublic = 'http://localhost:3000';
const urlPrivate = 'http://localhost:3100';

const transactionHttpPublic = new TransactionHttp(urlPublic);
const transactionHttpPrivate = new TransactionHttp(urlPrivate);


const aliceAddressPrivate = Address.createFromRawAddress('SBNRDTMZ3MF4RFNQA2ZA33G74EGTINCKX2EHYYHY');
const aliceAddressPublic = Address.createFromRawAddress('SBWEUWON6IBHCW5IC4EI6V6SMTVJGCJWGLF57UGK');
const bobAddressPrivate = Address.createFromRawAddress('SCBCMLVDJBXARCOI6XSKEU3ER2L6HH7UBEPTENGQ');
const bobAddressPublic = Address.createFromRawAddress('SB2Y5ND4FDLBIO5KHXTKRWODDG2QHIN73DTYT2PC');

secretlock_disp.js

AliceとBobの保有モザイクを表示するコード。

MosaicServiceを使えば、すべてのモザイクの保有量を表示してくれます。

secretlock_disp.js
const alicePrivateKeyPrivate = '8CBA73B9DF31D85DCFA4B9E1D3B7A88E5A5F32C930FCC45FE5A457619F3E6687';
const aliceAccountPrivate = Account.createFromPrivateKey(alicePrivateKeyPrivate, NetworkType.MIJIN_TEST);
const alicePrivateKeyPublic = '7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4';
const aliceAccountPublic = Account.createFromPrivateKey(alicePrivateKeyPublic, NetworkType.MIJIN_TEST);

const bobPrivateKeyPrivate = 'BA46F91D7BCD40B6482E138F0D3D53A29F0A6097B5C4586C6ABD81A3BA3A2DAD';
const bobAccountPrivate = Account.createFromPrivateKey(bobPrivateKeyPrivate, NetworkType.MIJIN_TEST);
const bobPrivateKeyPublic = '31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E';
const bobAccountPublic = Account.createFromPrivateKey(bobPrivateKeyPublic, NetworkType.MIJIN_TEST);


const accountHttpPublic = new AccountHttp(urlPublic);
const mosaicHttpPublic = new MosaicHttp(urlPublic);
const namespaceHttpPublic = new NamespaceHttp(urlPublic);
const mosaicServicePublic = new MosaicService(accountHttpPublic, mosaicHttpPublic, namespaceHttpPublic);

const accountHttpPrivate = new AccountHttp(urlPrivate);
const mosaicHttpPrivate = new MosaicHttp(urlPrivate);
const namespaceHttpPrivate = new NamespaceHttp(urlPrivate);
const mosaicServicePrivate = new MosaicService(accountHttpPrivate, mosaicHttpPrivate, namespaceHttpPrivate);

const alisBobMosaicsAmountView = () => {
  const mosaicsAmountViewFromAddress = (logPrefix, mosaicService, address) => {
      mosaicService.mosaicsAmountViewFromAddress(address)
          .flatMap((_) => _)
          .subscribe(
              mosaic => console.log(logPrefix, mosaic.relativeAmount(), mosaic.fullName()),
              err => console.error(err)
          );
  };
  const timestamp = new Date().getTime();
  mosaicsAmountViewFromAddress('[' + timestamp + '] Alice(Public) have', mosaicServicePublic, aliceAccountPublic.address);
  mosaicsAmountViewFromAddress('[' + timestamp + '] Alice(Private) have', mosaicServicePrivate, aliceAccountPrivate.address);
  mosaicsAmountViewFromAddress('[' + timestamp + '] Bob(Public) have', mosaicServicePublic, bobAccountPublic.address);
  mosaicsAmountViewFromAddress('[' + timestamp + '] Bob(Private) have', mosaicServicePrivate, bobAccountPrivate.address);
};

alisBobMosaicsAmountView();

もしくは、こんなシェルスクリプトでもよいと思います。

secretlock_disp.sh
#!/bin/bash
nem2-cli account info --profile alicepub
nem2-cli account info --profile bobpub
nem2-cli account info --profile alicepriv
nem2-cli account info --profile bobpriv

secretlock1.js

Publicチェーンで、AliceがBobへXEMを送信するトランザクションです。これをTX1とします。

Aliceは、秘密の値proofを作成し、そのハッシュ値secretをトランザクションに埋め込みます。

Bobは、このXEMを受け取るには、秘密の値proofを埋め込んだSecretProofトランザクションを作成しなければなりません。しかし、まだBobはproofの値を知りません。

secretlock1.js
const alicePrivateKeyPrivate = '8CBA73B9DF31D85DCFA4B9E1D3B7A88E5A5F32C930FCC45FE5A457619F3E6687';
const aliceAccountPrivate = Account.createFromPrivateKey(alicePrivateKeyPrivate, NetworkType.MIJIN_TEST);
const alicePrivateKeyPublic = '7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4';
const aliceAccountPublic = Account.createFromPrivateKey(alicePrivateKeyPublic, NetworkType.MIJIN_TEST);

// const bobPrivateKeyPrivate = 'BA46F91D7BCD40B6482E138F0D3D53A29F0A6097B5C4586C6ABD81A3BA3A2DAD';
// const bobAccountPrivate = Account.createFromPrivateKey(bobPrivateKeyPrivate, NetworkType.MIJIN_TEST);
// const bobPrivateKeyPublic = '31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E';
// const bobAccountPublic = Account.createFromPrivateKey(bobPrivateKeyPublic, NetworkType.MIJIN_TEST);


// ***************************************************
//             Alice Create Secret
// ***************************************************

// Alice picks a random number and hashes it.
const random = crypto.randomBytes(10);
const hash = sha3_512.create();
const secret = hash.update(random).hex().toUpperCase();
const proof = random.toString('hex');

console.log('x    (proof)     : ' + proof);
console.log('H(x) (secret)    : ' + secret);


// ***************************************************
//             Alice to Bob   (Public)
// ***************************************************

// Alice creates creates TX1 SecretLockTransaction{ H(x), B, MosaicId, Amount, valid for 96h }
const tx1 = SecretLockTransaction.create(
    Deadline.create(),
    new Mosaic(new MosaicId('nem:xem'), UInt64.fromUint(100000000)),
    UInt64.fromUint(60), //officially 96h
    HashType.SHA3_512,
    secret,
    bobAddressPublic, // send to bob public
    NetworkType.MIJIN_TEST
);

// Alice sends TX1 to network (PUBLIC)
const tx1Signed = aliceAccountPublic.sign(tx1);

console.log('tx1Signed.hash   : ' + tx1Signed.hash);
console.log('tx1Signed.signer : ' + tx1Signed.signer);

transactionHttpPublic.announce(tx1Signed).subscribe(
    x => console.log(x),
    err => console.error(err)
);

secretlock2.js

Privateチェーンで、BobがAliceへモザイクを送信するトランザクションです。これをTX2とします。

Bobは、PublicチェーンでTX1を見て、secretを入手できますので、これをトランザクションに埋め込みます。

Aliceは、このモザイクを受け取るには、秘密の値proofを埋め込んだSecretProofトランザクションを作成しなければなりません。

今回、secretの値は、base64エンコードの文字列を引数で渡す形にしました。

secretlock2.js
// const alicePrivateKeyPrivate = '8CBA73B9DF31D85DCFA4B9E1D3B7A88E5A5F32C930FCC45FE5A457619F3E6687';
// const aliceAccountPrivate = Account.createFromPrivateKey(alicePrivateKeyPrivate, NetworkType.MIJIN_TEST);
// const alicePrivateKeyPublic = '7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4';
// const aliceAccountPublic = Account.createFromPrivateKey(alicePrivateKeyPublic, NetworkType.MIJIN_TEST);

const bobPrivateKeyPrivate = 'BA46F91D7BCD40B6482E138F0D3D53A29F0A6097B5C4586C6ABD81A3BA3A2DAD';
const bobAccountPrivate = Account.createFromPrivateKey(bobPrivateKeyPrivate, NetworkType.MIJIN_TEST);
const bobPrivateKeyPublic = '31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E';
const bobAccountPublic = Account.createFromPrivateKey(bobPrivateKeyPublic, NetworkType.MIJIN_TEST);


// ***************************************************
//             Bob to Alice   (Private)
// ***************************************************
if (process.argv.length <= 2) {
    throw new Error("need secret (base64 string)");
}

const secret = Buffer.from(process.argv[2], "base64").toString('hex').toUpperCase();

// B creates TX2 SecretLockTransaction{ H(x), A, MosaicId, Amount, valid for 84h }
const tx2 = SecretLockTransaction.create(
    Deadline.create(),
    new Mosaic(new MosaicId('foo:bar'), UInt64.fromUint(1)),
    UInt64.fromUint(10), //officially 84h
    HashType.SHA3_512,
    secret,
    aliceAddressPrivate,
    NetworkType.MIJIN_TEST
);

// Bob sends TX2 to network (PRIVATE)
const tx2Signed = bobAccountPrivate.sign(tx2);

console.log('tx2Signed.hash   : ' + tx2Signed.hash);
console.log('tx2Signed.signer : ' + tx2Signed.signer);

// const transactionHttpPrivate = new TransactionHttp('http://192.168.11.77:3100');

transactionHttpPrivate.announce(tx2Signed).subscribe(
    x => console.log(x),
    err => console.error(err)
);    

secretlock3.js

Privateチェーンで、Aliceがモザイクを引き出すトランザクションです。これをTX3とします。

Aliceは、BobのTX2をチェーン上で確認します。TX2のモザイクを受け取るには、秘密の値proofを埋め込んだSecretProofトランザクションを作成しなければなりません。

Aliceはproofを知っています(自分で作った)ので、これを使ってSecretProofトランザクションを作成します。

今回、proofの値は、文字列を引数で渡すようにしました。

secretlock3.js
const alicePrivateKeyPrivate = '8CBA73B9DF31D85DCFA4B9E1D3B7A88E5A5F32C930FCC45FE5A457619F3E6687';
const aliceAccountPrivate = Account.createFromPrivateKey(alicePrivateKeyPrivate, NetworkType.MIJIN_TEST);
const alicePrivateKeyPublic = '7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4';
const aliceAccountPublic = Account.createFromPrivateKey(alicePrivateKeyPublic, NetworkType.MIJIN_TEST);

// const bobPrivateKeyPrivate = 'BA46F91D7BCD40B6482E138F0D3D53A29F0A6097B5C4586C6ABD81A3BA3A2DAD';
// const bobAccountPrivate = Account.createFromPrivateKey(bobPrivateKeyPrivate, NetworkType.MIJIN_TEST);
// const bobPrivateKeyPublic = '31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E';
// const bobAccountPublic = Account.createFromPrivateKey(bobPrivateKeyPublic, NetworkType.MIJIN_TEST);


// ***************************************************
//             Alice Unlock   (Private)
// ***************************************************
if (process.argv.length <= 2) {
    throw new Error("need proof (hex string)");
}

const proof = process.argv[2];
const hash = sha3_512.create();
const secret = hash.update(Buffer.from(proof, 'hex')).hex().toUpperCase();

// Alice waits until Txs are confirmed
// Alice spends TX2 transaction by sending SecretProofTransaction (in PRIVATE network)
// const alicePrivateKeyPrivate = '8CBA73B9DF31D85DCFA4B9E1D3B7A88E5A5F32C930FCC45FE5A457619F3E6687';
// const aliceAccountPrivate = Account.createFromPrivateKey(alicePrivateKeyPrivate, NetworkType.MIJIN_TEST);
const tx3 = SecretProofTransaction.create(
    Deadline.create(),
    HashType.SHA3_512,
    secret,
    proof,
    NetworkType.MIJIN_TEST
);

const tx3Signed = aliceAccountPrivate.sign(tx3);

console.log('tx3Signed.hash   : ' + tx3Signed.hash);
console.log('tx3Signed.signer : ' + tx3Signed.signer);

transactionHttpPrivate.announce(tx3Signed).subscribe(
    x => console.log(x),
    err => console.error(err)
);

secretlock4.js

Publicチェーンで、BobがXEMを引き出すトランザクションです。これをTX4とします。

Bobは、AliceのTX3をチェーン上で確認します。TX3のXEMを受け取るには、秘密の値proofを埋め込んだSecretProofトランザクションを作成しなければなりません。

BobはTX3に埋め込まれたproofを見ることができます。これを使ってSecretProofトランザクションを作成します。

今回、proofの値は、base64エンコードされた文字列を引数で渡すようにしました。

secretlock4.js
// const alicePrivateKeyPrivate = '8CBA73B9DF31D85DCFA4B9E1D3B7A88E5A5F32C930FCC45FE5A457619F3E6687';
// const aliceAccountPrivate = Account.createFromPrivateKey(alicePrivateKeyPrivate, NetworkType.MIJIN_TEST);
// const alicePrivateKeyPublic = '7808B5B53ECF24E40BE17B8EC3D0EB5F7C3F3D938E0D95A415F855AD4C27B2A4';
// const aliceAccountPublic = Account.createFromPrivateKey(alicePrivateKeyPublic, NetworkType.MIJIN_TEST);

const bobPrivateKeyPrivate = 'BA46F91D7BCD40B6482E138F0D3D53A29F0A6097B5C4586C6ABD81A3BA3A2DAD';
const bobAccountPrivate = Account.createFromPrivateKey(bobPrivateKeyPrivate, NetworkType.MIJIN_TEST);
const bobPrivateKeyPublic = '31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E';
const bobAccountPublic = Account.createFromPrivateKey(bobPrivateKeyPublic, NetworkType.MIJIN_TEST);


// ***************************************************
//             Bob Unlock   (Public)
// ***************************************************
if (process.argv.length <= 2) {
    throw new Error("need proof (base64 string)");
}

const proof = Buffer.from(process.argv[2], "base64").toString('hex').toUpperCase();
const hash = sha3_512.create();
const secret = hash.update(Buffer.from(proof, 'hex')).hex().toUpperCase();

// Bob spends TX1 transaction by sending SecretProofTransaction (in PUBLIC network)
// const bobPrivateKeyPublic = '31B96EEB0C7FD6F8FB6B4ED09A9EB142A42B194AFBEB9EB52F0B79889F22326E';
// const bobAccountPublic = Account.createFromPrivateKey(bobPrivateKeyPublic, NetworkType.MIJIN_TEST);
const tx4 = SecretProofTransaction.create(
    Deadline.create(),
    HashType.SHA3_512,
    secret,
    proof,
    NetworkType.MIJIN_TEST
);

const tx4Signed = bobAccountPublic.sign(tx4);

console.log('tx4Signed.hash   : ' + tx4Signed.hash);
console.log('tx4Signed.signer : ' + tx4Signed.signer);


transactionHttpPublic.announce(tx4Signed).subscribe(
    x => console.log(x),
    err => console.error(err)
);

実行結果

初期の状態

Public Private
Alice 409089785 nem:xem 818181668 nem:xem
0 foo:bar
Bob 409090163 nem:xem 100 foo:bar
# node secretlock_disp.js
[1528285555168] Alice(Private) have 818181668 nem:xem
[1528285555168] Bob(Private) have 100 foo:bar
[1528285555168] Alice(Public) have 409089785 nem:xem
[1528285555168] Bob(Public) have 409090163 nem:xem

TX1

Publicチェーンにて、AliceからBobへ10XEM送りました。

# node secretlock1.js
x    (proof)     : 095b4fcd1f88f1785e59
H(x) (secret)    : B271D49970445F078CAD6979B75642B55C14DFAADF8067FE450332C63F60DE622B9DC1E6C02C96E690D4BC2E50BA8E8A0F3C065D98668D545C20E1A97B141B9D
tx1Signed.hash   : 7006B34B0C2397004CA37E1723EDA985093613549382EB96D9CB620B1519C4E3
tx1Signed.signer : 5D9513282B65A12A1B68DCB67DB64245721F7AE7822BE441FE813173803C512C
TransactionAnnounceResponse {
  message: 'packet 9 was pushed to the network via /transaction' }

Bob気づく

Bobは、TX1の存在に気づくことができるようになります。

ここでは、Bobの公開鍵を指定して、トランザクションを検索しています。secretが何かを把握することができます。

# curl http://localhost:3000/account/3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57/transactions | jq .[0]
{
  "meta": {
    "height": [
      61098,
      0
    ],
    "hash": "7006B34B0C2397004CA37E1723EDA985093613549382EB96D9CB620B1519C4E3",
    "merkleComponentHash": "7006B34B0C2397004CA37E1723EDA985093613549382EB96D9CB620B1519C4E3",
    "index": 0,
    "id": "5B17CC0A80FDB30001360936"
  },
  "transaction": {
    "signature": "2D27C913CEFB82671A69B39421B78DE4EB66431216005D4789ED6A4E9F6E22265F87E3DC2A4D99EE7456B78835938B7E599C2EC641A922D5B8BD7861E9A2370F",
    "signer": "5D9513282B65A12A1B68DCB67DB64245721F7AE7822BE441FE813173803C512C",
    "version": 36867,
    "type": 16972,
    "fee": [
      0,
      0
    ],
    "deadline": [
      104496673,
      16
    ],
    "duration": "60",
    "mosaicId": "-3087871471161192663",
    "amount": "100000000",
    "hashAlgorithm": 0,
    "secret": "snHUmXBEXweMrWl5t1ZCtVwU36rfgGf+RQMyxj9g3mIrncHmwCyW5pDUvC5Quo6KDzwGXZhmjVRcIOGpexQbnQ==",
    "recipient": "kHWOtHwo1hQ7qj3mqNnDGbUDob/Y54np4g=="
  }
}

TX2

Privateチェーンで、BobからAliceへfoo:barを送ります。その時に、先ほどゲットしたsecretを使います。

# node secretlock2.js snHUmXBEXweMrWl5t1ZCtVwU36rfgGf+RQMyxj9g3mIrncHmwCyW5pDUvC5Quo6KDzwGXZhmjVRcIOGpexQbnQ==
tx2Signed.hash   : 3ED705A3DD796C12A8F69CB29315576943E97C4996EAB430D3077CC546EB09DB
tx2Signed.signer : 1C650F49DD67EC50BFDEA40906D32CDE3C969BDF58837C7DA320829BDDE96150
TransactionAnnounceResponse {
  message: 'packet 9 was pushed to the network via /transaction' }

Alice気づく

Aliceの公開鍵を指定して、トランザクションを検索。secretが一致していること、signerがBobであること、もしくはトランザクションハッシュ値をBobから教えてもらう、など。

# curl http://localhost:3100/account/C36F5BDDE8B2B586D17A4E6F4B999DD36EBD114023C1231E38ABCB1976B938C0/transactions | jq .[0]
{
  "meta": {
    "height": [
      50499,
      0
    ],
    "hash": "3ED705A3DD796C12A8F69CB29315576943E97C4996EAB430D3077CC546EB09DB",
    "merkleComponentHash": "3ED705A3DD796C12A8F69CB29315576943E97C4996EAB430D3077CC546EB09DB",
    "index": 0,
    "id": "5B17CC4EFABE9E0001404C3F"
  },
  "transaction": {
    "signature": "CE674E5378D3237311915613CB41C087F3A1A67210BFEA0BD607580B617A22226C4D0F13E7F6126AB9C4143982418658CED082BC909EB5518918CB2200215B05",
    "signer": "1C650F49DD67EC50BFDEA40906D32CDE3C969BDF58837C7DA320829BDDE96150",
    "version": 36867,
    "type": 16972,
    "fee": [
      0,
      0
    ],
    "deadline": [
      104554127,
      16
    ],
    "duration": "10",
    "mosaicId": "-1412091718288690897",
    "amount": "1",
    "hashAlgorithm": 0,
    "secret": "snHUmXBEXweMrWl5t1ZCtVwU36rfgGf+RQMyxj9g3mIrncHmwCyW5pDUvC5Quo6KDzwGXZhmjVRcIOGpexQbnQ==",
    "recipient": "kFsRzZnbC8iVsAayDezf4Q00NEq+iHxg+A=="
  }
}

状況

お互いにSecret Lockトランザクションを送りあった状況です。

それぞれ、送った量のモザイクが減っているだけです。

Public Private
Alice 409089785 → 409089685 nem:xem 818181668 nem:xem
0 foo:bar
Bob 409090163 nem:xem 100 → 99 foo:bar
# node secretlock_disp.js
[1528285679620] Bob(Private) have 99 foo:bar
[1528285679620] Alice(Private) have 818181668 nem:xem
[1528285679620] Alice(Public) have 409089685 nem:xem
[1528285679620] Bob(Public) have 409090163 nem:xem

TX3

Privateチェーンで、AliceはSecret Proofトランザクションを作ります。同時に、チェーン上でproofを公開することになります。

# node secretlock3.js 095b4fcd1f88f1785e59
tx3Signed.hash   : 924D2FEAB00ECFB4A0C726C82AEA05FEE92125EBD54729E1C1E1E1FA66C5BEA8
tx3Signed.signer : C36F5BDDE8B2B586D17A4E6F4B999DD36EBD114023C1231E38ABCB1976B938C0
TransactionAnnounceResponse {
  message: 'packet 9 was pushed to the network via /transaction' }

Bob気づく

Bobは、Privateチェーン上に送信されたTX3を見て、proofが何かを知ることができます。

Bobの公開鍵を指定して、トランザクションを検索。signerがAliceで、secretが一致しているかどうか。もしくはトランザクションハッシュ値をAliceから教えてもらうか。もしくは、Aliceから直接proofを教えてもらってもよいと思います。

# curl http://localhost:3100/account/C36F5BDDE8B2B586D17A4E6F4B999DD36EBD114023C1231E38ABCB1976B938C0/transactions | jq .[0]
{
  "meta": {
    "height": [
      50504,
      0
    ],
    "hash": "924D2FEAB00ECFB4A0C726C82AEA05FEE92125EBD54729E1C1E1E1FA66C5BEA8",
    "merkleComponentHash": "924D2FEAB00ECFB4A0C726C82AEA05FEE92125EBD54729E1C1E1E1FA66C5BEA8",
    "index": 0,
    "id": "5B17CCA3FABE9E0001404C46"
  },
  "transaction": {
    "signature": "89B1840CFF796447698D7703ED0D25894817E05E97579C63DA9B143B4AF11F6CA820F1D5D3AB402E2EF33C7D74849216959AC257656809176ECC579D35901507",
    "signer": "C36F5BDDE8B2B586D17A4E6F4B999DD36EBD114023C1231E38ABCB1976B938C0",
    "version": 36867,
    "type": 17228,
    "fee": [
      0,
      0
    ],
    "deadline": [
      104639747,
      16
    ],
    "hashAlgorithm": 0,
    "secret": "snHUmXBEXweMrWl5t1ZCtVwU36rfgGf+RQMyxj9g3mIrncHmwCyW5pDUvC5Quo6KDzwGXZhmjVRcIOGpexQbnQ==",
    "proof": "CVtPzR+I8XheWQ=="
  }
}

TX4

Publicチェーン上で、BobがSecret Proofトランザクションを作ります。

# node secretlock4.js CVtPzR+I8XheWQ==
tx4Signed.hash   : C2CA04BA34FDEE75302788FA409A2584D3E1EEBDF42DF0373B235FA29EE0CA70
tx4Signed.signer : 3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57
TransactionAnnounceResponse {
  message: 'packet 9 was pushed to the network via /transaction' }

Alice気づく

Aliceは、Bobがちゃんと引き出したかどうかを確認できます。しなくてもよいです。

# curl http://localhost:3000/account/3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57/transactions | jq .[0]
{
  "meta": {
    "height": [
      61110,
      0
    ],
    "hash": "C2CA04BA34FDEE75302788FA409A2584D3E1EEBDF42DF0373B235FA29EE0CA70",
    "merkleComponentHash": "C2CA04BA34FDEE75302788FA409A2584D3E1EEBDF42DF0373B235FA29EE0CA70",
    "index": 0,
    "id": "5B17CCD280FDB30001360944"
  },
  "transaction": {
    "signature": "24D508D89AC0BA07CEBE95657E6428101A1818000CC8C428578FEA2CB6249757F9F585D0474AE2C264F69D2434DD2D1AADA96236750A080616CD99EC983D250A",
    "signer": "3390BF02D2BB59C8722297FF998CE89183D0906E469873284C091A5CDC22FD57",
    "version": 36867,
    "type": 17228,
    "fee": [
      0,
      0
    ],
    "deadline": [
      104692571,
      16
    ],
    "hashAlgorithm": 0,
    "secret": "snHUmXBEXweMrWl5t1ZCtVwU36rfgGf+RQMyxj9g3mIrncHmwCyW5pDUvC5Quo6KDzwGXZhmjVRcIOGpexQbnQ==",
    "proof": "CVtPzR+I8XheWQ=="
  }
}

最終状態

いい感じになりました。

Public Private
Alice 409089785 → 409089685 nem:xem 818181668 nem:xem
0 → 1 foo:bar
Bob 409090163 → 409090263 nem:xem 100 → 99 foo:bar
# node secretlock_disp.js
[1528285803662] Bob(Private) have 99 foo:bar
[1528285803662] Alice(Private) have 1 foo:bar
[1528285803662] Alice(Private) have 818181668 nem:xem
[1528285803662] Bob(Public) have 409090263 nem:xem
[1528285803662] Alice(Public) have 409089685 nem:xem

Secret Lockトランザクションの有効期限

これまでまったく触れてきませんでしたが、Secret Lockトランザクションには、Deadlineとは別にDurationという期間を設定します。単位はブロック数だったはず。

これを過ぎると、そのSecret Lockトランザクションは無効になり、持ち主のもとへ返されます。

なので、有効期限は、TX1 > TX2 でなければなりません。TX3によってproofが開示された時点で、TX1が期限切れだった場合、TX4が送信できないからです。

関連

nem catapult secret lock/proof tx 有効期限と複数回送信

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2