diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..f5c287c54 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +## How to contribute to web3j + +Thank you for reading this! If you'd like to report a bug or join in the buidl-ing of web3j, then here are some notes on how to do that. + +#### **Did you find a bug?** + +* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/web3j/web3j/issues). If there is then please add any more information that you have, or give it a :+1:. This will help us prioritize work. + +* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/web3j/web3j/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or event better an **executable test case** demonstrating the expected behavior that is not occurring. + +#### **Did you write a patch that fixes a bug?** + +* Open a new pull request with the patch. + +* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. + +#### **Do you intend to add a new feature or change an existing one?** + +* Suggest your change in the [gitter channel](https://gitter.im/web3j/web3j) then start writing code. + +* Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes. + +#### **Do you have questions about the source code?** + +* Ask any question about how to use web3j should be directed to the [gitter channel](https://gitter.im/web3j/web3j) + +## :heart: BUIDL :heart: + +team web3j diff --git a/core/src/main/java/org/web3j/crypto/WalletUtils.java b/core/src/main/java/org/web3j/crypto/WalletUtils.java index ad5cd64f3..35c27707c 100644 --- a/core/src/main/java/org/web3j/crypto/WalletUtils.java +++ b/core/src/main/java/org/web3j/crypto/WalletUtils.java @@ -108,6 +108,27 @@ public static Bip39Wallet generateBip39Wallet(String password, File destinationD return new Bip39Wallet(walletFile, mnemonic); } + /** + * Generates a BIP-39 compatible Ethereum wallet using a mnemonic passed as argument. + * + * @param password Will be used for both wallet encryption and passphrase for BIP-39 seed + * @param mnemonic The mnemonic that will be used to generate the seed + * @param destinationDirectory The directory containing the wallet + * @return A BIP-39 compatible Ethereum wallet + * @throws CipherException if the underlying cipher is not available + * @throws IOException if the destination cannot be written to + */ + public static Bip39Wallet generateBip39WalletFromMnemonic( + String password, String mnemonic, File destinationDirectory) + throws CipherException, IOException { + byte[] seed = MnemonicUtils.generateSeed(mnemonic, password); + ECKeyPair privateKey = ECKeyPair.create(sha256(seed)); + + String walletFile = generateWalletFile(password, privateKey, destinationDirectory, false); + + return new Bip39Wallet(walletFile, mnemonic); + } + public static Credentials loadCredentials(String password, String source) throws IOException, CipherException { return loadCredentials(password, new File(source)); diff --git a/core/src/main/java/org/web3j/ens/Contracts.java b/core/src/main/java/org/web3j/ens/Contracts.java index 22eeb3f92..26bb9fd55 100644 --- a/core/src/main/java/org/web3j/ens/Contracts.java +++ b/core/src/main/java/org/web3j/ens/Contracts.java @@ -1,6 +1,6 @@ package org.web3j.ens; -import org.web3j.tx.ChainId; +import org.web3j.tx.ChainIdLong; /** * ENS registry contract addresses. @@ -12,16 +12,16 @@ public class Contracts { public static final String RINKEBY = "0xe7410170f87102df0055eb195163a03b7f2bff4a"; public static String resolveRegistryContract(String chainId) { - switch (Byte.valueOf(chainId)) { - case ChainId.MAINNET: - return MAINNET; - case ChainId.ROPSTEN: - return ROPSTEN; - case ChainId.RINKEBY: - return RINKEBY; - default: - throw new EnsResolutionException( - "Unable to resolve ENS registry contract for network id: " + chainId); + final Long chainIdLong = Long.parseLong(chainId); + if (chainIdLong.equals(ChainIdLong.MAINNET)) { + return MAINNET; + } else if (chainIdLong.equals(ChainIdLong.ROPSTEN)) { + return ROPSTEN; + } else if (chainIdLong.equals(ChainIdLong.RINKEBY)) { + return RINKEBY; + } else { + throw new EnsResolutionException( + "Unable to resolve ENS registry contract for network id: " + chainId); } } } diff --git a/core/src/main/java/org/web3j/ens/EnsResolver.java b/core/src/main/java/org/web3j/ens/EnsResolver.java index 0defe36cc..26affc37f 100644 --- a/core/src/main/java/org/web3j/ens/EnsResolver.java +++ b/core/src/main/java/org/web3j/ens/EnsResolver.java @@ -9,7 +9,6 @@ import org.web3j.protocol.core.methods.response.EthSyncing; import org.web3j.protocol.core.methods.response.NetVersion; import org.web3j.tx.ClientTransactionManager; -import org.web3j.tx.ManagedTransaction; import org.web3j.tx.TransactionManager; import org.web3j.tx.gas.DefaultGasProvider; import org.web3j.utils.Numeric; diff --git a/core/src/main/java/org/web3j/tx/ChainId.java b/core/src/main/java/org/web3j/tx/ChainId.java index cd2153adf..7df1f5229 100644 --- a/core/src/main/java/org/web3j/tx/ChainId.java +++ b/core/src/main/java/org/web3j/tx/ChainId.java @@ -4,6 +4,7 @@ * Ethereum chain ids as per * EIP-155. */ +@Deprecated public class ChainId { public static final byte NONE = -1; public static final byte MAINNET = 1; @@ -16,3 +17,4 @@ public class ChainId { public static final byte ETHEREUM_CLASSIC_MAINNET = 61; public static final byte ETHEREUM_CLASSIC_TESTNET = 62; } + diff --git a/core/src/main/java/org/web3j/tx/ChainIdLong.java b/core/src/main/java/org/web3j/tx/ChainIdLong.java new file mode 100644 index 000000000..8df588241 --- /dev/null +++ b/core/src/main/java/org/web3j/tx/ChainIdLong.java @@ -0,0 +1,14 @@ +package org.web3j.tx; + +public class ChainIdLong { + public static final long NONE = -1; + public static final long MAINNET = 1; + public static final long EXPANSE_MAINNET = 2; + public static final long ROPSTEN = 3; + public static final long RINKEBY = 4; + public static final long ROOTSTOCK_MAINNET = 30; + public static final long ROOTSTOCK_TESTNET = 31; + public static final long KOVAN = 42; + public static final long ETHEREUM_CLASSIC_MAINNET = 61; + public static final long ETHEREUM_CLASSIC_TESTNET = 62; +} diff --git a/core/src/main/java/org/web3j/tx/RawTransactionManager.java b/core/src/main/java/org/web3j/tx/RawTransactionManager.java index 7b2c190f6..126e47da9 100644 --- a/core/src/main/java/org/web3j/tx/RawTransactionManager.java +++ b/core/src/main/java/org/web3j/tx/RawTransactionManager.java @@ -29,11 +29,11 @@ public class RawTransactionManager extends TransactionManager { private final Web3j web3j; final Credentials credentials; - private final byte chainId; + private final long chainId; protected TxHashVerifier txHashVerifier = new TxHashVerifier(); - public RawTransactionManager(Web3j web3j, Credentials credentials, byte chainId) { + public RawTransactionManager(Web3j web3j, Credentials credentials, long chainId) { super(web3j, credentials.getAddress()); this.web3j = web3j; @@ -43,7 +43,7 @@ public RawTransactionManager(Web3j web3j, Credentials credentials, byte chainId) } public RawTransactionManager( - Web3j web3j, Credentials credentials, byte chainId, + Web3j web3j, Credentials credentials, long chainId, TransactionReceiptProcessor transactionReceiptProcessor) { super(transactionReceiptProcessor, credentials.getAddress()); @@ -54,7 +54,7 @@ public RawTransactionManager( } public RawTransactionManager( - Web3j web3j, Credentials credentials, byte chainId, int attempts, long sleepDuration) { + Web3j web3j, Credentials credentials, long chainId, int attempts, long sleepDuration) { super(web3j, attempts, sleepDuration, credentials.getAddress()); this.web3j = web3j; diff --git a/core/src/test/java/org/web3j/crypto/WalletUtilsTest.java b/core/src/test/java/org/web3j/crypto/WalletUtilsTest.java index c57725561..5cfad5699 100644 --- a/core/src/test/java/org/web3j/crypto/WalletUtilsTest.java +++ b/core/src/test/java/org/web3j/crypto/WalletUtilsTest.java @@ -18,6 +18,7 @@ import static org.web3j.crypto.Hash.sha256; import static org.web3j.crypto.SampleKeys.CREDENTIALS; import static org.web3j.crypto.SampleKeys.KEY_PAIR; +import static org.web3j.crypto.SampleKeys.MNEMONIC; import static org.web3j.crypto.SampleKeys.PASSWORD; import static org.web3j.crypto.WalletUtils.isValidAddress; import static org.web3j.crypto.WalletUtils.isValidPrivateKey; @@ -48,6 +49,16 @@ public void testGenerateBip39Wallets() throws Exception { assertEquals(credentials, WalletUtils.loadBip39Credentials(PASSWORD, wallet.getMnemonic())); } + @Test + public void testGenerateBip39WalletFromMnemonic() throws Exception { + Bip39Wallet wallet = WalletUtils.generateBip39WalletFromMnemonic( + PASSWORD, MNEMONIC, tempDir); + byte[] seed = MnemonicUtils.generateSeed(wallet.getMnemonic(), PASSWORD); + Credentials credentials = Credentials.create(ECKeyPair.create(sha256(seed))); + + assertEquals(credentials, WalletUtils.loadBip39Credentials(PASSWORD, wallet.getMnemonic())); + } + @Test public void testGenerateFullNewWalletFile() throws Exception { String fileName = WalletUtils.generateFullNewWalletFile(PASSWORD, tempDir); diff --git a/core/src/test/java/org/web3j/ens/EnsResolverTest.java b/core/src/test/java/org/web3j/ens/EnsResolverTest.java index ed1894d29..571cb276a 100644 --- a/core/src/test/java/org/web3j/ens/EnsResolverTest.java +++ b/core/src/test/java/org/web3j/ens/EnsResolverTest.java @@ -6,19 +6,16 @@ import org.junit.Before; import org.junit.Test; -import org.web3j.abi.TypeDecoder; import org.web3j.abi.TypeEncoder; import org.web3j.abi.datatypes.Utf8String; import org.web3j.protocol.Web3j; import org.web3j.protocol.Web3jService; -import org.web3j.protocol.core.JsonRpc2_0Web3j; import org.web3j.protocol.core.Request; import org.web3j.protocol.core.methods.response.EthBlock; import org.web3j.protocol.core.methods.response.EthCall; import org.web3j.protocol.core.methods.response.EthSyncing; import org.web3j.protocol.core.methods.response.NetVersion; -import org.web3j.protocol.http.HttpService; -import org.web3j.tx.ChainId; +import org.web3j.tx.ChainIdLong; import org.web3j.utils.Numeric; import static org.hamcrest.CoreMatchers.is; @@ -51,7 +48,7 @@ public void testResolve() throws Exception { configureLatestBlock(System.currentTimeMillis() / 1000); // block timestamp is in seconds NetVersion netVersion = new NetVersion(); - netVersion.setResult(Byte.toString(ChainId.MAINNET)); + netVersion.setResult(Long.toString(ChainIdLong.MAINNET)); String resolverAddress = "0x0000000000000000000000004c641fb9bad9b60ef180c31f56051ce826d21a9a"; @@ -81,7 +78,7 @@ public void testReverseResolve() throws Exception { configureLatestBlock(System.currentTimeMillis() / 1000); // block timestamp is in seconds NetVersion netVersion = new NetVersion(); - netVersion.setResult(Byte.toString(ChainId.MAINNET)); + netVersion.setResult(Long.toString(ChainIdLong.MAINNET)); String resolverAddress = "0x0000000000000000000000004c641fb9bad9b60ef180c31f56051ce826d21a9a"; diff --git a/crypto/src/main/java/org/web3j/crypto/Sign.java b/crypto/src/main/java/org/web3j/crypto/Sign.java index 4df6ab85b..1c5582097 100644 --- a/crypto/src/main/java/org/web3j/crypto/Sign.java +++ b/crypto/src/main/java/org/web3j/crypto/Sign.java @@ -82,7 +82,7 @@ public static SignatureData signMessage(byte[] message, ECKeyPair keyPair, boole int headerByte = recId + 27; // 1 header + 32 bytes for R + 32 bytes for S - byte v = (byte) headerByte; + byte[] v = new byte[]{(byte) headerByte}; byte[] r = Numeric.toBytesPadded(sig.r, 32); byte[] s = Numeric.toBytesPadded(sig.s, 32); @@ -217,7 +217,7 @@ static BigInteger signedMessageHashToKey( verifyPrecondition(r != null && r.length == 32, "r must be 32 bytes"); verifyPrecondition(s != null && s.length == 32, "s must be 32 bytes"); - int header = signatureData.getV() & 0xFF; + int header = signatureData.getV()[0] & 0xFF; // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, // 0x1D = second key with even y, 0x1E = second key with odd y if (header < 27 || header > 34) { @@ -277,17 +277,21 @@ public static BigInteger publicFromPoint(byte[] bits) { } public static class SignatureData { - private final byte v; + private final byte[] v; private final byte[] r; private final byte[] s; public SignatureData(byte v, byte[] r, byte[] s) { + this(new byte[]{v}, r, s); + } + + public SignatureData(byte[] v, byte[] r, byte[] s) { this.v = v; this.r = r; this.s = s; } - public byte getV() { + public byte[] getV() { return v; } @@ -310,7 +314,7 @@ public boolean equals(Object o) { SignatureData that = (SignatureData) o; - if (v != that.v) { + if (!Arrays.equals(v, that.v)) { return false; } if (!Arrays.equals(r, that.r)) { @@ -321,7 +325,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = (int) v; + int result = Arrays.hashCode(v); result = 31 * result + Arrays.hashCode(r); result = 31 * result + Arrays.hashCode(s); return result; diff --git a/crypto/src/main/java/org/web3j/crypto/SignedRawTransaction.java b/crypto/src/main/java/org/web3j/crypto/SignedRawTransaction.java index 472b71fd9..8ae198ee1 100644 --- a/crypto/src/main/java/org/web3j/crypto/SignedRawTransaction.java +++ b/crypto/src/main/java/org/web3j/crypto/SignedRawTransaction.java @@ -3,12 +3,14 @@ import java.math.BigInteger; import java.security.SignatureException; +import org.web3j.utils.Numeric; + 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; + private final Sign.SignatureData signatureData; public SignedRawTransaction(BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String to, BigInteger value, String data, @@ -29,7 +31,7 @@ public String getFrom() throws SignatureException { } else { encodedTransaction = TransactionEncoder.encode(this, chainId.byteValue()); } - byte v = signatureData.getV(); + BigInteger v = Numeric.toBigInt(signatureData.getV()); byte[] r = signatureData.getR(); byte[] s = signatureData.getS(); Sign.SignatureData signatureDataV = new Sign.SignatureData(getRealV(v), r, s); @@ -44,9 +46,10 @@ public void verify(String from) throws SignatureException { } } - private byte getRealV(byte v) { + private byte getRealV(BigInteger bv) { + long v = bv.longValue(); if (v == LOWER_REAL_V || v == (LOWER_REAL_V + 1)) { - return v; + return (byte) v; } byte realV = LOWER_REAL_V; int inc = 0; @@ -57,11 +60,12 @@ private byte getRealV(byte v) { } public Integer getChainId() { - byte v = signatureData.getV(); + BigInteger bv = Numeric.toBigInt(signatureData.getV()); + long v = bv.longValue(); if (v == LOWER_REAL_V || v == (LOWER_REAL_V + 1)) { return null; } - Integer chainId = (v - CHAIN_ID_INC) / 2; + Integer chainId = (int)((v - CHAIN_ID_INC) / 2); return chainId; } } diff --git a/crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java b/crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java index 75ccdc4ea..4716d0802 100644 --- a/crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java +++ b/crypto/src/main/java/org/web3j/crypto/TransactionDecoder.java @@ -20,7 +20,7 @@ public static RawTransaction decode(String hexTransaction) { BigInteger value = ((RlpString) values.getValues().get(4)).asPositiveBigInteger(); String data = ((RlpString) values.getValues().get(5)).asString(); if (values.getValues().size() > 6) { - byte v = ((RlpString) values.getValues().get(6)).getBytes()[0]; + byte[] v = ((RlpString) values.getValues().get(6)).getBytes(); byte[] r = Numeric.toBytesPadded( Numeric.toBigInt(((RlpString) values.getValues().get(7)).getBytes()), 32); byte[] s = Numeric.toBytesPadded( @@ -33,5 +33,4 @@ public static RawTransaction decode(String hexTransaction) { gasPrice, gasLimit, to, value, data); } } - } diff --git a/crypto/src/main/java/org/web3j/crypto/TransactionEncoder.java b/crypto/src/main/java/org/web3j/crypto/TransactionEncoder.java index 6a5a1eedf..454e9bb8b 100644 --- a/crypto/src/main/java/org/web3j/crypto/TransactionEncoder.java +++ b/crypto/src/main/java/org/web3j/crypto/TransactionEncoder.java @@ -1,5 +1,7 @@ package org.web3j.crypto; +import java.math.BigInteger; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -16,6 +18,9 @@ */ public class TransactionEncoder { + private static final int CHAIN_ID_INC = 35; + private static final int LOWER_REAL_V = 27; + public static byte[] signMessage(RawTransaction rawTransaction, Credentials credentials) { byte[] encodedTransaction = encode(rawTransaction); Sign.SignatureData signatureData = Sign.signMessage( @@ -25,7 +30,7 @@ public static byte[] signMessage(RawTransaction rawTransaction, Credentials cred } public static byte[] signMessage( - RawTransaction rawTransaction, byte chainId, Credentials credentials) { + RawTransaction rawTransaction, long chainId, Credentials credentials) { byte[] encodedTransaction = encode(rawTransaction, chainId); Sign.SignatureData signatureData = Sign.signMessage( encodedTransaction, credentials.getEcKeyPair()); @@ -34,30 +39,56 @@ public static byte[] signMessage( return encode(rawTransaction, eip155SignatureData); } + @Deprecated + public static byte[] signMessage( + RawTransaction rawTransaction, byte chainId, Credentials credentials) { + return signMessage(rawTransaction, (long) chainId, credentials); + } + public static Sign.SignatureData createEip155SignatureData( - Sign.SignatureData signatureData, byte chainId) { - byte v = (byte) (signatureData.getV() + (chainId << 1) + 8); + Sign.SignatureData signatureData, long chainId) { + BigInteger v = Numeric.toBigInt(signatureData.getV()); + v = v.subtract(BigInteger.valueOf(LOWER_REAL_V)); + v = v.add(BigInteger.valueOf(chainId * 2)); + v = v.add(BigInteger.valueOf(CHAIN_ID_INC)); return new Sign.SignatureData( - v, signatureData.getR(), signatureData.getS()); + v.toByteArray(), signatureData.getR(), signatureData.getS()); + } + + @Deprecated + public static Sign.SignatureData createEip155SignatureData( + Sign.SignatureData signatureData, byte chainId) { + return createEip155SignatureData(signatureData, (long) chainId); } public static byte[] encode(RawTransaction rawTransaction) { return encode(rawTransaction, null); } - public static byte[] encode(RawTransaction rawTransaction, byte chainId) { + public static byte[] encode(RawTransaction rawTransaction, long chainId) { Sign.SignatureData signatureData = new Sign.SignatureData( - chainId, new byte[] {}, new byte[] {}); + longToBytes(chainId), new byte[] {}, new byte[] {}); return encode(rawTransaction, signatureData); } + @Deprecated + public static byte[] encode(RawTransaction rawTransaction, byte chainId) { + return encode(rawTransaction, (long) chainId); + } + private static byte[] encode(RawTransaction rawTransaction, Sign.SignatureData signatureData) { List values = asRlpValues(rawTransaction, signatureData); RlpList rlpList = new RlpList(values); return RlpEncoder.encode(rlpList); } + private static byte[] longToBytes(long x) { + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); + buffer.putLong(x); + return buffer.array(); + } + static List asRlpValues( RawTransaction rawTransaction, Sign.SignatureData signatureData) { List result = new ArrayList<>(); @@ -83,7 +114,7 @@ static List asRlpValues( result.add(RlpString.create(data)); if (signatureData != null) { - result.add(RlpString.create(signatureData.getV())); + result.add(RlpString.create(Bytes.trimLeadingZeroes(signatureData.getV()))); result.add(RlpString.create(Bytes.trimLeadingZeroes(signatureData.getR()))); result.add(RlpString.create(Bytes.trimLeadingZeroes(signatureData.getS()))); } diff --git a/crypto/src/test/java/org/web3j/crypto/SampleKeys.java b/crypto/src/test/java/org/web3j/crypto/SampleKeys.java index 4a8ca29f1..d13d359cb 100644 --- a/crypto/src/test/java/org/web3j/crypto/SampleKeys.java +++ b/crypto/src/test/java/org/web3j/crypto/SampleKeys.java @@ -18,6 +18,8 @@ public class SampleKeys { public static final String ADDRESS_NO_PREFIX = Numeric.cleanHexPrefix(ADDRESS); public static final String PASSWORD = "Insecure Pa55w0rd"; + public static final String MNEMONIC = "scatter major grant return flee easy female jungle" + + " vivid movie bicycle absent weather inspire carry"; static final BigInteger PRIVATE_KEY = Numeric.toBigInt(PRIVATE_KEY_STRING); static final BigInteger PUBLIC_KEY = Numeric.toBigInt(PUBLIC_KEY_STRING); diff --git a/crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java b/crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java index 7e0414cc4..fd6c35e20 100644 --- a/crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java +++ b/crypto/src/test/java/org/web3j/crypto/TransactionDecoderTest.java @@ -75,7 +75,7 @@ public void testDecodingSignedChainId() throws Exception { BigInteger gasLimit = BigInteger.TEN; String to = "0x0add5355"; BigInteger value = BigInteger.valueOf(Long.MAX_VALUE); - Integer chainId = 1; + Integer chainId = 46; RawTransaction rawTransaction = RawTransaction.createEtherTransaction( nonce, gasPrice, gasLimit, to, value); byte[] signedMessage = TransactionEncoder.signMessage( diff --git a/gradle.properties b/gradle.properties index f7aeea0c0..c62d94add 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ group=org.web3j -version=4.2.0 +version=4.3.0-SNAPSHOT