Open13

Voucher Hook

tequtequ

Hookアカウントのバリデーション

事前に定義されたHookアカウントID(hook_acc_id)と先ほど取得したuser_acc_idを比較し同じである場合、VoucherHookを設定したアカウント自身が送信したトランザクションのため以降のHooks処理を行わず、正常終了とする

    uint8_t hook_acc_id[20] = { 0x0AU, 0x2DU, 0x33U, 0xB2U, 0xB1U, 0x6AU, 0x05U, 0x8DU, 0x91U, 0x75U, 0x84U, 0x63U, 0x55U, 0xDDU, 0x73U, 0x1FU, 0xA2U, 0xC9U, 0x19U, 0x41U };
    int8_t equal = 0; BUFFER_EQUAL(equal, hook_acc_id, user_acc_id, 20);

    if(equal)
        accept(SBUF("Voucher Create: Outgoing Transaction."), __LINE__);

なお、ここでhook_acc_idの値(アカウントID)は固定値を使用するのではなく、hook_account()を利用して、処理中のHookの所有アカウントを取得するのが望ましい。
https://xrpl-hooks.readme.io/reference/hook_account

tequtequ

namespaceの定義等

ledger_seq()で処理中のトランザクションを含む現在のレジャー番号を取得

config_nslimit_nsは後々読み込む外部Stateのnamespace
keyはStateのkey値

config_nsの値はCONFIGのSHA256h, limit_nsの値はLIMITのSHA256h, keyの値はLIMITのHEX文字

    uint64_t current_ledger =  ledger_seq();
    uint8_t config_ns[32] = { 0xF4U, 0x34U, 0x37U, 0xFCU, 0xA1U, 0xD5U, 0xF3U, 0xD0U, 0x38U, 0x10U, 0x73U, 0xEDU, 0x3EU, 0xECU, 0x9AU, 0xE4U, 0x2BU, 0xF8U, 0x69U, 0x88U, 0x55U, 0x9EU, 0x98U, 0x00U, 0x97U, 0x95U, 0xA9U, 0x69U, 0x91U, 0x9CU, 0xBEU, 0xCAU };
    uint8_t limit_ns[32]  = { 0xB0U, 0x44U, 0xC7U, 0xA1U, 0x64U, 0x7AU, 0xB9U, 0x06U, 0x5FU, 0x68U, 0x5DU, 0xAEU, 0x11U, 0xB8U, 0xE3U, 0xFCU, 0xDDU, 0x11U, 0x94U, 0xF8U, 0x8CU, 0x21U, 0x19U, 0x00U, 0x2BU, 0xD8U, 0x98U, 0xD2U, 0x91U, 0x3FU, 0x29U, 0xC0U };
    uint8_t key[32]    = { 0x4CU, 0x49U, 0x4DU, 0x49U, 0x54U }; // LIMIT

https://xrpl-hooks.readme.io/reference/ledger_seq

tequtequ

state用の構造体の定義

Voucher情報とConfig情報の構造体を定義

Voucher Stateに含まれる情報

  • 合計送信額
  • 合計受取額
  • 送信額
  • 受取額
  • Voucher作成日時
  • Voucher請求日時

Config Stateに含まれる情報

  • Voucherの最小額
  • Voucherの最大額
  • 1ヶ月の最大作成可能額
    typedef struct myState {
        int64_t  total_sent;
        int64_t  total_received;
        int64_t  sent;
        int64_t  received;        
        int32_t  ledger_create;
        int32_t  ledger_claim;
    } State;

    State S;

    typedef struct Voucher_Config {
        int64_t min;
        int64_t max;
        int64_t cap;
    } Config;

    Config c;
tequtequ

Config情報の取得

自身のアカウントのconfig_nsnamespace外部StateからConfig情報を取得

Config情報が存在しない場合は、エラーとしてHookを終了する。

    if(state_foreign(SVAR(c), SBUF(key), SBUF(config_ns), SBUF(hook_acc_id)) != 24){
      rollback(SBUF("Voucher: Configuration Not Set."), 2);
    }

Config情報はVoucherClaim Hookで設定される

https://xrpl-hooks.readme.io/reference/state_foreign

tequtequ

Amountフィールドのバリデーション

otxn_field()を使い、Amountフィールドの値を取得する。(AmountフィールドはNativeトークンの場合は8byte, IOUトークンの場合は48byte)
Nativeトークンのみサポートしているため、IOUトークンの場合はエラーとしてHookを終了する

Amountフィールドのデータをdropsに変換し、float_set()を使いXFLフォーマットに変換する

float_compare()を使ってConfigのmax, minと比較し、この2つの範囲内にない場合はエラーとしてHookを終了する。

    uint8_t amount[8];
    if(otxn_field(SBUF(amount), sfAmount) != 8)
        rollback(SBUF("Voucher Create: Non XAH Transaction not allowed."), __LINE__);

   uint64_t otxn_drops = AMOUNT_TO_DROPS(amount);
   int64_t amount_xfl = float_set(-6, otxn_drops);

   if(float_compare(amount_xfl, c.min, COMPARE_LESS) == 1)
       rollback(SBUF("Voucher Create: Less than allowed minimum."), 2);

   if(float_compare(amount_xfl, c.max, COMPARE_GREATER) == 1)
       rollback(SBUF("Voucher Create: Greater than allowed maximum."), 500);  

