本地构建交易
本文将通过示例来向开发者介绍本地构造交易。本地构造交易用于构建交易内部数据,如果开发者有离线签名,交易备注等需求,可以参考此文内容。这些数据主要包括:
message raw {
bytes ref_block_bytes = 1;//最新块高度的第6到8(不包含)之间的字节
int64 ref_block_num = 3;//区块高度,可选
bytes ref_block_hash = 4;//最新块的hash的第8到16(不包含)之间的字节
int64 expiration = 8;//交易过期时间。最新块的时间加N分钟。N的大小根据需要设定(在本地节点的配置文件中修改,公共节点默认过期时间1分钟),后台的判断条件是(Expiration > 最新块时间 and Expiration < 最新块时时 + 24小时),如果条件成立则交易合法,否则交易为过期交易,不会被mainnet接收。
repeated authority auths = 9;//权限信息
// data not used
bytes data = 10;//未使用字段,可以添加备注信息,可选
//only support size = 1, repeated list here for extension
repeated Contract contract = 11;//合约信息
// scripts not used
bytes scripts = 12;//未使用字段,可以添加备注信息,可选
int64 timestamp = 14;//交易创建时间
int64 fee_limit = 18;//因资源不足,消耗trx的阀值
}
注:目前只支持RPC接口
Java示例
这里可以通过wallet-cli来引入RPC接口的调用环境。
package org.tron.demo;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.tron.api.GrpcAPI.Return;
import org.tron.api.GrpcAPI.TransactionExtention;
import org.tron.common.crypto.ECKey;
import org.tron.common.crypto.Sha256Sm3Hash;
import org.tron.common.utils.ByteArray;
import org.tron.core.exception.CancelException;
import org.tron.protos.Contract;
import org.tron.protos.Protocol.Block;
import org.tron.protos.Protocol.Transaction;
import org.tron.walletserver.WalletApi;
import java.util.Arrays;
public class TransactionSignDemo {
/*设置参照块数据*/
public static Transaction setReference(Transaction transaction, Block newestBlock) {
long blockHeight = newestBlock.getBlockHeader().getRawData().getNumber();
byte[] blockHash = getBlockHash(newestBlock).getBytes();
byte[] refBlockNum = ByteArray.fromLong(blockHeight);
Transaction.raw rawData = transaction.getRawData().toBuilder()
.setRefBlockHash(ByteString.copyFrom(ByteArray.subArray(blockHash, 8, 16)))
.setRefBlockBytes(ByteString.copyFrom(ByteArray.subArray(refBlockNum, 6, 8)))
.setRefBlockNum(blockHeight)
.build();
return transaction.toBuilder().setRawData(rawData).build();
}
public static Sha256Sm3Hash getBlockHash(Block block) {
return Sha256Sm3Hash.of(block.getBlockHeader().getRawData().toByteArray());
}
public static String getTransactionHash(Transaction transaction) {
String txid = ByteArray.toHexString(Sha256Sm3Hash.hash(transaction.getRawData().toByteArray()));
return txid;
}
public static Transaction createTransaction(byte[] from, byte[] to, long amount) {
Transaction.Builder transactionBuilder = Transaction.newBuilder();
Block newestBlock = WalletApi.getBlock(-1);
/*设置合约内部数据*/
Transaction.Contract.Builder contractBuilder = Transaction.Contract.newBuilder();
Contract.TransferContract.Builder transferContractBuilder =
Contract.TransferContract.newBuilder();
transferContractBuilder.setAmount(amount);
ByteString bsTo = ByteString.copyFrom(to);
ByteString bsOwner = ByteString.copyFrom(from);
transferContractBuilder.setToAddress(bsTo);
transferContractBuilder.setOwnerAddress(bsOwner);
try {
Any any = Any.pack(transferContractBuilder.build());
contractBuilder.setParameter(any);
} catch (Exception e) {
return null;
}
/*设置备注,交易过期时间等数据*/
contractBuilder.setType(Transaction.Contract.ContractType.TransferContract);
transactionBuilder.getRawDataBuilder().addContract(contractBuilder)
.setTimestamp(System.currentTimeMillis())
.setExpiration(newestBlock.getBlockHeader().getRawData().getTimestamp() + 10 * 60 * 60 * 1000)
.setData(ByteString.copyFromUtf8("memo"))
.setScripts(ByteString.copyFromUtf8("scripts"));
Transaction transaction = transactionBuilder.build();
Transaction refTransaction = setReference(transaction, newestBlock);
return refTransaction;
}
private static byte[] signTransaction2Byte(byte[] transaction, byte[] privateKey)
throws InvalidProtocolBufferException {
ECKey ecKey = ECKey.fromPrivate(privateKey);
Transaction transaction1 = Transaction.parseFrom(transaction);
byte[] rawdata = transaction1.getRawData().toByteArray();
byte[] hash = Sha256Sm3Hash.hash(rawdata);
byte[] sign = ecKey.sign(hash).toByteArray();
return transaction1.toBuilder().addSignature(ByteString.copyFrom(sign)).build().toByteArray();
}
private static boolean broadcast(byte[] transactionBytes) throws InvalidProtocolBufferException {
return WalletApi.broadcastTransaction(transactionBytes);
}
public static void main(String[] args) throws InvalidProtocolBufferException, CancelException {
String privateStr = "da146374a75310b9666e834ee4ad0866d6f4035967bfc76217c5a495fff9f0d0";
byte[] privateBytes = ByteArray.fromHexString(privateStr);
ECKey ecKey = ECKey.fromPrivate(privateBytes);
byte[] from = ecKey.getAddress();
byte[] to = WalletApi.decodeFromBase58Check("TN9RRaXkCFtTXRso2GdTZxSxxwufzxLQPP");
long amount = 100_000_000L; // 100 TRX, api only receive trx in Sun, and 1 trx = 1000000 Sun
Transaction transaction = createTransaction(from, to, amount);
byte[] transactionBytes = transaction.toByteArray();
byte[] transaction4 = signTransaction2Byte(transactionBytes, privateBytes);
boolean result = broadcast(transaction4);
System.out.println(result);
}
}执行完上述代码后,查询此交易信息的结果:
{
"ret": [
{
"contractRet": "SUCCESS"
}
],
"signature": [
"ced3929af13ca455fca59088c1a98908897640f6dc7c5746f1a21eb850fb266e6d7996e1984464b0d8a262f991342a1c9e4197311ea3562631f411ffde9abcda00"
],
"txID": "a1185aad04cfa78cb66d5eb3780b8801dde1d49d14aee9e21841194b81a64bd6",
"raw_data": {
"data": "6d656d6f",
"contract": [
{
"parameter": {
"value": {
"amount": 100000000,
"owner_address": "41bf97a54f4b829c4e9253b26024b1829e1a3b1120",
"to_address": "41859009fd225692b11237a6ffd8fdba2eb7140cca"
},
"type_url": "type.googleapis.com/protocol.TransferContract"
},
"type": "TransferContract"
}
],
"ref_block_bytes": "c75b",
"ref_block_hash": "7cfc890c6e4d7a15",
"expiration": 1583304414000,
"ref_block_num": 2541403,
"scripts": "73637269707473",
"timestamp": 1583268416080
},
"raw_data_hex": "0a02c75b18db8e9b0122087cfc890c6e4d7a1540b0a6b0a28a2e52046d656d6f5a68080112640a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412330a1541bf97a54f4b829c4e9253b26024b1829e1a3b1120121541859009fd225692b11237a6ffd8fdba2eb7140cca1880c2d72f62077363726970747370d0949b918a2e"
}
注:其中的data和scripts字段的信息可以用作备注信息(TronScan中会显示data数据),通过Hex转String来获取内容。
Updated 9 months ago