Webhook
Webhookは、ブロックチェーンイベントの発生に関する通知を、設定可能なHTTPエンドポイントに送信します。
現在、MultiBaaSは2種類のブロックチェーンイベント通知をサポートしています:
transaction.included: クラウドウォレットのトランザクションがブロックチェーン上のブロックに含まれました。event.emitted: イベント同期が有効になっているスマートコントラクトに対して、スマートコントラクトイベントが発行されました。
新しいWebhookの作成
MultiBaas Webhookにアクセスするには、ナビゲーションバーのBlockchainに移動してからWebhooksをクリックします。左上隅のプラスボタンをクリックして新しいWebhookを作成します。
必要な入力項目を記入します:
- Label: Webhookを識別するのに役立つラベル
- Enter Endpoint URL: エンドポイントのURL。
- transaction.included: トランザクションがブロックに含まれたときにWebhookがトリガーされます
- event.emitted: イベントが発行されたときにWebhookがトリガーされます
最後に、Add Endpointをクリックします。
Webhookの編集
- サイドメニューから編集したいWebhookを選択します
- ページの右上隅にある名前の横のペンボタンをクリックします
- 必要な変更を適用してUpdateをクリックします。
Webhookの削除
- サイドメニューから削除したいWebhookを選択します
- ページの右上隅にある名前の横のゴミ箱ボタンをクリックします
- Deleteボタンをクリックして確認します
Webhookの受信と検証
サブスクライブされたブロックチェーンイベントが発生すると、MultiBaaSは指定したHTTPエンドポイント(サーバー)に接続し、以下に指定されたJSON形式でデータを送信します。セキュリティのために、安全な接続(HTTPS/TLS)を使用することをお勧めします。
データ構造
HTTPボディには、次の形式のブロックチェーンイベントのJSON配列が含まれています:
[
{
"id": "<event identifier>",
"event": "<transaction.included|event.emitted>"
"data": {}
}
]
dataの値は、発行されたイベントのタイプに応じて異なります:
transaction.included: トランザクションレシートエンドポイントで返されるのと同じ形式event.emitted: イベントリストAPIエンドポイントで返されるのと同じ形式
データの検証
エンドポイントは公共のインターネットに開かれているため、エンドポイントへのリクエストが実際にMultiBaaSから来たものであることを検証することが重要です。MultiBaaSは、タイムスタンプとリクエストボディとタイムスタンプの署名をHTTPヘッダーとして送信します。X-MultiBaas-SignatureヘッダーにはHMAC(ハッシュベースメッセージ認証コード)署名が含まれ、X-MultiBaas-Timestampヘッダーにはタイムスタンプが保持されます。
HMAC署名は、リクエストボディデータとタイムスタンプおよびシークレットキーの組み合わせに基づいて生成されます。HMACアルゴリズムは、SHA-256ハッシュ関数と提供されたシークレットキーを使用して、リクエストのボディとタイムスタンプの組み合わせに対して一意の署名を作成します。
以下の擬似コードと説明手順は、リクエストボディと署名を検証するプロセスを詳細に示しています。
// addHMACSignature adds a timestamped HMAC signature to the given request's headers
func addHMACSignature(req *http.Request, jsonBody []byte, secret string) {
HMACSignature := NewHMACSignature(jsonBody, time.Now().Unix(), secret)
req.Header.Set("X-MultiBaas-Signature", HMACSignature.Signature)
req.Header.Set("X-MultiBaas-Timestamp", HMACSignature.Timestamp)
}
// HMACSignature represents an HMAC signature and its timestamp
type HMACSignature struct {
Signature string
Timestamp string
}
// NewHMACSignature creates an HMAC signature form the given data, timestamp and secret.
func NewHMACSignature(data []byte, timestamp int64, secret string) HMACSignature {
timestampStr := strconv.FormatInt(timestamp, 10)
// The message is the data + timestamp (as a decimal string)
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(data)
mac.Write([]byte(timestampStr))
signature := hex.EncodeToString(mac.Sum(nil))
return HMACSignature{signature, timestampStr}
}
ステップ1: ヘッダーからタイムスタンプと署名を抽出する:
addHMACSignature関数は、生成されたHMAC署名(HMACSignature.Signature)とタイムスタンプ(HMACSignature.Timestamp)をHTTPリクエストのヘッダー"X-MultiBaas-Signature"と"X-MultiBaas-Timestamp"にそれぞれ設定します。
ステップ2: signed_payload文字列を準備する:
NewHMACSignature関数は、提供されたデータ、タイムスタンプ、およびシークレットキーの組み合わせに基づいて署名を生成します。このプロセスは、データとタイムスタンプ(両方とも文字列として)を連結して、HMAC署名を作成するために使用される組み合わせメッセージを生成することにより、実質的に署名されたペイロードを作成します。
ステップ3: 期待される署名を決定する:
NewHMACSignature関数は、提供されたシークレットキーを使用してHMACアルゴリズム(SHA256ハッシュ関数)を使用し、データとタイムスタンプの組み合わせから一意の署名を作成します。
ステップ4: 署名を比較する:
addHMACSignature関数は、生成された署名とタイムスタンプをリクエストヘッダーに設定します。これらの値は、別の関数内で別の検証プロセスで後で比較するためにアクセスできます。
署名検証プロセスを実行するには、X-MultiBaas-SignatureとX-MultiBaas-Timestampヘッダーを取得し、受信したタイムスタンプと署名を比較に使用し、署名の手動検証のために前述の手順を実行する別の関数が必要です。
transaction.includedイベントタイプのサンプルWebhookデータ
ヘッダー
User-Agent: Go-http-client/1.1
Content-Length: 974
Content-Type: application/json
X-Multibaas-Signature: 50942fdcec1b92fc9de6319d9c8495d335b0c796b7c8ef9c55e32a985e2afe4a
X-Multibaas-Timestamp: 1699582292
Accept-Encoding: gzip
POSTボディ
[
{
"id": "f04c3919-120b-46ff-8766-85c3d0a081b6",
"event": "transaction.included",
"data": {
"tx": {
"type": "0x2",
"chainId": "0x64f6",
"nonce": "0x1",
"to": "0x9dee62d32898b37f2bdf7e7cb1fa16a45d31d67a",
"gas": "0x9ba9",
"gasPrice": null,
"maxPriorityFeePerGas": "0xa2d21d6e",
"maxFeePerGas": "0xcc755c72",
"value": "0x0",
"input": "0xa0712d68000000000000000000000000000000000000000000000000000000000001e240",
"accessList": [],
"v": "0x1",
"r": "0x17263ca900aefbce647e11a35d105f07fdb2075ffa5a63beb2b2f07e23102b1c",
"s": "0x5b0158f62f60902f86fb2e83f6ca824030cf0709277b9436637da62e9874da70",
"yParity": "0x1",
"hash": "0xe6136095471608942dda7f20b81b8a92c3bbb733ff4e6cb2960e6b457e1e14b2"
},
"status": "included",
"from": "0xf9450d254a66ab06b30cfa9c6e7ae1b7598c7172",
"failed": false,
"blockNumber": 10,
"blockHash": "0xa63e7d8463ca0d1285fc0585529edff5527821ec5d30d679d7a9496fe67563ec",
"resubmissionAttempts": 0,
"successfulResubmissions": 0,
"createdAt": "2023-11-10T11:11:30.755733+09:00",
"updatedAt": "2023-11-10T02:11:32.82265Z"
}
}
]
event.emittedイベントタイプのサンプルWebhookデータ
ヘッダー
User-Agent: Go-http-client/1.1
Content-Length: 3749
Content-Type: application/json
X-Multibaas-Signature: ddeac03177be7b17d08dad736b2817a584149b9bc44571f72d8121c8818068e5
X-Multibaas-Timestamp: 1699582290
Accept-Encoding: gzip
Body
[
{
"id": "952699ad-717c-413c-ab58-0c779fa2fffc",
"event": "event.emitted",
"data": {
"triggeredAt": "2023-11-10T11:11:30+09:00",
"event": {
"name": "Mint",
"signature": "Mint(address,address,uint256)",
"inputs": [
{
"name": "minter",
"value": "0xF9450D254A66ab06b30Cfa9c6e7AE1B7598c7172",
"hashed": false,
"type": "address"
},
{
"name": "receiver",
"value": "0xF9450D254A66ab06b30Cfa9c6e7AE1B7598c7172",
"hashed": false,
"type": "address"
},
{
"name": "value",
"value": "123.456",
"hashed": false,
"type": "uint256"
}
],
"rawFields": "{\"address\":\"0x9dee62d32898b37f2bdf7e7cb1fa16a45d31d67a\",\"topics\":[\"0xab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8\",\"0x000000000000000000000000f9450d254a66ab06b30cfa9c6e7ae1b7598c7172\",\"0x000000000000000000000000f9450d254a66ab06b30cfa9c6e7ae1b7598c7172\"],\"data\":\"0x000000000000000000000000000000000000000000000000000000000001e240\",\"blockNumber\":\"0xa\",\"transactionHash\":\"0xe6136095471608942dda7f20b81b8a92c3bbb733ff4e6cb2960e6b457e1e14b2\",\"transactionIndex\":\"0x0\",\"blockHash\":\"0xa63e7d8463ca0d1285fc0585529edff5527821ec5d30d679d7a9496fe67563ec\",\"logIndex\":\"0x0\",\"removed\":false}",
"contract": {
"address": "0x9deE62D32898B37F2BDf7e7cB1FA16a45D31D67a",
"addressLabel": "autotoken",
"name": "MltiToken",
"label": "mltitoken"
},
"indexInLog": 0
},
"transaction": {
"from": "0xF9450D254A66ab06b30Cfa9c6e7AE1B7598c7172",
"txData": "0xa0712d68000000000000000000000000000000000000000000000000000000000001e240",
"txHash": "0xe6136095471608942dda7f20b81b8a92c3bbb733ff4e6cb2960e6b457e1e14b2",
"txIndexInBlock": 0,
"blockHash": "0xa63e7d8463ca0d1285fc0585529edff5527821ec5d30d679d7a9496fe67563ec",
"blockNumber": 10,
"contract": {
"address": "0x9deE62D32898B37F2BDf7e7cB1FA16a45D31D67a",
"addressLabel": "autotoken",
"name": "MltiToken",
"label": "mltitoken"
},
"method": {
"name": "mint",
"signature": "mint(uint256)",
"inputs": [
{
"name": "_amount",
"value": "123.456",
"type": "uint256"
}
]
}
}
}
},
{
"id": "78274107-0db5-4c02-b80e-eff6430a4cc3",
"event": "event.emitted",
"data": {
"triggeredAt": "2023-11-10T11:11:30+09:00",
"event": {
"name": "Transfer",
"signature": "Transfer(address,address,uint256)",
"inputs": [
{
"name": "from",
"value": "0x0000000000000000000000000000000000000000",
"hashed": false,
"type": "address"
},
{
"name": "to",
"value": "0xF9450D254A66ab06b30Cfa9c6e7AE1B7598c7172",
"hashed": false,
"type": "address"
},
{
"name": "value",
"value": "123.456",
"hashed": false,
"type": "uint256"
}
],
"rawFields": "{\"address\":\"0x9dee62d32898b37f2bdf7e7cb1fa16a45d31d67a\",\"topics\":[\"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\",\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x000000000000000000000000f9450d254a66ab06b30cfa9c6e7ae1b7598c7172\"],\"data\":\"0x000000000000000000000000000000000000000000000000000000000001e240\",\"blockNumber\":\"0xa\",\"transactionHash\":\"0xe6136095471608942dda7f20b81b8a92c3bbb733ff4e6cb2960e6b457e1e14b2\",\"transactionIndex\":\"0x0\",\"blockHash\":\"0xa63e7d8463ca0d1285fc0585529edff5527821ec5d30d679d7a9496fe67563ec\",\"logIndex\":\"0x1\",\"removed\":false}",
"contract": {
"address": "0x9deE62D32898B37F2BDf7e7cB1FA16a45D31D67a",
"addressLabel": "autotoken",
"name": "MltiToken",
"label": "mltitoken"
},
"indexInLog": 1
},
"transaction": {
"from": "0xF9450D254A66ab06b30Cfa9c6e7AE1B7598c7172",
"txData": "0xa0712d68000000000000000000000000000000000000000000000000000000000001e240",
"txHash": "0xe6136095471608942dda7f20b81b8a92c3bbb733ff4e6cb2960e6b457e1e14b2",
"txIndexInBlock": 0,
"blockHash": "0xa63e7d8463ca0d1285fc0585529edff5527821ec5d30d679d7a9496fe67563ec",
"blockNumber": 10,
"contract": {
"address": "0x9deE62D32898B37F2BDf7e7cB1FA16a45D31D67a",
"addressLabel": "autotoken",
"name": "MltiToken",
"label": "mltitoken"
},
"method": {
"name": "mint",
"signature": "mint(uint256)",
"inputs": [
{
"name": "_amount",
"value": "123.456",
"type": "uint256"
}
]
}
}
}
}
]