スマートコントラクトとは、契約や契約の条件に従って、法的に関連するイベントやアクションを自動的に実行、制御、文書化することを目的としたコンピュータプログラムや取引プロトコルのことです。スマートコントラクトは、信頼された仲介者の必要性の削減、仲裁と執行のコスト、詐欺による損失、悪意のある偶発的例外の削減を目的としています。
QurasスマートコントラクトはC#で書かれています。開発者はスマートコントラクトを開発する前に、次の手順が必要です:
- Visual StudioがインストールされたWindowsコンピュータ;
- Visual Studio に QurasContractPlugIn をインストールする:
- Quras コンパイラを公開する:
quras-smartcontract-compilerを Githubからダウンロード し、Visual Studio でソリューションを開き、quras_msil_compiler を公開します。
設定したら「Publish」を押して公開します。
ターゲットの場所に"quras-msil-compile.exe"があります。
- Quras msilコンパイラを設定
コントロールパネル->システム->システムの詳細設定で、環境変数をクリックします。システム変数のセクションで、PATH環境変数を見つけて選択します。編集をクリックします。PATH環境変数が存在しない場合は、新規作成をクリックします。システム変数の編集(または新規システム変数)ウィンドウで、環境変数PATHにpublishのパスを指定します。OKをクリックします。
パスを設定した後、cmd を再起動し "quras-msil-compile" と入力します。表示されれば設定が完了しています。コンパイラを設定した後、Visual Studioを再起動する必要があります。
Qurasコントラクトを作成します:
1.「ファイル」→「新規作成」→「プロジェクト」 をクリックします。
2.リストから Qurasスマートコントラクト を選択し、「次へ」 をクリックします。
3.プロジェクト名を入力し、「作成」をクリックします。
4.Quras.SmartContract.Framework をプロジェクトにインストールします。
「NuGet」でこのフレームワークをインストールします。
Qurasのコントラクトをコンパイルします:
Build -> Build Solutions をクリックして .qsb ファイルをコンパイルします。
コンパイルが完了すると、プロジェクトの bin/Debug ディレクトリに Hello.qsb Qurasスマートコントラクトが生成されます。Hello.abi.jsonは、このコントラクトのスクリプタッシュ、エントリ、パラメータ、戻り値の説明を含む記述ファイルです。 そして、Hello.qsbファイルをwindowsウォレットやJSLibでブロックチェーンにデプロイします。
ここでは、基本的なHello worldスマートコントラクトのサンプルを示し、ストレージに「Hello」と「World」を書き込みます:
すべてのスマートコントラクトは、Qurasフレームワークの SmartContract クラスを継承しています。Qurasネームスペースは、ブロックチェーンと対話し、永続的なデータストレージを操作するためのAPIです。これらのAPIは2つの部分に分かれています:
- ブロックチェーンAPIコントラクトは、ブロックやトランザクションのように、ブロックチェーンAPIを介してブロックチェーン内のすべてのデータにアクセスすることができます。
- 永続的なストレージAPIQurasチェーンに展開されているすべてのコントラクトは、そのコントラクト自体にしかアクセスできないストレージスペースを持っています。これらのAPIは、コントラクトのデータにアクセスすることができます。
コントラクトプロパティ
コントラクトのクラスでは、静的なreadonlyやconstは変更できない定数のコントラクトプロパティです。例えば、コントラクトの所有者や、後に資産移転で使用するfactor numberを定義したい場合は、以下のように定数を定義することができます:
// Represents owner of this contract,Usually should be the contract creator public static readonly byte[] Owner = "Dqf3UKe1f5FBWduGxHp8RMqP29dL6DgGS1".ToScriptHash(); // A constant number private const ulong factor = 10000;
定義されたこれらのプロパティは、スマートコントラクトのメソッドで使用できる定数であり、スマートコントラクトがどのインスタンスで実行されていても、同じ値を保持します。
さらに、開発者はコントラクト内に静的メソッドを定義して定数値を返すことができ、エンドユーザーがコントラクトに問い合わせをしようとしたときにこのメソッドを呼び出して定数値を取得することができます。例えば、トークンを作成する際には、このメソッドで誰もが確認できる名前を定義する必要があります。
public static string Name()=> "name of the token";
ストレージプロパティ
スマートコントラクトを開発する際に、アプリケーションのデータをブロックチェーン上に保存する必要がある場合があります。コントラクトが作成されたとき、またはトランザクションがそれを呼び出すとき、コントラクトのコードはそのストレージを読み取ったり書き込んだりすることができます。スマートコントラクトのストレージに保存されているすべてのデータは、スマートコントラクトの呼び出しの間に自動的に永続化されます。Qurasのフルノードは、チェーン上のすべてのスマートコントラクトの状態を保存します。
Qurasは、キー値に基づいたデータアクセスインターフェイスを提供しています。データレコードは、キーを使用してスマートコントラクトから読み取ったり、削除したり、スマートコントラクトに書き込んだりすることができます。また、スマートコントラクトは、ストレージコンテキストを他のコントラクトに取得や送信し、他のコントラクトがそのストレージ領域を管理することを信任します。C#開発では、スマートコントラクトは、読み取り/永続的なストレージを書き込むためにストレージクラスを使用することができます。ストレージクラスは、静的なクラスであり、コンストラクタを必要としません。例えば、トークンの総供給量をストレージに保存したい場合:
//Key is totalSupply and value is 100000000 Storage.Put(Storage.CurrentContext, "totalSupply", 100000000);
CurrentContext は、現在のストアコンテキストを返します。ストアコンテキストを取得した後、オブジェクトは他のコントラクトへの引数として渡すことができ、他のコントラクトが現在のコントラクトの永続ストアに対して読み書き操作を実行できるようにします。
ストレージはプリミティブな値を格納するのに適しており、構造化されたデータを格納するためのStorageMapを使用することもできます。これによりスマートコントラクトストレージの中の各キーを含む全コンテナを格納できます。
//Get the totalSupply in the storageMap. The Map is used an entire container with key name "contract" StorageMap contract = Storage.CurrentContext.CreateMap(nameof(contract)); return contract.Get("totalSupply").AsBigInteger();
データ型
C#でスマートコントラクトを開発する場合、QVMと.netの違いのためC#の機能をフルに使用できません。
限られたC#の機能を*.qsbファイルに使うことしかできません。
QVMは以下の基本的なタイプを提供します:
- ByteArray
- 整数
- ブーリアン
- 配列
- 構造
- マップ
- インターフェース
qsbコードから直接生成できる基本的な型は以下の通りです:
- ByteArray (整数とブール値の両方を ByteArray で表現します)
- 配列
- 構造
- マップ
C#の基本的な種類:
- Int8 int16 int32 int64 uint8 uint16 uint32 uint64
- float double
- Boolean
- Char String
主な方法
主な機能は、スマートコントラクトのエントリーポイントです。メイン関数では、ユーザーは異なるエントリポイントに応じて他の関数を呼び出すことができます。
トリガー
トリガーとは、スマートコントラクトの実行を発動させる仕組みのことです。Qurasスマートコントラクトでは4つのトリガーが導入されており、最も使用されるのは「検証」と「アプリケーション」です。
検証トリガー
検証トリガーは、複数のパラメータを受け入れ、トランザクションやブロックの有効性を示すブール値を返すことができる検証関数としてコントラクトを呼び出すためのものです。 あるアドレスから別のアドレスにトークンを転送しようとすると、検証コントラクトが発動します。トランザクションを受信したすべてのノードが送信元アドレスのコントラクトを確認します。戻り値が真の場合、転送は正常に行われています。false を返すと、転送に失敗しています。 したがって、検証トリガーは、トランザクションがネットワークの残りの部分に中継されるかどうかを決定します。falseを返す場合は、トランザクションがブロックチェーンにパッケージ化されず、トランザクションが失敗したことを意味します。
public static bool Main(byte[] signature) { if (Runtime.Trigger == TriggerType.Verification) { if (/*condition A*/) return true; else return false; } }
アプリケーショントリガー
アプリケーショントリガーは、複数のパラメータを受け入れ、ブロックチェーンの状態を変更し、任意の型の値を返すことができる検証機能としてコントラクトを呼び出すためのものです。 トランスファーにより発動する検証トリガーとは異なり、アプリケーショントリガーは特別なイノボケーショントランザクションによって発動します。アプリケーションがスマートコントラクトを呼び出すと、呼び出しトランザクションが作成され、署名され、ブロックチェーンにブロードキャストされます。呼び出しトランザクションが確認された後、コンセンサスノードによってスマートコントラクトが実行されます。 イノボケーショントランザクションが確認された後にアプリケーションのコントラクトが実行されるため、実行が成功してもしなくてもブロックチェーンに記録されます。 通常、スマートコントラクトでは、検証トリガーとアプリケーショントリガーの両方が引っかかることがあり、開発者はトリガーを処理しなければなりません。
public static Object Main(string operation, params object[] args) { if (Runtime.Trigger == TriggerType.Verification) { if (/*Condition A*/) return true; else return false; } if (Runtime.Trigger == TriggerType.Application) { if (operation == "FunctionA") return FunctionA(args); } } // There is a smart contract entry point and redirected from main method public static bool FunctionA(params object[] args) { //some code }
CheckWitness
場合によっては、コントラクトのコードを呼び出すアドレスが本当にそのアドレスであるかを検証したい場合があります。 Runtime.CheckWitness メソッドは、コントラクトのコードの呼び出しに使用されるアドレスと照合して検証するアドレスを表す単一のパラメータを受け取ります。これは、呼び出したコントラクトのトランザクション/ブロックが必要なスクリプトのハッシュを検証したことを確認します。 通常はこのメソッドを使って、指定したアドレスがコントラクトの呼び出し元であるかどうかを確認し、そのアドレスでストレージの変更などを行うことができます。 ここでは、Runtime.CheckWitness関数を使用します。そして、まずドメインの所有者を取得して、そのドメインがすでにストレージに存在しているかどうかを確認してみます。そうでない場合は、Storage.Putメソッドを使用してドメイン->所有者のペアを保存することができます。
private static bool Register(string domain, byte[] owner){ if (!Runtime.CheckWitness(owner)) return false; byte[] value = Storage.Get(Storage.CurrentContext, domain); if (value != null) return false; Storage.Put(Storage.CurrentContext, domain, owner); return true; }
registerメソッドと同様に、Delete関数ではまず所有者を確認し、存在していてコントラクトを発動した人と同じであれば、Storage.Deleteメソッドを使ってペアを削除します。このメソッドについては、このパートの最後に質問として残しています。
イベント情報
スマートコントラクトでは、イベントは、アプリケーションとブロックチェーンの間で何かが起こったことを伝えるために使用され、特定のイベントをリスニングし、それが起こったときに行動を起こすことができます。これを使って、外部データベースの更新やアナリティクスを行ったり、UIを更新したりすることができます。例えば、トークンシステムでは、ユーザーがtransfer関数を呼び出したときにイベント転送を呼び出す必要があります。
//発信者がトークンを転送したときに呼び出されるべきです。 public static event transfer(byte[] from, byte[] to, BigInteger amount)
スマートコントラクトの導入
スマートコントラクトを書き終えたら、それをQurasブロックチェーンにデプロイして、他のユーザーが使用したり、他のコントラクトを呼び出したりすることができます。
まず.qsbファイルにコンパイルし、GUI(quras windows wallet)でデプロイするか、JSLibを使ってデプロイします:
var address = 'Do27ycn5urnJnWnNboiDh5i5PkAEFmvehd'; // The address that launch the smart contract. var privKey = '02bf9e9964a3c0421ad5a8dde06f848977c514fd5cc638434d567a05b87ade39'; // The private key. var script = '53c56b6c766b00527ac46c766b51527ac461616168124d6f64756c652e52756e74696d652e4c6f6761616819' + '4d6f64756c652e53746f726167652e476574436f6e746578740e48656c6c6f2046756e6374696f6e05576f726c6461527268124d' + '6f64756c652e53746f726167652e50757461516c766b52527ac46203006c766b52c3616c7566'; // Script that is compiled. var param = '07'; // Smart contract parameter types. var returns = 5; // Smart contract return type var needStorage = true; var scName = 'HelloWorld'; var version = '1.0.0.1'; var author = 'qurasuser'; var mail = 'hello@quras.io'; var description = 'My First SC using JS Library.'; Quras.api.qurasDB.deploySmartContract(Quras.CONST.QURAS_NETWORK.MAIN, address, privKey, script, param, returns, needStorage, scName, version, author, mail, description, 490) .then((data) => { console.log(data) }) .catch((error) => { console.log(error) })
スマートコントラクトの発動
1つのコントラクトから別のコントラクトを呼び出すには、C#でAppcallを使ってステートメントを追加し、呼び出すコントラクトのスクリプトハッシュをコードに追加する必要があります。
[Appcall("XXXXXXXXXX")]//ScriptHash public static extern int AnotherContract(string arg); public static void Main() { AnotherContract("Hello"); }