forked from hyperledger-web3j/web3j
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request hyperledger-web3j#491 from e-Contract/transaction-…
…decoder Transaction decoder
- Loading branch information
Showing
6 changed files
with
235 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
crypto/src/main/java/org/web3j/crypto/SignedRawTransaction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package org.web3j.crypto; | ||
|
||
import java.math.BigInteger; | ||
import java.security.SignatureException; | ||
|
||
public class SignedRawTransaction extends RawTransaction { | ||
|
||
private static final int CHAIN_ID_INC = 35; | ||
private static final int LOWER_REAL_V = 27; | ||
|
||
private Sign.SignatureData signatureData; | ||
|
||
public SignedRawTransaction(BigInteger nonce, BigInteger gasPrice, | ||
BigInteger gasLimit, String to, BigInteger value, String data, | ||
Sign.SignatureData signatureData) { | ||
super(nonce, gasPrice, gasLimit, to, value, data); | ||
this.signatureData = signatureData; | ||
} | ||
|
||
public Sign.SignatureData getSignatureData() { | ||
return signatureData; | ||
} | ||
|
||
public String getFrom() throws SignatureException { | ||
Integer chainId = getChainId(); | ||
byte[] encodedTransaction; | ||
if (null == chainId) { | ||
encodedTransaction = TransactionEncoder.encode(this); | ||
} else { | ||
encodedTransaction = TransactionEncoder.encode(this, chainId.byteValue()); | ||
} | ||
byte v = signatureData.getV(); | ||
byte[] r = signatureData.getR(); | ||
byte[] s = signatureData.getS(); | ||
Sign.SignatureData signatureDataV = new Sign.SignatureData(getRealV(v), r, s); | ||
BigInteger key = Sign.signedMessageToKey(encodedTransaction, signatureDataV); | ||
return "0x" + Keys.getAddress(key); | ||
} | ||
|
||
public void verify(String from) throws SignatureException { | ||
String actualFrom = getFrom(); | ||
if (!actualFrom.equals(from)) { | ||
throw new SignatureException("from mismatch"); | ||
} | ||
} | ||
|
||
private byte getRealV(byte v) { | ||
if (v == LOWER_REAL_V || v == (LOWER_REAL_V + 1)) { | ||
return v; | ||
} | ||
byte realV = LOWER_REAL_V; | ||
int inc = 0; | ||
if ((int) v % 2 == 0) { | ||
inc = 1; | ||
} | ||
return (byte) (realV + inc); | ||
} | ||
|
||
public Integer getChainId() { | ||
byte v = signatureData.getV(); | ||
if (v == LOWER_REAL_V || v == (LOWER_REAL_V + 1)) { | ||
return null; | ||
} | ||
Integer chainId = (v - CHAIN_ID_INC) / 2; | ||
return chainId; | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package org.web3j.crypto; | ||
|
||
import java.math.BigInteger; | ||
|
||
import org.web3j.rlp.RlpDecoder; | ||
import org.web3j.rlp.RlpList; | ||
import org.web3j.rlp.RlpString; | ||
import org.web3j.utils.Numeric; | ||
|
||
public class TransactionDecoder { | ||
|
||
public static RawTransaction decode(String hexTransaction) { | ||
byte[] transaction = Numeric.hexStringToByteArray(hexTransaction); | ||
RlpList rlpList = RlpDecoder.decode(transaction); | ||
RlpList values = (RlpList) rlpList.getValues().get(0); | ||
BigInteger nonce = ((RlpString) values.getValues().get(0)).asBigInteger(); | ||
BigInteger gasPrice = ((RlpString) values.getValues().get(1)).asBigInteger(); | ||
BigInteger gasLimit = ((RlpString) values.getValues().get(2)).asBigInteger(); | ||
String to = ((RlpString) values.getValues().get(3)).asString(); | ||
BigInteger value = ((RlpString) values.getValues().get(4)).asBigInteger(); | ||
String data = ((RlpString) values.getValues().get(5)).asString(); | ||
if (values.getValues().size() > 6) { | ||
byte v = ((RlpString) values.getValues().get(6)).getBytes()[0]; | ||
byte[] r = zeroPadded(((RlpString) values.getValues().get(7)).getBytes(), 32); | ||
byte[] s = zeroPadded(((RlpString) values.getValues().get(8)).getBytes(), 32); | ||
Sign.SignatureData signatureData = new Sign.SignatureData(v, r, s); | ||
return new SignedRawTransaction(nonce, gasPrice, gasLimit, | ||
to, value, data, signatureData); | ||
} else { | ||
return RawTransaction.createTransaction(nonce, | ||
gasPrice, gasLimit, to, value, data); | ||
} | ||
} | ||
|
||
private static byte[] zeroPadded(byte[] value, int size) { | ||
if (value.length == size) { | ||
return value; | ||
} | ||
int diff = size - value.length; | ||
byte[] paddedValue = new byte[size]; | ||
System.arraycopy(value, 0, paddedValue, diff, value.length); | ||
return paddedValue; | ||
} | ||
} |
109 changes: 109 additions & 0 deletions
109
crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package org.web3j.crypto; | ||
|
||
import java.math.BigInteger; | ||
|
||
import org.junit.Test; | ||
|
||
import org.web3j.utils.Numeric; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assert.assertNull; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
public class TransactionDecoderTest { | ||
|
||
@Test | ||
public void testDecoding() throws Exception { | ||
BigInteger nonce = BigInteger.ZERO; | ||
BigInteger gasPrice = BigInteger.ONE; | ||
BigInteger gasLimit = BigInteger.TEN; | ||
String to = "0x0add5355"; | ||
BigInteger value = BigInteger.valueOf(Long.MAX_VALUE); | ||
RawTransaction rawTransaction = RawTransaction.createEtherTransaction( | ||
nonce, gasPrice, gasLimit, to, value); | ||
byte[] encodedMessage = TransactionEncoder.encode(rawTransaction); | ||
String hexMessage = Numeric.toHexString(encodedMessage); | ||
|
||
RawTransaction result = TransactionDecoder.decode(hexMessage); | ||
assertNotNull(result); | ||
assertEquals(nonce, result.getNonce()); | ||
assertEquals(gasPrice, result.getGasPrice()); | ||
assertEquals(gasLimit, result.getGasLimit()); | ||
assertEquals(to, result.getTo()); | ||
assertEquals(value, result.getValue()); | ||
assertEquals("", result.getData()); | ||
} | ||
|
||
@Test | ||
public void testDecodingSigned() throws Exception { | ||
BigInteger nonce = BigInteger.ZERO; | ||
BigInteger gasPrice = BigInteger.ONE; | ||
BigInteger gasLimit = BigInteger.TEN; | ||
String to = "0x0add5355"; | ||
BigInteger value = BigInteger.valueOf(Long.MAX_VALUE); | ||
RawTransaction rawTransaction = RawTransaction.createEtherTransaction( | ||
nonce, gasPrice, gasLimit, to, value); | ||
byte[] signedMessage = TransactionEncoder.signMessage( | ||
rawTransaction, SampleKeys.CREDENTIALS); | ||
String hexMessage = Numeric.toHexString(signedMessage); | ||
|
||
RawTransaction result = TransactionDecoder.decode(hexMessage); | ||
assertNotNull(result); | ||
assertEquals(nonce, result.getNonce()); | ||
assertEquals(gasPrice, result.getGasPrice()); | ||
assertEquals(gasLimit, result.getGasLimit()); | ||
assertEquals(to, result.getTo()); | ||
assertEquals(value, result.getValue()); | ||
assertEquals("", result.getData()); | ||
assertTrue(result instanceof SignedRawTransaction); | ||
SignedRawTransaction signedResult = (SignedRawTransaction) result; | ||
assertNotNull(signedResult.getSignatureData()); | ||
Sign.SignatureData signatureData = signedResult.getSignatureData(); | ||
byte[] encodedTransaction = TransactionEncoder.encode(rawTransaction); | ||
BigInteger key = Sign.signedMessageToKey(encodedTransaction, signatureData); | ||
assertEquals(key, SampleKeys.PUBLIC_KEY); | ||
assertEquals(SampleKeys.ADDRESS, signedResult.getFrom()); | ||
signedResult.verify(SampleKeys.ADDRESS); | ||
assertNull(signedResult.getChainId()); | ||
} | ||
|
||
@Test | ||
public void testDecodingSignedChainId() throws Exception { | ||
BigInteger nonce = BigInteger.ZERO; | ||
BigInteger gasPrice = BigInteger.ONE; | ||
BigInteger gasLimit = BigInteger.TEN; | ||
String to = "0x0add5355"; | ||
BigInteger value = BigInteger.valueOf(Long.MAX_VALUE); | ||
Integer chainId = 1; | ||
RawTransaction rawTransaction = RawTransaction.createEtherTransaction( | ||
nonce, gasPrice, gasLimit, to, value); | ||
byte[] signedMessage = TransactionEncoder.signMessage( | ||
rawTransaction, chainId.byteValue(), SampleKeys.CREDENTIALS); | ||
String hexMessage = Numeric.toHexString(signedMessage); | ||
|
||
RawTransaction result = TransactionDecoder.decode(hexMessage); | ||
assertNotNull(result); | ||
assertEquals(nonce, result.getNonce()); | ||
assertEquals(gasPrice, result.getGasPrice()); | ||
assertEquals(gasLimit, result.getGasLimit()); | ||
assertEquals(to, result.getTo()); | ||
assertEquals(value, result.getValue()); | ||
assertEquals("", result.getData()); | ||
assertTrue(result instanceof SignedRawTransaction); | ||
SignedRawTransaction signedResult = (SignedRawTransaction) result; | ||
assertEquals(SampleKeys.ADDRESS, signedResult.getFrom()); | ||
signedResult.verify(SampleKeys.ADDRESS); | ||
assertEquals(chainId, signedResult.getChainId()); | ||
} | ||
|
||
@Test | ||
public void testRSize31() throws Exception { | ||
//CHECKSTYLE:OFF | ||
String hexTransaction = "0xf883370183419ce09433c98f20dd73d7bb1d533c4aa3371f2b30c6ebde80a45093dc7d00000000000000000000000000000000000000000000000000000000000000351c9fb90996c836fb34b782ee3d6efa9e2c79a75b277c014e353b51b23b00524d2da07435ebebca627a51a863bf590aff911c4746ab8386a0477c8221bb89671a5d58"; | ||
//CHECKSTYLE:ON | ||
RawTransaction result = TransactionDecoder.decode(hexTransaction); | ||
SignedRawTransaction signedResult = (SignedRawTransaction) result; | ||
assertEquals("0x1b609b03e2e9b0275a61fa5c69a8f32550285536", signedResult.getFrom()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters