説明1
http://hope.2ch.net/test/read.cgi/cryptocoin/1413242198/の説明です。
CREACOINではデータ一式(を表すオブジェクト)を保存したり、ノード間で通信したりするときは
基本的にバイト配列に変換してから保存や送信を行います。
具体的なデータからバイト配列への変換はSHAREDDATA基底クラスのToBinaryメソッドで行うので、データ一式を表すクラスを実装する際にはSHAREDDATA基底クラスを継承するようにし、幾つかのプロパティをオーバーライドします。
一番重要なのはStreamInfoプロパティです。このプロパティでオブジェクトのどのデータを保存するかを指定します。
MainDataInformationの配列を返すFuncを返すようにします。
MainDataInformationでデータの型の指定や実際のデータとバイト配列変換部分との結び付けを行います
(詳細な実装方法は後述)。
その他にIsVersioned、IsVersionSaved、IsCorruptionCheckedなどのプロパティがあります(その他に署名関連のプロパティもありますが、今は使っていないです。使っても良いんですけどね・・・)。
IsVersionedはバージョン機能を有効にするかどうかです。
オブジェクトが保持するデータがバージョンによって変わり得る場合にはtrueにします。
IsVersionSavedはバージョンをバイト配列の中に保持するかです。
バージョン機能を有効にしても必ず出力されるバイト配列の中にバージョンが格納されるとは限りません。
バージョンを格納したい場合には、IsVersionSavedをtrueにします。
IsCorruptionCheckedは破損検査を有効にするかです。
破損検査を有効にすると、バイト配列の中にデータをバイト配列にしたもののSha256ハッシュ値の
先頭4ビットが格納されるようになります。使い過ぎると重そうです。
MainDataInformationの基本的な使い方は↓のような感じです。
基本型の場合は第1引数が型で、第2引数がバイト配列変換部分にデータを渡すFuncで第3引数がバイト配列(逆)変換部分からデータをもらうActionです。
(ただ、全ての基本型に対応させていなかったと思います。対応していないと例外を投げます)
new MainDataInfomation(typeof(string), () => this.Name, (o) => this.Name = (string)o),
基本型の配列の場合、配列の長さを渡さなければなりません。可変長の場合はnullを渡します。
new MainDataInfomation(typeof(byte), 32, () => hash, (o) => hash = (byte)o)
SHAREDDATAから派生した型のデータにも対応していますが、その場合は、バージョンを指定しなければなりません。
バージョンがない場合はnullを渡します。
new MainDataInfomation(typeof(TransactionOutput), 0, () => txOutput, (o) => txOutput = (TransactionOutput)o),
SHAREDDATAから派生した型の配列の場合、要素数とバージョンを指定しなければなりません。
new MainDataInfomation(typeof(TransactionOutput), 0, null, () => txOutputs, (o) => txOutputs = (TransactionOutput)o),
MainDataInformationによるデータ指定は結構柔軟だと思います。
MainDataInformationのコンストラクタに渡すFuncやActionは基本的に内部でどのような処理でもできるので、例えば、次のようなこともできます。
new MainDataInfomation(typeof(AnonymousAccountHolder), 0, () => anonymousAccountHolder, (o) => { anonymousAccountHolder = (AnonymousAccountHolder)o; if (anonymousAccountHolder.Version != 0) throw new NotSupportedException(); }), また、StreamInfoプロパティはIEnumerable<MainDataInfomation>を返すFuncなので、次のようなこともできます。
private IEnumerable<MainDataInfomation> StreamInfoInner { get { bool isInBound = nodeInfo != null; yield return new MainDataInfomation(typeof(bool), () => isInBound, (o) => isInBound = (bool)o); if (isInBound) yield return new MainDataInfomation(typeof(NodeInformation), 0, () => nodeInfo, (o) => nodeInfo = (NodeInformation)o); yield return new MainDataInfomation(typeof(int), () => creaVersion, (o) => creaVersion = (int)o); yield return new MainDataInfomation(typeof(int), () => protocolVersion, (o) => protocolVersion = (int)o); yield return new MainDataInfomation(typeof(string), () => client, (o) => client = (string)o); yield return new MainDataInfomation(typeof(bool), () => isTemporary, (o) => isTemporary = (bool)o); } }
何故このような枠組みを作ったのかと思われるかもしれませんが、当初は通信プロトコルの実装も同様な形式でできないかと考えていました。
結局の所、通信プロトコルもどのようなデータを送受信するか、という点が重要なためなるべく宣言的な書き方ができたら分かりやすいだろうと思っていた訳です。
しかし、現時点ではまだ通信プロトコルの宣言的記述は実現できていません。