- aLice -

- aLice -はSymbolアカウントで署名ができるMobileAppです。
ユーザーはSymbolアカウントの秘密鍵をユーザが設定するパスワードにて暗号化し端末に保存しています。

他のMobileAppやwebサイトから署名が必要なデータを送ることでユーザが署名し署名データを返すことができます。

- aLice - が出来ることは以下です

  1. 公開鍵を渡す
  2. 署名する

カスタムURLスキーム

- aLice - のカスタムURLスキームは alice:// です。prefixにはsignを使います。

クエリパラメータ

クエリパラメータ
type request_sign_transaction, request_sign_utf8, request_sign_binary_hex, request_pubkey, request_sign_batches
data 署名するデータ(string)
callback これらをhexにする(https://hogehoge.com/callback, sampleApp://callback/)
batch batch0, batch1, batch2 …
method post
redirect_url これらをhexにする (https://google.com)

ex)
alice://sign?data={data}&type={request_type}&callback={callbackURL}

POST通信

&method=postを与えてやるとPOSTにて送信します。
その際は、aLiceの画面に留まるため
&redirect_url={redirect_url}を追加してやると画面が遷移します。redirect_urlにパラメータを追加するとそのまま遷移するのでtokenなどの受け渡しに使えるかと思います。
※redirect_urlもutf8をhexにして渡してください

postの場合は以下のようなJSONをBodyに含め送信します

{
    "pubkey": "PUBLIC_KEY",
    "original_data": "ORIGINAL_DATA",
    "signature": "SIGNATURE"
}

callbackUrl

ここに設定したURLに遷移(もしくはPOST送信)します。
ただし、これらは16進数にエンコードしてください

const callback = `sample://callback/param1=test`;
const url = `alice://sign?data=${transferTransaction.serialize()}&type=request_sign_transaction&callback=${Convert.uint8ToHex(callback)}`;

request_sign_transaction

Symbolのトランザクションペイロードに署名します。

const transaction = facade.transactionFactory.create({
    type: 'transfer_transaction_v1',
    signerPublicKey: aliceKeyPair.publicKey,
    recipientAddress: 'BOB_PLAIN_ADDRESS',
    mosaics: [
        { 
            mosaicId: 0x72C0212E67A08BCEn,
            amount: 1_000000n 
        }
    ],
    message: [0,...(new TextEncoder('utf-8')).encode('Hello, Symbol!!')],
    fee: 1_000000n,
    deadline,
});
const payload = symbolSdk.utils.uint8ToHex(transaction.serialize());

alice://sign?type=request_sign_transaction&data={payload}&callback={callbackURL}

sdk v2系の場合はhexへのコンバートは不要です

// v2
const payload = transaction.serialize();

Note: トランザクションのSignerPublicKeyはユーザが設定しているメインアカウントの公開鍵が自動的に設定されます。

以下をCallbackURLに返します。

{callbackUrl}?signed_payload={signedPayload}&pubkey={publicKey}&original_data={data}

signedPayloadは以下のように扱います

// v2 v2系では一度再構築しなおす必要があります
const hash = Transaction.createTransactionHash(payload, [... Convert.hexToUint8(generationHash)])
const signed = TransactionMapping.createFromPayload(payload);
const signedTx = new SignedTransaction(payload, hash, signed.signer?.publicKey!, signed.type, signed.type)

// v3 v3系ではそのままpayloadとして扱えますがput送信する時には以下のように扱う
const jsonObject = {
    payload: payload
};
const d = JSON.stringify(jsonObject);

Note: ここでのoriginal_dataは送信時のhex_payloadです。

暗号化メッセージ

暗号化メッセージはTransferTransactionのみで使えます。
パラメータにに&recipient_publicKey_for_encrypt_message={RecipientPublicKey}を追加で渡してください。なお、暗号化することでトランザクションのサイズは大きくなり手数料が変わります。追加で&fee_multiplier=100とすることで手数料乗数を設定できます。このパラメータがない場合は100に設定されます・

request_sign_utf8

UTF8文字列に署名します
文字列はそのままではなく16進数文字列にしてから送信してください

const data = Buffer.from('hello,symbol!', 'utf-8').toString('hex').toUpperCase()

alice://sign?type=request_sign_utf8&data={data}&callback={callbackURL}

以下をCallbackURLに返します。
{callbackUrl}?signature={signature}&pubkey={publicKey}&original_data={data}

request_sign_binary_hex

バイナリデータに署名します

// v3
const data = symbolSdk.default.utils.uint8ToHex(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
// v2
const data = Convert.uint8ToHex(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))

alice://sign?type=request_sign_binary_hex&data={data}&callback={callbackURL}

以下をCallbackURLに返します。
{callbackUrl}?signature={signature}&pubkey={publicKey}&original_data={data}

request_sign_batches

複数のInnerTransactionに全て署名します。
今の所Metal用です。
以下のようにtxsの16進数文字列をbatchクエリに追加してください。
データ数が大きくなることが想定されます。&method=postにしてください。URLが長すぎると画面遷移できません。

import {MetalService} from "metal-on-symbol";
const { txs, key, additive } = await metalService.createForgeTxs(
    type, 
    sourcePubAccount,
    targetPubAccount,
    targetId,
    payaload,
    additive,
    metadataPool
); 
let url = `alice://sign?type=request_sign_batches&method=post&callback=${callbackURL}`;
for(let i = 0; i < txs.length; i++){
  url += `&batch${i}=${tx.serialize()}`
}

以下のようなJSONをBodyに含め送信します

{
    "pubkey": "PUBLIC_KEY",
    "signed0": "SIGNED_PAYLOAD_0",
    "signed1": "SIGNED_PAYLOAD_1",
    "signed2": "SIGNED_PAYLOAD_2",
    ...
}

request_pubkey

ユーザが - aLice - のメインに設定しているアカウントの公開鍵(string)を返します。

エラーについて

ユーザが署名を拒否すると以下を返します

{callback}?error=sign_rejected

署名の検証について

トランザクションの検証

v3系の場合

// 返ってきた公開鍵とトランザクションの公開鍵が一致するかどうか
const publicKey = new symbolSdk.default.PublicKey("PUBLIC_KEY");
const tx = symbolSdk.default.symbol.TransactionFactory.deserialize(symbolSdk.utils.hexToUint8(payload));
console.log(symbolSdk.default.utils.uint8ToHex(tx.signerPublicKey.bytes) == symbolSdk.utils.uint8ToHex(publicKey.bytes));

// トランザクションの検証
console.log(facade.verifyTransaction(tx, tx.signature));

その他署名の検証

v3系の場合

const publicKey = new symbolSdk.default.PublicKey("PUBLIC_KEY");
const verifier = new facade.constructor.Verifier(publicKey);
console.log(verifier.verify(original_data, signature));