Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
@ToString
public class TransactionResult {

/** Spec-compliant zero value for QUANTITY (uint) fields per Ethereum JSON-RPC. */
private static final String QUANTITY_ZERO = "0x0";

@Getter
private final String hash;
@Getter
Expand All @@ -46,7 +49,7 @@ public class TransactionResult {
@Getter
private final String input;
@Getter
private final String type = "0x0";
private final String type = QUANTITY_ZERO;

@Getter
private String v;
Expand All @@ -58,9 +61,10 @@ public class TransactionResult {
private void parseSignature(Transaction tx) {

if (tx.getSignatureCount() == 0) {
v = ByteArray.toJsonHex(new byte[1]);
r = ByteArray.toJsonHex(new byte[32]);
s = ByteArray.toJsonHex(new byte[32]);
// v, r, s are QUANTITY per Ethereum JSON-RPC spec; use QUANTITY_ZERO.
v = QUANTITY_ZERO;
r = QUANTITY_ZERO;
s = QUANTITY_ZERO;
return;
}

Expand Down Expand Up @@ -98,7 +102,7 @@ public TransactionResult(BlockCapsule blockCapsule, int index, Protocol.Transact
TransactionCapsule capsule = new TransactionCapsule(tx);
byte[] txId = capsule.getTransactionId().getBytes();
hash = ByteArray.toJsonHex(txId);
nonce = ByteArray.toJsonHex(new byte[8]); // no value
nonce = QUANTITY_ZERO; // no value
blockHash = ByteArray.toJsonHex(blockCapsule.getBlockId().getBytes());
blockNumber = ByteArray.toJsonHex(blockCapsule.getNum());
transactionIndex = ByteArray.toJsonHex(index);
Expand All @@ -119,7 +123,7 @@ public TransactionResult(BlockCapsule blockCapsule, int index, Protocol.Transact
} else {
from = ByteArray.toJsonHex(new byte[20]);
to = ByteArray.toJsonHex(new byte[20]);
value = "0x0";
value = QUANTITY_ZERO;
}

gas = ByteArray.toJsonHex(energyUsageTotal);
Expand All @@ -133,10 +137,13 @@ public TransactionResult(Transaction tx, Wallet wallet) {
TransactionCapsule capsule = new TransactionCapsule(tx);
byte[] txId = capsule.getTransactionId().getBytes();
hash = ByteArray.toJsonHex(txId);
nonce = ByteArray.toJsonHex(new byte[8]); // no value
blockHash = "0x";
blockNumber = "0x";
transactionIndex = "0x";
nonce = QUANTITY_ZERO; // no value
// Block context is unknown in this fallback path; per Ethereum JSON-RPC spec
// (TransactionInfo schema), these fields must be null when not yet associated
// with a confirmed block, not a malformed "0x".
blockHash = null;
blockNumber = null;
transactionIndex = null;

if (!tx.getRawData().getContractList().isEmpty()) {
Contract contract = tx.getRawData().getContract(0);
Expand All @@ -148,11 +155,11 @@ public TransactionResult(Transaction tx, Wallet wallet) {
} else {
from = ByteArray.toJsonHex(new byte[20]);
to = ByteArray.toJsonHex(new byte[20]);
value = "0x0";
value = QUANTITY_ZERO;
}

gas = "0x0";
gasPrice = "0x";
gas = QUANTITY_ZERO;
gasPrice = QUANTITY_ZERO;
input = parseInput(tx);

parseSignature(tx);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.tron.core.services.jsonrpc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.ByteString;
import java.util.regex.Pattern;
import javax.annotation.Resource;
import org.junit.Assert;
import org.junit.Test;
Expand All @@ -23,10 +25,22 @@ public class TransactionResultTest extends BaseTest {
private static final String OWNER_ADDRESS = "41548794500882809695a8a687866e76d4271a1abc";
private static final String CONTRACT_ADDRESS = "A0B4750E2CD76E19DCA331BF5D089B71C3C2798548";

// QUANTITY pattern from ethereum/execution-apis base-types schema (uint).
private static final Pattern QUANTITY_PATTERN =
Pattern.compile("^0x(0|[1-9a-f][0-9a-f]*)$");

private static final ObjectMapper MAPPER = new ObjectMapper();

static {
Args.setParam(new String[] {"-d", dbPath()}, TestConstants.TEST_CONF);
}

private static void assertQuantity(String value) {
Assert.assertNotNull(value);
Assert.assertTrue("not a valid QUANTITY: " + value,
QUANTITY_PATTERN.matcher(value).matches());
}

@Test
public void testBuildTransactionResultWithBlock() {
SmartContractOuterClass.TriggerSmartContract.Builder builder2 =
Expand All @@ -49,10 +63,25 @@ public void testBuildTransactionResultWithBlock() {
transactionResult.getHash());
Assert.assertEquals(transactionResult.getGasPrice(), "0x1");
Assert.assertEquals(transactionResult.getGas(), "0x64");
// Unsigned transactions still must emit spec-compliant QUANTITY values for
// nonce, v, r, s. Previously these were emitted as "0x0000000000000000" /
// "0x00" / "0x000...000", which violate the uint pattern.
Assert.assertEquals("0x0", transactionResult.getNonce());
Assert.assertEquals("0x0", transactionResult.getV());
Assert.assertEquals("0x0", transactionResult.getR());
Assert.assertEquals("0x0", transactionResult.getS());
assertQuantity(transactionResult.getNonce());
assertQuantity(transactionResult.getBlockNumber());
assertQuantity(transactionResult.getGas());
assertQuantity(transactionResult.getGasPrice());
assertQuantity(transactionResult.getValue());
assertQuantity(transactionResult.getV());
assertQuantity(transactionResult.getR());
assertQuantity(transactionResult.getS());
}

@Test
public void testBuildTransactionResult() {
public void testBuildTransactionResult() throws Exception {
SmartContractOuterClass.TriggerSmartContract.Builder builder2 =
SmartContractOuterClass.TriggerSmartContract.newBuilder()
.setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS)))
Expand All @@ -64,8 +93,34 @@ public void testBuildTransactionResult() {
new TransactionResult(transactionCapsule.getInstance(), wallet);
Assert.assertEquals("0x5691531881bc44adbc722060d85fdf29265823db8e884b0d104fcfbba253cf11",
transactionResult.getHash());
Assert.assertEquals(transactionResult.getGasPrice(), "0x");
Assert.assertEquals(transactionResult.getNonce(), "0x0000000000000000");
// Block context is unknown for this constructor path; the spec requires null
// for these fields, not the malformed "0x" placeholder used previously.
Assert.assertNull(transactionResult.getBlockHash());
Assert.assertNull(transactionResult.getBlockNumber());
Assert.assertNull(transactionResult.getTransactionIndex());
// QUANTITY fields must be spec-compliant ("0x0" instead of "0x" / leading-zero forms).
Assert.assertEquals("0x0", transactionResult.getNonce());
Assert.assertEquals("0x0", transactionResult.getGasPrice());
Assert.assertEquals("0x0", transactionResult.getV());
Assert.assertEquals("0x0", transactionResult.getR());
Assert.assertEquals("0x0", transactionResult.getS());
assertQuantity(transactionResult.getNonce());
assertQuantity(transactionResult.getGas());
assertQuantity(transactionResult.getGasPrice());
assertQuantity(transactionResult.getValue());
assertQuantity(transactionResult.getV());
assertQuantity(transactionResult.getR());
assertQuantity(transactionResult.getS());
Comment thread
waynercheung marked this conversation as resolved.
// Pin the wire-level form: nullable fields must serialize as JSON `null`
// (not be omitted), so strict Ethereum clients can distinguish "unknown
// block context" from a missing field.
String json = MAPPER.writeValueAsString(transactionResult);
Assert.assertTrue("blockHash must serialize as null literal: " + json,
json.contains("\"blockHash\":null"));
Assert.assertTrue("blockNumber must serialize as null literal: " + json,
json.contains("\"blockNumber\":null"));
Assert.assertTrue("transactionIndex must serialize as null literal: " + json,
json.contains("\"transactionIndex\":null"));
}

}
Loading