feat(pay): Phase D 归集 —— 气隙签名 sweep(#34/34A)
动钱代码,按"联网建/广播 + 离线签"气隙流程,离线签名独立验签防篡改: - wallet: DecodeTronAddress/TronAddressBodyHex(base58check 解码校验)、AddressFromMnemonic(离线校验用)。 - tron/sign: TxID(sha256 raw_data)、SignRawData(secp256k1 → R||S||recid 65B)、RecoverAddressBody。 - tron/abi: ABIEncodeTransferParams(transfer(address,uint256) 参数)、keccak 地址体。 - tron/tx(联网): BuildTransfer(triggersmartcontract 建未签名)、TRC20Balance、Broadcast。 - cmd/sweep: plan/build/sign/broadcast 四段;sign 对每笔独立验:①重算 txid 防篡改 ②收款人+额 ABI 内嵌 ③USDT 合约内嵌 ④派生地址==owner,任一不符拒签;助记词只经 PAY_SWEEP_MNEMONIC(离线机,不入 arg)。 - 测试:签名可恢复到正确地址、ABI 编码、地址解码 roundtrip 全绿。 - 联网上链部分标注需 Phase E 真链验证;README 补 sweep 用法 + 验签说明 + gas 提醒。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package tron
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/wangjia/pangolin/pay/internal/wallet"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// TransferSelector is the TRC20 transfer(address,uint256) function selector
|
||||
// string that TronGrid's triggersmartcontract expects.
|
||||
const TransferSelector = "transfer(address,uint256)"
|
||||
|
||||
// ABIEncodeTransferParams builds the 64-byte ABI parameter for
|
||||
// transfer(address,uint256): the recipient (20-byte body, left-padded to 32) and
|
||||
// the amount (uint256, left-padded to 32). Returns hex (no 0x, no 4-byte
|
||||
// selector — TronGrid derives the selector from TransferSelector).
|
||||
func ABIEncodeTransferParams(toAddr string, amount int64) (string, error) {
|
||||
if amount <= 0 {
|
||||
return "", fmt.Errorf("tron: transfer amount must be positive")
|
||||
}
|
||||
payload, err := wallet.DecodeTronAddress(toAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out := make([]byte, 64)
|
||||
copy(out[12:32], payload[1:]) // 20-byte body, right-aligned in first word
|
||||
new(big.Int).SetInt64(amount).FillBytes(out[32:64])
|
||||
return hex.EncodeToString(out), nil
|
||||
}
|
||||
|
||||
// keccakAddressBody derives the 20-byte address body from a 65-byte uncompressed
|
||||
// secp256k1 public key: keccak256(X||Y)[12:].
|
||||
func keccakAddressBody(uncompressed []byte) []byte {
|
||||
h := sha3.NewLegacyKeccak256()
|
||||
h.Write(uncompressed[1:])
|
||||
sum := h.Sum(nil)
|
||||
return sum[12:]
|
||||
}
|
||||
Reference in New Issue
Block a user