教程 | 使用WeBASE进行“两阶段交易”
“两阶段交易”是什么?“两阶段交易”是指分成两个步骤发送交易,即对交易编码并签名、将交易提交到链上这两个阶段:
第一阶段:构造并获取交易编码值,并通过私钥对交易编码值签名; 第二阶段:发送交易,也就是将已签名的编码值发送到链上。
值得一提的是,调用 /trans/convertRawTxStr/withSign 接口时:
如果传入了 signUserId 非空,则返回的交易体编码值是通过 signUserId 对应私钥签名后的交易体编码值。 如果传入的 signUserId 为空,则返回的是未签名的交易体编码值,开发者也可以通过JAVA-SDK用私钥对该值签名。
public void testSign(TransactionEncoderService encoderService, RawTransaction rawTransaction) {
// 未签名的交易编码值
String encodedTransaction = "0xf8a9a001b41b2cc71fe0bf0450f1fa4d820209b6686a8f226d217be0bc51cd9fc4a020018405f5e100820204941f2dfecfd75b883b51762aef6326d3ae9ad5230180b8644ed3885e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033132330000000000000000000000000000000000000000000000000000000000010180";
// 私钥
String privateKey = "0x123";
// ECDSA 加密套件
CryptoSuite cryptoSuite = new CryptoSuite(CryptoType.ECDSA_TYPE);
// 对待签名的编码值作哈运算
String hashMessageStr = cryptoSuite.hash(encodedTransaction);
System.out.println("hashMessageStr: " + hashMessageStr);
// 创建私钥对
CryptoKeyPair myKeyPair = cryptoSuite.createKeyPair(privateKey);
// 对交易编码值签名
SignatureResult signedTx = cryptoSuite.sign(hashMessageStr, myKeyPair);
// 获得最终签名后的交易编码值
byte[] signedTransaction = encoderService.encode(rawTransaction, signedTx);
// 转十六进制字符串
String signedTransactionStr = Numeric.toHexString(signedTransaction);
System.out.println("signedTransactionStr: " + signedTransactionStr);
}
// 构造Function实例
Function function = new Function(funcName, contractFunction.getFinalInputs(),
contractFunction.getFinalOutputs());
// 编码Function
FunctionEncoder functionEncoder = new FunctionEncoder(cryptoSuite);
String encodedFunction = functionEncoder.encode(function);
// 构造交易体
BigInteger randomId = new BigInteger(250, new SecureRandom());
BigInteger blockLimit = web3j.getBlockLimit();
RawTransaction rawTransaction =
RawTransaction.createTransaction(randomId, Constants.GAS_PRICE,
Constants.GAS_LIMIT, blockLimit, contractAddress, BigInteger.ZERO, encodedFunction,
new BigInteger(Constants.chainId), BigInteger.valueOf(groupId), "");
// 编码交易体RawTransaction
TransactionEncoderService encoderService = new TransactionEncoderService(cryptoSuite);
byte[] encodedTransaction = encoderService.encode(rawTransaction, null);
对交易编码值签名前,WeBASE-Front 中会根据传入的 user 字段和 isLocal 字段判断:
如果 user 字段为空,则将 encodedTransaction 转为十六进制后返回。该值就是第一阶段未签名的交易编码值。 如果 user 字段非空, isLocal 字段为 true,则 user 为 WeBASE-Front 本地的用户私钥,通过本地私钥对交易编码值 encodedTransaction 签名。注意,签名前还需对 encodedTransaction 进行一次哈希运算后再签名。 如果 user 字段非空, isLocal 字段为 false,则 user 为 WeBASE-Sign 托管私钥的signUserId,通过签名服务对交易体编码值 encodedTransaction 签名。注意,此处签名前没有对 encodedTransaction 进行哈希,而是直接转为十六进制发到签名服务,签名服务拿到该值后再做哈希运算并签名返回结果。
// encodedTransaction转十六进制
String hashMessageStr = Numeric.toHexString(encodedTransaction);
// 通过WeBASE-Sign签名
EncodeInfo encodeInfo = new EncodeInfo(user, hashMessageStr);
String signDataStr = keyStoreService.getSignData(encodeInfo);
// 反序列化签名结果
SignatureResult signData = CommonUtils.stringToSignatureData(signDataStr, cryptoSuite.cryptoTypeConfig);
// 加入签名结果,再次编码
byte[] signedMessage = encoderService.encode(rawTransaction, userSignResult);
// 转为十六进制
String signResultStr = Numeric.toHexString(signedMessage);
// 通过CryptoSuite实例计算signResultStr的交易哈希值
String transHash = cryptoSuite.hash(signResultStr);
即刻使用
上述优化及功能所涉及的最新代码和技术文档已同步更新,欢迎体验和star支持。如需咨询技术问题,欢迎本公众号对话框回复【小助手】进技术交流群。
评论