https://xrpl-hooks.readme.io/reference/float_set

https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl

https://xrpl-hooks.readme.io/reference/float_compare

tequtequ

VoucherのID情報を取得

otxn_param()を使ってパラメータ名VC(Voucher Create)をキーとするHookParameter情報を取得する。
VCに紐づく値は先頭に0xEDバイトが追加されたed25519形式の公開鍵情報(合計33バイト)。

取得したデータ全体が33バイトでない場合はエラーとしHookを終了。
(このエラーメッセージはVoucher Create: Please pass valid public key.の方が適切でしょう)

そして取得したデータのうち2バイト目以降の実際の32バイトの公開鍵情報部分をstate_key変数に格納する。

state_key変数に格納した公開鍵情報がVoucher情報のIDとなるため、state()を使って同一のVoucherがすでに存在していないかのチェックも行う。

    uint8_t pname[2] = { 0x56U, 0x43U };
    uint8_t state_key[32];
    if(otxn_param(state_key - 1, 33, pname, 2) != 33)
        rollback(SBUF("Voucher Create: Please pass valid account id."), __LINE__);

    uint8_t vault[8];
    if(state(SBUF(vault), SBUF(state_key)) == 8)
        rollback(SBUF("Voucher Create: Voucher already present."), __LINE__);

https://xrpl-hooks.readme.io/reference/otxn_param

https://xrpl-hooks.readme.io/reference/state

tequtequ

VoucherCreatorアカウント情報の更新

Creatorアカウント単位で保存しているStateのうち次の情報を更新する

  • total_sent
  • sent
  • ledger_create

total_sentは合計Voucher作成額
sentは500,000レジャー以内の合計Voucher作成額
ledger_createは500,000レジャーの基準となるレジャー番号。現在のledger_createから500,000レジャー以上経過していた場合はledger_createを更新。

500,000レジャー以内に月間上限額を超える量のVoucherを更新しようとした場合はエラーとなりHookの処理を終了

    if(state_foreign(SVAR(S), user_acc_id, 32, SBUF(limit_ns), SBUF(hook_acc_id)) == 40) {
        if(current_ledger <= S.ledger_create + 500000) {
            S.total_sent = float_sum(S.total_sent, amount_xfl);            
            int64_t total = float_sum(S.sent, amount_xfl);
            if (float_compare(total, c.cap, COMPARE_GREATER) == 1){
                rollback(SBUF("Try sending less XAH. You are exceeding the monthly limit."), 5000);
            }
            S.sent = total;
        } else {
            S.total_sent = float_sum(S.total_sent, amount_xfl);      
            S.sent = amount_xfl;
            S.ledger_create = current_ledger;
        }
    } else {
        S.sent = amount_xfl;
        S.total_sent = amount_xfl;
        S.ledger_create = current_ledger;
    }

https://xrpl-hooks.readme.io/reference/float_sum

tequtequ

Total Stateの更新

Totalの構造体を定義する。

state_foreign()からTotal State情報を取得し、createdに作成するVoucherの金額を加算する。

state_foreign_set()を使ってTotal state情報を保存する。

total_nsTOTALのSHA256h

    typedef struct voucherTotal {
        int64_t created;
        int64_t claimed;
    } VoucherTotal;
    VoucherTotal Total;
    uint8_t total_ns[32]    = { 0x5DU, 0x8FU, 0xF9U, 0xC2U, 0x82U, 0xE7U, 0xD9U, 0x68U, 0xD4U, 0x40U, 0xBEU, 0x77U, 0x02U, 0xA5U, 0x5AU, 0x42U, 0x49U, 0x1EU, 0x3AU, 0x7EU, 0x56U, 0xDFU, 0xA2U, 0x06U, 0xB2U, 0xE6U, 0xEAU, 0xD7U, 0x90U, 0xBEU, 0xB7U, 0x10U }; //TOTAL
    uint8_t tkey[32]        = { 0x54U, 0x4FU, 0x54U, 0x41U, 0x4CU }; // TOTAL

    if(state_foreign(SVAR(Total), SBUF(tkey), SBUF(total_ns), SBUF(hook_acc_id)) == 16){
          Total.created = float_sum(Total.created, amount_xfl);
          state_foreign_set(SVAR(Total), SBUF(tkey), SBUF(total_ns), SBUF(hook_acc_id));
    }

https://xrpl-hooks.readme.io/reference/state_foreign_set

tequtequ

VoucherCreatorアカウント情報とVoucher情報の保存

state_foreign_set()state_set()を利用し、VoucherCreatorアカウント情報とVoucher情報を保存する。

    if(state_foreign_set(SVAR(S), user_acc_id, 32, SBUF(limit_ns), SBUF(hook_acc_id)) != 40) {
        rollback(SBUF("Voucher Create: Failed at creating the user limit."), __LINE__);
    }

    if (state_set(SVAR(amount_xfl), SBUF(state_key)) != 8)
	   rollback(SBUF("Voucher Create: Failed to create Voucher."), __LINE__);

https://xrpl-hooks.readme.io/reference/state_set