Byteball開発者向け情報wiki - チャットボットからのスマートコントラクト作成と送付
ここではチャットボットからスマートコントラクトを作成し、送付する手順の解説
およびコード上でスマートコントラクトを実装する場合の手順を記載します。

セットアップ

開発環境構築

開発環境構築から環境の構築を行ってください

改造元コードの取得

今回はheadless-byteballのstart.jsのコードを改造します。

以下、作成中

実装

コーディング

まずは新しい.jsファイルを作成します。名前はなんでも構いません。
vim testcode.js
そのファイルにstart.jsの全てのコードをコピーした上で、
430行目あたり、function handleTextのswitch文に以下のコードを追加してください。
今回はチュートリアルなのでチャットボット側のアドレスや、動作確認に使うGUIウォレットのアドレス等は直接コピー&ペーストで書き込んで行きます。
  • ここで言う自分とはチャットボット、相手はGUIウォレットを指します。また、デバイスアドレスとウォレットアドレスを間違えないように注意してください。
  • ggの部分は任意のコマンドに置き換えて構いません

//***********
                case 'gg':
                        var arrDefinition = ['or', [
                        ['and', [
                                        ['address','相手のアドレス(ウォレット)'],//useraddress
                                        ['in data feed', [['I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT'], 'timestamp', '>', Date.now()]]
                                        //I2ADHGP4HL6J37NQAD73J7E5SKFIXJOTはタイムスタンプオラクルのアドレスです。
                                ]
                        ],
                        ['and', [
                                        ['address', '自分のアドレス(ウォレット)'],//myaddress
                                        ['in data feed', [['I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT'], 'timestamp', '>', Date.now()]]
                                ]
                        ]
                ]];
                var device = require('byteballcore/device.js');
                var assocSignersByPath = {
                'r.0.0': {
                        address:'相手のアドレス(ウォレット)',//user_address
                        member_signing_path: 'r', // unused, should be always 'r'
                        device_address: '相手のアドレス(デバイスアドレス)'//user_device_address
                },
                'r.1.0': {
                        address: '自分のアドレス(ウォレット)',//myaddress
                        member_signing_path: 'r', // unused, should be always 'r'
                        device_address: device.getMyDeviceAddress()
                }
                };
                var walletDefinedByAddresses = require('byteballcore/wallet_defined_by_addresses.js');
                walletDefinedByAddresses.createNewSharedAddress(arrDefinition, assocSignersByPath, {
                ifError: function(err){
                        // handle error
                        device.sendMessageToDevice(from_address, 'text', 'contract create error:'+err);
                },
                ifOk: function(shared_address){
                        // new sharer address created
                       //1000は相手に要求する支払額。任意の値に変更可能
                        var arrPayments = [{address: shared_address, amount: 1000, asset: 'base'}];
                                var assocDefinitions = {};
                                assocDefinitions[shared_address] = {
                                        definition: arrDefinition,
                                        signers: assocSignersByPath
                                };
                        var objPaymentRequest = {payments: arrPayments, definitions: assocDefinitions};
                        var paymentJson = JSON.stringify(objPaymentRequest);
                        var paymentJsonBase64 = Buffer(paymentJson).toString('base64');
                        var paymentRequestCode = 'payment:'+paymentJsonBase64;
                        var paymentRequestText = '[your share of payment to the contract]('+paymentRequestCode+')';
                        device.sendMessageToDevice(from_address, 'text', paymentRequestText);
                        }
                });
                break;
//***********

動作確認

node testcode.js
初回起動の場合は名前とパスワードを入力

デバイス上に出てくるペアリングコードを控える
下記のXXXの部分です。
====== my pairing code: XXX 

GUIウォレットからペアリングを行いチャット画面で「gg」
と打ち込むと、実装に成功していればこのような返信が送られてくる。


クリックするとこのような画面が出てきて、実際に送金すると契約成立となる。


