# 交易签名
对交易签名前请将交易对象序列化。
# 序列化交易对象
交易对象数据结构请参见 交易协议。
将交易对象序列化,即按照固定顺序将交易对象中的参数转换成二进制数组。
交易对象中的参数主要有 4 个基础类型,分别是 Uint16, Uint32, Uint64, String。
由于 Java 中没有无符号类型,所以涉及数字一律用 BigInteger 类型,序列化时再转换。
序列化采用小端字节序(little endian)。
其他组合类型都需要拆分成基础类型处理,例如在 JavaSDK 的投票接口中,传入的是 Map<String, BigInteger> 对象,需拆分成 Text 和 Uint64 拼接在一起。
请按照下表中的顺序将参数序列化:
参数名称 | 参数类型 | 示例值 | 序列化值(Hex) |
---|---|---|---|
tx_type | Uint16 | 4 | 0x0400 |
tx_len | Uint16 | 0 | 0x0000 |
tx_structure_version | Uint32 | 0 | 0x00000000 |
to_ledger_id | Uint16 | 0 | 0x0000 |
from_ledger_id | Uint16 | 0 | 0x0000 |
tx_deposit | Uint32 | 100000 | 0xa0860100 |
tx_expire_duration | Uint16 | 100 | 0x6400 |
send_time_stamp | Uint64 | 1602820177 | 0x5118895f00000000 |
tx_random_nonce | Uint32 | 0 | 0x00000000 |
last_tx_nonce | Uint64 | 2 | 0x0200000000000000 |
last_tx_hash | String | 0x2a21b09155cad8aa | 0xaad8ca5591b0212a |
challenge_proof | String | "" | 0x00000000 |
ext | String | "" | 0x00000000 |
note | String | "1231fsd" | 0x0700000031323331667364 |
sender_action | Object | - | - |
receiver_action | Object | - | - |
sender action 参数序列化:
参数名称 | 参数类型 | 示例值 | 序列化值(Hex) |
---|---|---|---|
action_hash | Uint32 | 0 | 0x00000000 |
action_type | Uint16 | 0 | 0x0000 |
action_size | Uint16 | 0 | 0x0000 |
tx_sender_account_addr | String | T8000066ab344963eaa071f9636faac26b0d1a39900325 | 0x26000000542d302d4c5261467961475a31697366634b6e684c78504c6f655046556d5932697946477636 |
action_name | String | "" | 0x00000000 |
action_param | String | 0x000000008c00000000000000 | 0x0c000000000000008c00000000000000 |
action_ext | String | "" | 0x00000000 |
action_authorization | String | "" | 0x00000000 |
receiver action 参数序列化:
参数名称 | 参数类型 | 示例值 | 序列化值(Hex) |
---|---|---|---|
action_hash | Uint32 | 0 | 0x00000000 |
action_type | Uint16 | 6 | 0x0000 |
action_size | Uint16 | 0 | 0x0000 |
tx_sender_account_addr | String | T8000085a8e8acd53c72dca85dcb002a6710796975b4ba | 0x26000000542d302d4c617a4e7a7679484c70747a6450466b61796e4e484b7144593471585a3267435668 |
action_name | String | "" | 0x00000000 |
action_param | String | 0x000000008c00000000000000 | 0x0c000000000000008c00000000000000 |
action_ext | String | "" | 0x00000000 |
action_authorization | String | ||
非部署用户合约交易: "" | 0x00000000 | ||
部署用户合约交易,需要传入用户合约公钥: {"authorization":"0x0426dc404815d13f7d08017818825b5df1bd3bb61c3a84d44579427cac43bce4b906498135dfa42adf7ae73bf9885250f88188207974c5cb965611bb23464591eb"} | 980000007b22617574686f72697a6174696f6e223a22307830343236646334303438313564313366376430383031373831383832356235646631626433626236316333613834643434353739343237636163343362636534623930363439383133356466613432616466376165373362663938383532353066383831383832303739373463356362393635363131626232333436343539316562227d |
其中 action_param
序列化请参见 action_param 序列化。
# 签名
签名步骤
对序列化后的二进制数组进行 SHA256 hash 计算。
使用私钥对步骤 1 计算的结果进行签名。
签名算法
ECDSA(secp256k1)
Demo
public static String signData(byte[] dataBytes, BigInteger privateKey) throws Exception {
ECKey ceKey = ECKey.fromPrivate(privateKey, false);
Sha256Hash sha256Hash = Sha256Hash.wrap(dataBytes);
ECKey.ECDSASignature sig = ceKey.sign(sha256Hash);
byte recId = ceKey.findRecoveryId(sha256Hash, sig);
String authHex = TextUtils.bytesToHex(sig.r.toByteArray()) + TextUtils.bytesToHex(sig.s.toByteArray());
if (authHex.length() == 130) {
if(recId == 1 && "00".equals(authHex.subText(0, 2))) {
authHex = authHex.replaceFirst("00", "01");
}
return "0x" + authHex;
}
if(recId == 1) {
authHex = "01" + authHex;
} else {
authHex = "00" + authHex;
}
return "0x" + authHex;
}
签名后结果
0x015c5be7876376b09297f9247e34e1e921474f2f66c61700e57832546e6c1de918136abe1a56c346ebf2dce9358941df89d02fd5274222435e7bd0f756de1adf83