Blockchain Symbol のアグリゲートボンデッドトランザクションに署名する

前回はアグリゲートボンデッドで他のアカウントに請求するトランザクションを発行しました。その際、相手の連署が必要となりますが、今回はその連署をコードで行ってみます。

https://qiita.com/ccHarvestasya/items/0ab163dc7139e51f14b0

連署はトランザクションハッシュがあれば出来るので、前回のコードの最後にボブで連署するメソッドを設置する体で進めます。

## メソッド
メソッドはこのような形にします。
`Hash256`は`'symbol-sdk/ts/src/CryptoTypes'`をインポートします。
```typescript
export async function bobCosignature(aggregateTxHash: Hash256)
```

## 必要な情報を設定
今回はボブが署名するので、ボブの秘密鍵が必要です。
```typescript
/** APIノードURL */
const NODE = 'http://sym-test-01.opening-line.jp:3000';

/** ボブ秘密鍵 */
const BOB_PRIVATE_KEY =
  '****************************************************************';
```

## アカウント生成
ボブの秘密鍵からアカウントを生成します。
```typescript
  // 秘密鍵からボブアカウント生成
  const bobKeyPair = new symbolSdk.symbol.KeyPair(
    new symbolSdk.PrivateKey(BOB_PRIVATE_KEY)
  );
```

## パーシャル通知待ち
パーシャルがまだの可能性があるので、通知されるまで待機します。
```typescript
  // パーシャル通知待ち
  for (let i = 0; i < 100; i++) {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    const hashLockStatus = await fetch(
      new URL(`/transactionStatus/${aggregateTxHash}`, NODE),
      {
        method: 'GET',
        headers: { 'Content-Type': 'application/json' },
      }
    );
    if ((await hashLockStatus.json()).group === 'partial') {
      break;
    }
  }
```

## ボブによる連署
トランザクション生成時に取得出来るトランザクションハッシュの型`Hash256`は、`symbol-sdk/ts/src/CryptoTypes`のもので、ここで使う`Hash256`は`'symbol-sdk/ts/src/symbol/models'`のものです。なので、別名を付けるなりしてキャストしてあげないとエラーになります。
`import * as sm from 'symbol-sdk/ts/src/symbol/models';`

newした方がいいのでしょうけど、コレで動くので…

```typescript
  // ボブ連署
  const bobCosignature = new symbolSdk.symbol.DetachedCosignature();
  bobCosignature.parentHash = aggregateTxHash as sm.Hash256;
  bobCosignature.signerPublicKey = bobKeyPair.publicKey as sm.PublicKey;
  bobCosignature.signature = new symbolSdk.symbol.Signature(
    bobKeyPair.sign(aggregateTxHash.bytes).bytes
  );
  bobCosignature.version = 0n;
```

## アナウンス
jsonを出力するメソッドがないので、自力でjsonを作成します。
アナウンスは`/transactions/cosignature`へPUTします。
```typescript
  // 連署Json作成
  const bobCosignatureReq = {
    parentHash: bobCosignature.parentHash.toString(),
    signature: bobCosignature.signature.toString(),
    signerPublicKey: bobCosignature.signerPublicKey.toString(),
    version: bobCosignature.version.toString(),
  };
  const bobCosignatureJson = JSON.stringify(bobCosignatureReq);
  // アナウンス
  const bobCosignatureResponse = await fetch(
    new URL('/transactions/cosignature', NODE),
    {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: bobCosignatureJson,
    }
  );
  const responseJson = await bobCosignatureResponse.json();
  console.log(`restResponse      : ${responseJson.message}`);
```

## 前回のコードの末尾に作成したメソッド呼び出し追加
```typescript
bobCosignature(aggregateTxHash);
```

## 全文
```typescript
import symbolSdk from 'symbol-sdk';
import { Hash256 } from 'symbol-sdk/ts/src/CryptoTypes';
import * as sm from 'symbol-sdk/ts/src/symbol/models';

export async function bobCosignature(aggregateTxHash: Hash256) {
  /** APIノードURL */
  const NODE = 'http://sym-test-01.opening-line.jp:3000';
  /** ボブ秘密鍵 */
  const BOB_PRIVATE_KEY =
    '****************************************************************';
  // 秘密鍵からボブアカウント生成
  const bobKeyPair = new symbolSdk.symbol.KeyPair(
    new symbolSdk.PrivateKey(BOB_PRIVATE_KEY)
  );
  // パーシャル通知待ち
  for (let i = 0; i < 100; i++) {
    await new Promise((resolve) => setTimeout(resolve, 1000));
    const hashLockStatus = await fetch(
      new URL(`/transactionStatus/${aggregateTxHash}`, NODE),
      {
        method: 'GET',
        headers: { 'Content-Type': 'application/json' },
      }
    );
    if ((await hashLockStatus.json()).group === 'partial') {
      break;
    }
  }
  // // faced生成
  // const facade = new symbolSdk.facade.SymbolFacade(NETWORK_IDENTIFIER);
  // facade.cosignTransaction
  // ボブ連署
  const bobCosignature = new symbolSdk.symbol.DetachedCosignature();
  bobCosignature.parentHash = aggregateTxHash as sm.Hash256;
  bobCosignature.signerPublicKey = bobKeyPair.publicKey as sm.PublicKey;
  bobCosignature.signature = new symbolSdk.symbol.Signature(
    bobKeyPair.sign(aggregateTxHash.bytes).bytes
  );
  bobCosignature.version = 0n;
  // 連署Json作成
  const bobCosignatureReq = {
    parentHash: bobCosignature.parentHash.toString(),
    signature: bobCosignature.signature.toString(),
    signerPublicKey: bobCosignature.signerPublicKey.toString(),
    version: bobCosignature.version.toString(),
  };
  const bobCosignatureJson = JSON.stringify(bobCosignatureReq);
  // アナウンス
  const bobCosignatureResponse = await fetch(
    new URL('/transactions/cosignature', NODE),
    {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: bobCosignatureJson,
    }
  );
  const responseJson = await bobCosignatureResponse.json();
  console.log(`restResponse      : ${responseJson.message}`);
}
```