開発の際はスマートコントラクト条件部分を作り変えることでスポーツの賭けから保険まで様々なサービスを提供することができる。
また、契約履行の判定を行う際に既存のオラクルで出来ない条件を付与する場合は、自分でオラクルの開発が必要になることがある。

解説

コントラクト(契約)のロック解除条件の設定

スマートコントラクトでは、どのような条件で誰からのアクセスロックが解除されるか決めることができます。
実装例
var arrDefinition = ['or', [
                        var arrDefinition = ['or', [
                        ['and', [
                                        ['address','相手のアドレス(ウォレット)'],
                                        ['in data feed', [['I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT'], 'timestamp', '>', Date.now()]]
                                        //I2ADHGP4HL6J37NQAD73J7E5SKFIXJOTはタイムスタンプオラクルのアドレスです。
                                ]
                        ],
                        ['and', [
                                        ['address', '自分のアドレス(ウォレット)'],
                                        ['in data feed', [['I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT'], 'timestamp', '>', Date.now()]]
                                ]
                        ]
                ]];
基本的な構造は'or',[条件],[条件]もしくは'and',[条件],[条件]のみで、それぞれOR条件とAND条件を示しています。
上記のコードもこれらを入れ子にして条件設定を行っています。
日本語にすると
「自分ウォレットのアドレスでかつ、tamestampのデータフィードが現在自刻よりも大きい値を示したとき」[要調査]
または、
「相手のウォレットのアドレスでかつ、tamestampのデータフィードが現在自刻よりも大きい値を示したとき」[要調査]
となります。
実際に開発する際にアドレス部分は直接手打ちではなく既存の関数を駆使して取得します。

スマートコントラクトにおける自分と相手の定義?[要調査]

               'r.0.0': {
                        address:'相手のアドレス(ウォレット)',//user_address
                        member_signing_path: 'r', // unused, should be always 'r'
                        device_address: '相手のアドレス(デバイスアドレス)'//user_device_address
                },
                'r.1.0': {
                        address: '自分のアドレス(ウォレット)',//myaddress
                        member_signing_path: 'r', // unused, should be always 'r'
                        device_address: device.getMyDeviceAddress()
                }
                };
詳細不明。[要調査]

[要調査]

コントラクトアドレスの生成?[要調査]

var walletDefinedByAddresses = require('byteballcore/wallet_defined_by_addresses.js');
                walletDefinedByAddresses.createNewSharedAddress(arrDefinition, assocSignersByPath, {
                ifError: function(err){
                        // handle error
                        device.sendMessageToDevice(from_address, 'text', 'contract create error:'+err);
                },
                ifOk: function(shared_address){

walletDefinedByAddresses.createNewSharedAddressでコントラクトのアドレスを生成します。
成功した場合は、 ifOk: function(shared_address)に
失敗した場合は、ifError: function(err)に入り、 'contract create error:'+errでエラーであることとエラー内容をそのまま返信します。

エンコードと送信

                       // new sharer address created
                       //1000は相手に要求する支払額。任意の値に変更可能
                        var arrPayments = [{address: shared_address, amount: 1000, asset: 'base'}];
                                var assocDefinitions = {};
                                assocDefinitions[shared_address] = {
                                        definition: arrDefinition,
                                        signers: assocSignersByPath
                                };
                        var objPaymentRequest = {payments: arrPayments, definitions: assocDefinitions};
                        var paymentJson = JSON.stringify(objPaymentRequest);
                        var paymentJsonBase64 = Buffer(paymentJson).toString('base64');
                        var paymentRequestCode = 'payment:'+paymentJsonBase64;
                        var paymentRequestText = '[your share of payment to the contract]('+paymentRequestCode+')';
                        device.sendMessageToDevice(from_address, 'text', paymentRequestText);
                        }
arrPaymentsの行の
amount:支払額。ここでは1000
asset:GBかGBBかを指定。'base'だとGB支払いに
objPaymentRequestからpaymentRequestTextの行まではBase64でエンコードしています。
最後に作成したテキストを、
device.sendMessageToDevice(from_address, 'text', paymentRequestText);
の行で返信しています。