How to send a signed transaction using Ethereum's Java library "Web3j"
It's pretty rough because it's a code I wrote for my own learning. .. In this sample source, the transaction send method is created with and without signature, respectively, and both are called in order from the main method.
SendTransaction.java
package jp.ethereum.transaction;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Optional;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.admin.methods.response.PersonalUnlockAccount;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert.Unit;
import org.web3j.utils.Numeric;
public class SendTransaction {
// ①
public static final Admin web3j = Admin.build(new HttpService("http://127.0.0.1:8545"));
public static void main(String args[]) {
try {
// ②
//Get transaction sender credentials
//Credentials include public and private keys
String password = "password";
Credentials credentials = WalletUtils.loadCredentials(password, "Full path of private key file");
String toAddress = "0x66b4e7be902300f9a15d900822bbd8803be87391";
SendTransaction tx= new SendTransaction();
//Send transaction
tx.sendTransaction(credentials, password, toAddress, 10);
//Send signed transaction
TransactionReceipt receipt = tx.sendSignedTransaction(credentials, password, toAddress);
if (receipt != null) System.out.println(receipt.getTransactionHash()) ;
}catch(IOException | CipherException ex) {
ex.printStackTrace();
}
}
public TransactionReceipt sendTransaction(Credentials credentials, String password, String toAddress, long value) {
TransactionReceipt receipt = null;
try {
//Transaction generation
// "personal_unlockAccount"Send a request and receive a response
PersonalUnlockAccount unlockAccountResponse = web3j.personalUnlockAccount(
credentials.getAddress(), //address
password //password
).send();
//If the unlock is successful, send Ether
if (unlockAccountResponse.getResult()) {
//Send a Transaction. No response is returned until it is loaded into the Block
receipt = Transfer.sendFunds(web3j, credentials, toAddress, BigDecimal.valueOf(value), Unit.ETHER).send();
}
}catch(IOException | TransactionException ex) {
ex.printStackTrace();
}catch(Exception ex) {
ex.printStackTrace();
}
return receipt;
}
public TransactionReceipt sendSignedTransaction(Credentials credentials, String password, String toAddress) {
TransactionReceipt receipt = null;
try {
//Transaction generation
// "personal_unlockAccount"Send a request and receive a response
PersonalUnlockAccount unlockAccountResponse = web3j.personalUnlockAccount(
credentials.getAddress(), //address
password //password
).send();
//If the unlock is successful, send Ether
if (unlockAccountResponse.getResult()) {
// "eth_sendTransaction"Create an object to pass to the argument of
RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
BigInteger.valueOf(10), // nonce
BigInteger.valueOf(700), // gasPrice
BigInteger.valueOf(4712388), // gasLimit
toAddress, // to
BigInteger.valueOf(101) // value
);
//Sign transaction
byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
String hexValue = Numeric.toHexString(signedMessage);
//Send transaction
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
String response = ethSendTransaction.getRawResponse();
String transactionHash = ethSendTransaction.getTransactionHash();
Optional<TransactionReceipt> transactionReceipt = null;
int retry = 0;
//Transaction monitoring
if(transactionHash != null) {
do {
System.out.printf("%3d checking if transaction " + transactionHash + " is mined....\n" ,retry);
EthGetTransactionReceipt ethGetTransactionReceiptResp =
web3j.ethGetTransactionReceipt(transactionHash).send();
transactionReceipt = ethGetTransactionReceiptResp.getTransactionReceipt();
Thread.sleep(3000);
retry++;
}while(!transactionReceipt.isPresent() && retry < 100);
} else {
System.out.println("Transaction Send failed...");
System.out.println("Message:" + ethSendTransaction.getError().getMessage());
System.out.println("Data :" + ethSendTransaction.getError().getData());
}
}
}catch(IOException | InterruptedException ex) {
ex.printStackTrace();
}catch(Exception ex) {
ex.printStackTrace();
}
return receipt;
}
}
I will supplement the points that are likely to be points.
Connect to Ethereum
SendTransaction.java
// ①
public static final Admin admin = Admin.build(new HttpService("http://127.0.0.1:8545"));
Here, we are creating an instance of the Admin class. The Admin class is a class that inherits from the Web3j class, and you can use methods that use personal information that are not implemented in the Web3j class. Use the Admin class instead of Web3j because sending transactions requires unlocking the account.
The URL specified in the argument will be the address specified by the "--rpcaddr" option when geth is started. If you do not specify the port, it will be "8545".
SendTransaction.java
//Get transaction sender credentials
//Credentials include public and private keys
String password = "password";
Credentials credentials = WalletUtils.loadCredentials(password, "Full path of private key file");
Get the private key information with the WalletUtils.loadCredentials method. User account password as the first argument Specify the path (including the file name) of the private key file (file starting with UTC) in the second argument.
All you have to do is unlock your account and send a transaction.
SendTransaction.java
public TransactionReceipt sendTransaction(Credentials credentials, String password, String toAddress, long value) {
TransactionReceipt receipt = null;
try {
//Transaction generation
// "personal_unlockAccount"Send a request and receive a response
PersonalUnlockAccount unlockAccountResponse = web3j.personalUnlockAccount(
credentials.getAddress(), //address
password //password
).send();
//If the unlock is successful, send Ether
if (unlockAccountResponse.getResult()) {
//Send a Transaction. No response is returned until it is loaded into the Block
receipt = Transfer.sendFunds(web3j, credentials, toAddress, BigDecimal.valueOf(value), Unit.ETHER).send();
}
}catch(IOException | TransactionException ex) {
ex.printStackTrace();
}catch(Exception ex) {
ex.printStackTrace();
}
return receipt;
}
The point is that no response was returned until the transaction was taken into the block. Although the block generation interval is shorter than Bitcoin, it seems that it will be necessary to consider it if there is a situation to use it in an actual application.
receipt = Transfer.sendFunds(web3j, credentials, toAddress, BigDecimal.valueOf(value), Unit.ETHER).send();
First, check with "eth.getTransaction" when the transaction is generated.
INFO [09-15|23:28:07.213] Submitted transaction fullhash=0xfa7ab0924c82b9e45a10acd5c6b72136a088b4dee0d4d4810a2d4f4408c3ee97 recipient=0x66B4e7bE902300F9a15D900822Bbd8803Be87391
> eth.getTransaction("0xfa7ab0924c82b9e45a10acd5c6b72136a088b4dee0d4d4810a2d4f4408c3ee97")
{
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
blockNumber: null,
from: "0x945cd603a6754cb13c3d61d8fe240990f86f9f8a",
gas: 21000,
gasPrice: 1000000000,
hash: "0xfa7ab0924c82b9e45a10acd5c6b72136a088b4dee0d4d4810a2d4f4408c3ee97",
input: "0x",
nonce: 7,
r: "0x3ca5a820995553d30656f2218dc10729d3e0f660c35817bbd69845ac96dc6279",
s: "0x2e915ae47699771108f65881273f464d70db0229ad12a94ac38746f498ea7ed3",
to: "0x66b4e7be902300f9a15d900822bbd8803be87391",
transactionIndex: 0,
v: "0x1c",
value: 10000000000000000000
}
The transaction has been created. Mining and see if it gets into the block.
> eth.getTransaction("0xfa7ab0924c82b9e45a10acd5c6b72136a088b4dee0d4d4810a2d4f4408c3ee97")
{
blockHash: "0xc9634aec9670d312759a0e12ea5fee54948688c88e4d45a5b9cdbeef3c44c681",
blockNumber: 2792,
from: "0x945cd603a6754cb13c3d61d8fe240990f86f9f8a",
gas: 21000,
gasPrice: 1000000000,
hash: "0xfa7ab0924c82b9e45a10acd5c6b72136a088b4dee0d4d4810a2d4f4408c3ee97",
input: "0x",
nonce: 7,
r: "0x3ca5a820995553d30656f2218dc10729d3e0f660c35817bbd69845ac96dc6279",
s: "0x2e915ae47699771108f65881273f464d70db0229ad12a94ac38746f498ea7ed3",
to: "0x66b4e7be902300f9a15d900822bbd8803be87391",
transactionIndex: 0,
v: "0x1c",
value: 10000000000000000000
}
It was taken in safely.
SendTransaction.java
public TransactionReceipt sendSignedTransaction(Credentials credentials, String password, String toAddress) {
TransactionReceipt receipt = null;
try {
//Transaction generation
// "personal_unlockAccount"Send a request and receive a response
PersonalUnlockAccount unlockAccountResponse = web3j.personalUnlockAccount(
credentials.getAddress(), //address
password //password
).send();
//If the unlock is successful, send Ether
if (unlockAccountResponse.getResult()) {
// "eth_sendTransaction"Create an object to pass to the argument of
RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
BigInteger.valueOf(10), // nonce
BigInteger.valueOf(700), // gasPrice
BigInteger.valueOf(4712388), // gasLimit
toAddress, // to
BigInteger.valueOf(101) // value
);
//Sign transaction
byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
String hexValue = Numeric.toHexString(signedMessage);
//Send transaction
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
String response = ethSendTransaction.getRawResponse();
String transactionHash = ethSendTransaction.getTransactionHash();
Optional<TransactionReceipt> transactionReceipt = null;
int retry = 0;
//Transaction monitoring
if(transactionHash != null) {
do {
System.out.printf("%3d checking if transaction " + transactionHash + " is mined....\n" ,retry);
EthGetTransactionReceipt ethGetTransactionReceiptResp =
web3j.ethGetTransactionReceipt(transactionHash).send();
transactionReceipt = ethGetTransactionReceiptResp.getTransactionReceipt();
Thread.sleep(3000);
retry++;
}while(!transactionReceipt.isPresent() && retry < 100);
} else {
System.out.println("Transaction Send failed...");
System.out.println("Message:" + ethSendTransaction.getError().getMessage());
System.out.println("Data :" + ethSendTransaction.getError().getData());
}
}
}catch(IOException | InterruptedException ex) {
ex.printStackTrace();
}catch(Exception ex) {
ex.printStackTrace();
}
return receipt;
}
}
What you are doing
It will be the flow. Transaction monitoring checks to see if the hash value of the specified transaction has been populated into the block.
The transaction has been created.
INFO [09-15|23:29:23.752] Submitted transaction fullhash=0x533ff5d2635284d01d8e85015da00d8962de8b98bf5efaa6d9ceca3200243a88 recipient=0x66B4e7bE902300F9a15D900822Bbd8803Be87391
> eth.getTransaction("0x533ff5d2635284d01d8e85015da00d8962de8b98bf5efaa6d9ceca3200243a88")
{
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
blockNumber: null,
from: "0x945cd603a6754cb13c3d61d8fe240990f86f9f8a",
gas: 4712388,
gasPrice: 800,
hash: "0x533ff5d2635284d01d8e85015da00d8962de8b98bf5efaa6d9ceca3200243a88",
input: "0x",
nonce: 10,
r: "0xc8022528f46078b3e9a8f2a3174d147b85c23af6794fc8b07d58651931b7556f",
s: "0x1ab35d15cf3afa6990bc6e96a593e6c2cba6432f769d0de919c770cec4a3f2c7",
to: "0x66b4e7be902300f9a15d900822bbd8803be87391",
transactionIndex: 0,
v: "0x1b",
value: 101
}
However, this transaction is not included in the block no matter how much mining is done, because my understanding is not catching up. Is there something wrong with the value specified for the transaction? Or is it not included in the block without validating the signed transaction?
I will continue to study this area.
I was just learning by vaguely inputting the contents written in the book, but when I investigated how to use it in Java, which is my best language, the points that had been pointed up until now became more and more connected.
Since there is still little information available, I would like to output what I have learned positively.
I mentioned that the signed transaction is not included in the block, but I found the solution (I asked my senior and found it in 1 minute), so I wrote it in the following article.