Skip to content

消息 API

消息 API 允许您验证并向 Hub 提交已签名的 Farcaster 协议消息。请注意,消息必须作为 protobuf 的编码字节流(在 TypeScript 中为 Message.encode(msg).finish())通过 POST 数据发送到端点。

POST 数据的编码必须设置为 application/octet-stream。如果消息成功提交或验证,端点将返回 JSON 格式的 Message 对象。

submitMessage

向 Hub 提交已签名的 protobuf 序列化消息

查询参数

参数描述示例
此端点不接受任何参数

示例

bash
curl -X POST "http://127.0.0.1:2281/v1/submitMessage" \
     -H "Content-Type: application/octet-stream" \
     --data-binary "@message.encoded.protobuf"

响应

json
{
  "data": {
    "type": "MESSAGE_TYPE_CAST_ADD",
    "fid": 2,
    "timestamp": 48994466,
    "network": "FARCASTER_NETWORK_MAINNET",
    "castAddBody": {
      "embedsDeprecated": [],
      "mentions": [],
      "parentCastId": {
        "fid": 226,
        "hash": "0xa48dd46161d8e57725f5e26e34ec19c13ff7f3b9"
      },
      "text": "Cast Text",
      "mentionsPositions": [],
      "embeds": []
    }
  },
  "hash": "0xd2b1ddc6c88e865a33cb1a565e0058d757042974",
  "hashScheme": "HASH_SCHEME_BLAKE3",
  "signature": "3msLXzxB4eEYe...dHrY1vkxcPAA==",
  "signatureScheme": "SIGNATURE_SCHEME_ED25519",
  "signer": "0x78ff9a...58c"
}

认证

如果服务器启用了 RPC 认证(使用 --rpc-auth username:password),在调用 submitMessage 时还需要通过 HTTP 基本认证传递用户名和密码。

示例

bash
curl -X POST "http://127.0.0.1:2281/v1/submitMessage" \
     -u "username:password" \
     -H "Content-Type: application/octet-stream" \
     --data-binary "@message.encoded.protobuf"

JS 示例

Javascript
import axios from "axios";

const url = `http://127.0.0.1:2281/v1/submitMessage`;

const postConfig = {
  headers: { "Content-Type": "application/octet-stream" },
  auth: { username: "username", password: "password" },
};

// 将消息编码为字节 Buffer
const messageBytes = Buffer.from(Message.encode(castAdd).finish());

try {
  const response = await axios.post(url, messageBytes, postConfig);
} catch (e) {
  // 处理错误...
}

validateMessage

通过 Hub 验证已签名的 protobuf 序列化消息。这可用于验证 Hub 是否认为消息有效,或验证无法提交的消息(例如 Frame 操作)。

Details

Hub 对所有消息进行以下验证:

  • FID 已注册
  • 签名者处于活跃状态并已注册到 FID
  • 消息哈希正确
  • 签名有效且与签名者对应
  • 任何其他特定于消息的验证

对于 FrameAction 消息,请注意 Hub 不会验证 castId 是否实际存在,也不会验证 frame URL 是否与 cast 中嵌入的 URL 匹配。如果这对您的应用很重要,请确保进行检查。

查询参数

参数描述示例
此端点不接受任何参数

示例

bash
curl -X POST "http://127.0.0.1:2281/v1/validateMessage" \
     -H "Content-Type: application/octet-stream" \
     --data-binary "@message.encoded.protobuf"

响应

json
{
  "valid": true,
  "message": {
    "data": {
      "type": "MESSAGE_TYPE_FRAME_ACTION",
      "fid": 2,
      "timestamp": 48994466,
      "network": "FARCASTER_NETWORK_MAINNET",
      "frameActionBody": {
        "url": "https://fcpolls.com/polls/1",
        "buttonIndex": 2,
        "inputText": "",
        "castId": {
          "fid": 226,
          "hash": "0xa48dd46161d8e57725f5e26e34ec19c13ff7f3b9"
        }
      }
    },
    "hash": "0xd2b1ddc6c88e865a33cb1a565e0058d757042974",
    "hashScheme": "HASH_SCHEME_BLAKE3",
    "signature": "3msLXzxB4eEYe...dHrY1vkxcPAA==",
    "signatureScheme": "SIGNATURE_SCHEME_ED25519",
    "signer": "0x78ff9a...58c"
  }
}

在 Rust、Go 或其他编程语言中使用

消息需要使用属于 FID 的 Ed25519 账户密钥签名。如果您使用的编程语言不是 TypeScript,可以手动构造 MessageData 对象并将其序列化为消息的 data_bytes 字段。然后,使用 data_bytes 计算 hashsignature。更多详情请参阅 rust-submitmessage 示例

rust
use ed25519_dalek::{SecretKey, Signer, SigningKey};
use hex::FromHex;
use reqwest::Client;

use message::{CastAddBody, FarcasterNetwork, MessageData};
use protobuf::Message;


#[tokio::main]
async fn main() {
    let fid = 6833; // 提交消息的用户 FID
    let network = FarcasterNetwork::FARCASTER_NETWORK_MAINNET;

    // 构造 cast add 消息
    let mut cast_add = CastAddBody::new();
    cast_add.set_text("Welcome to Rust!".to_string());

    // 构造 cast add 消息数据对象
    let mut msg_data = MessageData::new();
    msg_data.set_field_type(message::MessageType::MESSAGE_TYPE_CAST_ADD);
    msg_data.set_fid(fid);
    msg_data.set_timestamp(
        (std::time::SystemTime::now()
            .duration_since(FARCASTER_EPOCH)
            .unwrap()
            .as_secs()) as u32,
    );
    msg_data.set_network(network);
    msg_data.set_cast_add_body(cast_add);

    let msg_data_bytes = msg_data.write_to_bytes().unwrap();

    // 计算 blake3 哈希,截断为 20 字节
    let hash = blake3::hash(&msg_data_bytes).as_bytes()[0..20].to_vec();

    // 构造实际消息
    let mut msg = message::Message::new();
    msg.set_hash_scheme(message::HashScheme::HASH_SCHEME_BLAKE3);
    msg.set_hash(hash);

    // 签名消息。您需要使用与要添加的 FID 对应的签名密钥。
    // 请替换为您自己的私钥
    let private_key = SigningKey::from_bytes(
        &SecretKey::from_hex("0x...").expect("请提供有效的私钥"),
    );
    let signature = private_key.sign(&msg_data_bytes).to_bytes();

    msg.set_signature_scheme(message::SignatureScheme::SIGNATURE_SCHEME_ED25519);
    msg.set_signature(signature.to_vec());
    msg.set_signer(private_key.verifying_key().to_bytes().to_vec());

    // 序列化消息
    msg.set_data_bytes(msg_data_bytes.to_vec());
    let msg_bytes = msg.write_to_bytes().unwrap();

    // 最后,将消息提交到网络

    // 创建 reqwest Client
    let client = Client::new();

    // 定义端点 URL
    let url = "http://127.0.0.1:2281/v1/submitMessage";

    // 发送 POST 请求
    let res = client
        .post(url)
        .header("Content-Type", "application/octet-stream")
        .body(msg_bytes)
        .send()
        .await
        .unwrap();

    // 检查是否成功
    if res.status().is_success() {
        println!("消息发送成功。");
    } else {
        println!("消息发送失败。HTTP 状态码: {}", res.status());
    }
}