How to Build a Transaction Locally
This article will show developers how to build a transaction locally through an example. It is used to build data inside the transaction. If developers have offline-signature, remark transaction and other requirements, you can refer to this article. These data include:
message raw {
bytes ref_block_bytes = 1;//The bytes from 6 to 8(not included) of the height of the latest block
int64 ref_block_num = 3;//The height of the latest block.Optional.
bytes ref_block_hash = 4;//The bytes from the 8 to 16(not included) of the hash of the latest block
int64 expiration = 8;// Use the latest block time plus N minutes as the value of expiration time('N' can be modified in the configuration file of the local node, if using a public node, the default 'N' is one minute).The network judgment condition is if (expiration > latest block time and expiration < latest block time + 24 hours) means the transaction is in period of validity. Otherwise, it will be an overdue transaction, will not be accepted by the Mainnet.
repeated authority auths = 9;//Permission setting
// data not used
bytes data = 10;//Unused field, can be used for remark information, optional
//only support size = 1, repeated list here for extension
repeated Contract contract = 11;//The contract data
// scripts not used
bytes scripts = 12;//Unused field, can be used for remark information, optional
int64 timestamp = 14;//Create transaction time
int64 fee_limit = 18;//Threshold for trx consumption due to insufficient resources
}
Note:
Only supports RPC
Java
The calling environment of the RPC interface can be obtained through wallet-cli
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 {
/* set reference data*/
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);
/*set the contract data*/
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;
}
/*set memo,etc*/
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;
}
/*signature*/
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);
}
}
After executing the above code, you can get the following information:
{
"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"
}
Note:
The information in the
dataandscriptsfields can be used as remark information (datais displayed in TronScan), and the content can be obtained by converting Hex to String.
Updated over 5 years ago