From 46b917064866033bdcb18afa3c298611b35873a6 Mon Sep 17 00:00:00 2001 From: Gaurav Goel Date: Tue, 12 Sep 2023 11:30:20 +0530 Subject: [PATCH 1/5] feat:added signTypedData signing function and eip1559 signing function, modified v of Signature data Signed-off-by: Gaurav Goel --- .../MpcProviderTests.java | 64 +++++++++++++++++-- .../EthereumTssAccount.java | 64 +++++++++++++++++-- 2 files changed, 117 insertions(+), 11 deletions(-) diff --git a/web3-android-mpc-provider/src/androidTest/java/com/web3auth/web3_android_mpc_provider/MpcProviderTests.java b/web3-android-mpc-provider/src/androidTest/java/com/web3auth/web3_android_mpc_provider/MpcProviderTests.java index 2f5647e..0d78248 100644 --- a/web3-android-mpc-provider/src/androidTest/java/com/web3auth/web3_android_mpc_provider/MpcProviderTests.java +++ b/web3-android-mpc-provider/src/androidTest/java/com/web3auth/web3_android_mpc_provider/MpcProviderTests.java @@ -1,16 +1,21 @@ package com.web3auth.web3_android_mpc_provider; -import static org.junit.Assert.assertNotNull; - import androidx.test.ext.junit.runners.AndroidJUnit4; import com.web3auth.tss_client_android.client.TSSClientError; import org.junit.Test; import org.junit.runner.RunWith; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameterName; +import org.web3j.protocol.core.methods.response.EthChainId; +import org.web3j.protocol.core.methods.response.EthGasPrice; +import org.web3j.protocol.core.methods.response.EthGetTransactionCount; +import org.web3j.protocol.http.HttpService; import java.io.IOException; import java.math.BigInteger; +import java.security.SignatureException; import java.util.concurrent.ExecutionException; @RunWith(AndroidJUnit4.class) @@ -20,7 +25,7 @@ public class MpcProviderTests { System.loadLibrary("dkls-native"); } - final String example1 = "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{{\"name\":\"wallet\",\"type\":\"address\"}]],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"Ether Mail\",\"version\":\"1\",\"chainId\":1,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Account\",\"wallet\":\"0x048975d4997d7578a3419851639c10318db430b6\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\"},\"contents\":\"Hello, Bob!\"}}"; + final String example1 = "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"EtherMail\",\"version\":\"1\",\"chainId\":1,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Account\",\"wallet\":\"0x048975d4997d7578a3419851639c10318db430b6\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\"},\"contents\":\"Hello,Bob!\"}}"; final String fullAddress = "04238569d5e12caf57d34fb5b2a0679c7775b5f61fd18cd69db9cc600a651749c3ec13a9367380b7a024a67f5e663f3afd40175c3223da63f6024b05d0bd9f292e"; final String factorKey = "3b4af35bc4838471f94825f34c4f649904a258c0907d348bed653eb0c94ec6c0"; @@ -58,12 +63,59 @@ public void testSigningMessage() throws TSSClientError, CustomSigningError { } @Test - public void testSigningTransaction() throws TSSClientError, CustomSigningError, IOException, ExecutionException, InterruptedException { + public void testSignTypedData() throws TSSClientError, IOException, CustomSigningError { + EthTssAccountParams params = new EthTssAccountParams( + fullAddress, factorKey, tssNonce, tssShare, tssIndex, selected_tag, verifier, verifierId, + nodeIndexs, tssEndpoints, sigs); + + EthereumTssAccount account = new EthereumTssAccount(params); + + account.signTypedData(example1); + } + + @Test + public void testSigningLegacyTransaction() throws TSSClientError, CustomSigningError, ExecutionException, InterruptedException, IOException { + EthTssAccountParams params = new EthTssAccountParams( + fullAddress, factorKey, tssNonce, tssShare, tssIndex, selected_tag, verifier, verifierId, + nodeIndexs, tssEndpoints, sigs); + EthereumTssAccount account = new EthereumTssAccount(params); + // setup Web3j + String url = "https://rpc.ankr.com/eth_goerli"; + Web3j web3j = Web3j.build(new HttpService(url)); + EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount( + account.evmAddress, + DefaultBlockParameterName.LATEST + ).send(); + BigInteger nonce = ethGetTransactionCount.getTransactionCount(); + BigInteger gasLimit = BigInteger.valueOf(21000); + EthChainId chainIdResponse = web3j.ethChainId().sendAsync().get(); + BigInteger chainId = chainIdResponse.getChainId(); + + String toAddress = "0xE09543f1974732F5D6ad442dDf176D9FA54a5Be0"; + account.signLegacyTransaction(chainId, toAddress, 0.001, null, nonce, gasLimit); + } + + @Test + public void testSigningTransaction() throws TSSClientError, CustomSigningError, SignatureException, ExecutionException, InterruptedException, IOException { EthTssAccountParams params = new EthTssAccountParams( fullAddress, factorKey, tssNonce, tssShare, tssIndex, selected_tag, verifier, verifierId, nodeIndexs, tssEndpoints, sigs); EthereumTssAccount account = new EthereumTssAccount(params); - String toAddress = "0x048975d4997D7578A3419851639c10318db430b6"; - account.signTransaction(new BigInteger("5"), toAddress, 0.001, null, new BigInteger("0"), new BigInteger("21000" ), new BigInteger("21000"), new BigInteger("21000")); + // setup Web3j + String url = "https://rpc.ankr.com/eth_goerli"; + Web3j web3j = Web3j.build(new HttpService(url)); + EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount( + account.evmAddress, + DefaultBlockParameterName.LATEST + ).send(); + BigInteger nonce = ethGetTransactionCount.getTransactionCount(); + BigInteger gasLimit = BigInteger.valueOf(21000); + EthChainId chainIdResponse = web3j.ethChainId().sendAsync().get(); + BigInteger chainId = chainIdResponse.getChainId(); + EthGasPrice gasPriceResponse = web3j.ethGasPrice().send(); + BigInteger gasPrice = gasPriceResponse.getGasPrice(); + + String toAddress = "0xE09543f1974732F5D6ad442dDf176D9FA54a5Be0"; + account.signTransaction(chainId, toAddress, 0.001, null, nonce, gasLimit, gasPrice, gasPrice); } } diff --git a/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java b/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java index f0bf30c..70ef8ec 100644 --- a/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java +++ b/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java @@ -19,6 +19,7 @@ import org.web3j.crypto.Keys; import org.web3j.crypto.RawTransaction; import org.web3j.crypto.Sign; +import org.web3j.crypto.StructuredDataEncoder; import org.web3j.crypto.TransactionEncoder; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.methods.response.EthSendTransaction; @@ -27,6 +28,7 @@ import java.io.IOException; import java.math.BigInteger; +import java.security.SignatureException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -59,11 +61,20 @@ public String signMessage(String message) throws TSSClientError, CustomSigningEr return TSSHelpers.hexSignature(signatureResult.getFirst(), signatureResult.getSecond(), v); } - //todo: Method for signing eip712 structured data + public String signTypedData(String jsonData) throws IOException, TSSClientError, CustomSigningError { + StructuredDataEncoder dataEncoder = new StructuredDataEncoder(jsonData); + byte[] hashStructuredData = dataEncoder.hashStructuredData(); + String structuredData = android.util.Base64.encodeToString(hashStructuredData, Base64.NO_WRAP); + Triple signatureResult = sign(structuredData); + Byte v = signatureResult.getThird(); + if (v < 27) { + v = (byte) (v + 27); + } + return TSSHelpers.hexSignature(signatureResult.getFirst(), signatureResult.getSecond(), v); + } - //todo Method for signing eip1559 transactions, note that v follows a formula here and is not just modified with 27 - public String signTransaction(BigInteger chainID, String toAddress, Double amount, @Nullable String data, BigInteger nonce, BigInteger gasLimit, BigInteger maxPriorityFeePerGas, BigInteger maxFeePerGas) throws TSSClientError, CustomSigningError { + public String signLegacyTransaction(BigInteger chainID, String toAddress, Double amount, @Nullable String data, BigInteger nonce, BigInteger gasLimit) throws TSSClientError, CustomSigningError { BigInteger value = Convert.toWei(Double.toString(amount), Convert.Unit.ETHER).toBigInteger(); // todo: this appears to be a bug in web3j, if data is null it throws but is marked as nullable @@ -72,6 +83,42 @@ public String signTransaction(BigInteger chainID, String toAddress, Double amoun txData = data; } + RawTransaction rawTransaction = RawTransaction.createTransaction( + chainID, + nonce, + gasLimit, + toAddress, + value, + txData + ); + + byte[] encodedTransaction = TransactionEncoder.encode(rawTransaction); + String encodedTransactionString = Base64.encodeToString(Hash.sha3(encodedTransaction), Base64.NO_WRAP); + + Triple signatureResult = sign(encodedTransactionString); + + Byte v = signatureResult.getThird(); + if (v < 27) { + v = (byte) ((chainID.byteValue() * 2) + (v + 35)); + } + + Sign.SignatureData signatureData = new Sign.SignatureData(v, + signatureResult.getSecond().toByteArray(), + signatureResult.getFirst().toByteArray()); + + byte[] signedMsg = TransactionEncoder.encode(rawTransaction, signatureData); + + return Numeric.toHexString(signedMsg); + } + + public String signTransaction(BigInteger chainID, String toAddress, Double amount, @Nullable String data, BigInteger nonce, BigInteger gasLimit, BigInteger maxPriorityFeePerGas, BigInteger maxFeePerGas) throws TSSClientError, CustomSigningError, SignatureException { + BigInteger value = Convert.toWei(Double.toString(amount), Convert.Unit.ETHER).toBigInteger(); + + String txData = ""; + if (data != null) { + txData = data; + } + RawTransaction rawTransaction = RawTransaction.createTransaction( chainID.longValue(), nonce, @@ -88,12 +135,18 @@ public String signTransaction(BigInteger chainID, String toAddress, Double amoun Triple signatureResult = sign(encodedTransactionString); - Sign.SignatureData signatureData = new Sign.SignatureData((byte) (signatureResult.getThird() + 27), + Byte v = signatureResult.getThird(); + if (v < 27) { + v = (byte) ((chainID.byteValue() * 2) + (v + 35)); + } + + Sign.SignatureData signatureData = new Sign.SignatureData(v, signatureResult.getSecond().toByteArray(), signatureResult.getFirst().toByteArray()); + byte[] signedMsg = TransactionEncoder.encode(rawTransaction, signatureData); - return Numeric.toHexString(signedMsg); + return Numeric.toHexString(signedMsg); } public void sendTransaction(Web3j web3j, String signedTx) throws IOException, CustomSigningError { @@ -176,6 +229,7 @@ private Pair> bootstrapTssClient(EthTssAccountPar return new Pair<>(client, coeffs); } + private EndpointsData generateEndpoints(int parties, int clientIndex, List tssEndpoints) { List endpoints = new ArrayList<>(); List tssWSEndpoints = new ArrayList<>(); From effbc1fb2a0b01e3181ce655a414524bd4dec918 Mon Sep 17 00:00:00 2001 From: Gaurav Goel Date: Tue, 12 Sep 2023 12:46:28 +0530 Subject: [PATCH 2/5] feat: added jitpack release settings in build.gradle Signed-off-by: Gaurav Goel --- web3-android-mpc-provider/build.gradle | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/web3-android-mpc-provider/build.gradle b/web3-android-mpc-provider/build.gradle index ca4c401..80588d8 100644 --- a/web3-android-mpc-provider/build.gradle +++ b/web3-android-mpc-provider/build.gradle @@ -1,5 +1,6 @@ plugins { id 'com.android.library' + id 'maven-publish' } android { @@ -34,7 +35,32 @@ dependencies { implementation 'com.github.grvgoel81:tss-client-android:0.0.8' //Web3j implementation 'org.web3j:core:4.9.5' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} + +task javadoc(type: Javadoc) { + failOnError(false) + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + exclude '*R.java' + exclude { + it.file.path.contains('web3-android-mpc-provider') + } +} + +afterEvaluate { + javadoc.classpath += files(android.libraryVariants.collect { variant -> + variant.javaCompileProvider.get().classpath.files + }) + + publishing { + publications { + release(MavenPublication) { + from components.release + } + } + } } \ No newline at end of file From c1437fc2f1e9612def7f2d13c77e3de443e081f8 Mon Sep 17 00:00:00 2001 From: Gaurav Goel Date: Tue, 12 Sep 2023 13:31:46 +0530 Subject: [PATCH 3/5] fix: PR comments addressed. Signed-off-by: Gaurav Goel --- .../web3_android_mpc_provider/EthereumTssAccount.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java b/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java index 70ef8ec..baa8606 100644 --- a/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java +++ b/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java @@ -77,7 +77,6 @@ public String signTypedData(String jsonData) throws IOException, TSSClientError, public String signLegacyTransaction(BigInteger chainID, String toAddress, Double amount, @Nullable String data, BigInteger nonce, BigInteger gasLimit) throws TSSClientError, CustomSigningError { BigInteger value = Convert.toWei(Double.toString(amount), Convert.Unit.ETHER).toBigInteger(); - // todo: this appears to be a bug in web3j, if data is null it throws but is marked as nullable String txData = ""; if (data != null) { txData = data; @@ -99,7 +98,7 @@ public String signLegacyTransaction(BigInteger chainID, String toAddress, Double Byte v = signatureResult.getThird(); if (v < 27) { - v = (byte) ((chainID.byteValue() * 2) + (v + 35)); + v = (byte) (v + 27); } Sign.SignatureData signatureData = new Sign.SignatureData(v, @@ -136,7 +135,7 @@ public String signTransaction(BigInteger chainID, String toAddress, Double amoun Triple signatureResult = sign(encodedTransactionString); Byte v = signatureResult.getThird(); - if (v < 27) { + if (v < 35) { v = (byte) ((chainID.byteValue() * 2) + (v + 35)); } From da685c72787b461e3e79e53097ca6d5ca8676bea Mon Sep 17 00:00:00 2001 From: Gaurav Goel Date: Tue, 12 Sep 2023 13:36:43 +0530 Subject: [PATCH 4/5] fix: PR comments addressed. Signed-off-by: Gaurav Goel --- .../web3auth/web3_android_mpc_provider/MpcProviderTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web3-android-mpc-provider/src/androidTest/java/com/web3auth/web3_android_mpc_provider/MpcProviderTests.java b/web3-android-mpc-provider/src/androidTest/java/com/web3auth/web3_android_mpc_provider/MpcProviderTests.java index 0d78248..9cc56ed 100644 --- a/web3-android-mpc-provider/src/androidTest/java/com/web3auth/web3_android_mpc_provider/MpcProviderTests.java +++ b/web3-android-mpc-provider/src/androidTest/java/com/web3auth/web3_android_mpc_provider/MpcProviderTests.java @@ -25,7 +25,7 @@ public class MpcProviderTests { System.loadLibrary("dkls-native"); } - final String example1 = "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"EtherMail\",\"version\":\"1\",\"chainId\":1,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Account\",\"wallet\":\"0x048975d4997d7578a3419851639c10318db430b6\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\"},\"contents\":\"Hello,Bob!\"}}"; + final String example1 = "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"Ether Mail\",\"version\":\"1\",\"chainId\":1,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Account\",\"wallet\":\"0x048975d4997d7578a3419851639c10318db430b6\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\"},\"contents\":\"Hello,Bob!\"}}"; final String fullAddress = "04238569d5e12caf57d34fb5b2a0679c7775b5f61fd18cd69db9cc600a651749c3ec13a9367380b7a024a67f5e663f3afd40175c3223da63f6024b05d0bd9f292e"; final String factorKey = "3b4af35bc4838471f94825f34c4f649904a258c0907d348bed653eb0c94ec6c0"; From 54410c27e6f510343c00688613d9a0f937c65bff Mon Sep 17 00:00:00 2001 From: Gaurav Goel Date: Tue, 12 Sep 2023 13:41:46 +0530 Subject: [PATCH 5/5] fix: PR comments addressed. Signed-off-by: Gaurav Goel --- .../web3_android_mpc_provider/EthereumTssAccount.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java b/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java index baa8606..e848d01 100644 --- a/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java +++ b/web3-android-mpc-provider/src/main/java/com/web3auth/web3_android_mpc_provider/EthereumTssAccount.java @@ -105,9 +105,9 @@ public String signLegacyTransaction(BigInteger chainID, String toAddress, Double signatureResult.getSecond().toByteArray(), signatureResult.getFirst().toByteArray()); - byte[] signedMsg = TransactionEncoder.encode(rawTransaction, signatureData); + byte[] signedTransaction = TransactionEncoder.encode(rawTransaction, signatureData); - return Numeric.toHexString(signedMsg); + return Numeric.toHexString(signedTransaction); } public String signTransaction(BigInteger chainID, String toAddress, Double amount, @Nullable String data, BigInteger nonce, BigInteger gasLimit, BigInteger maxPriorityFeePerGas, BigInteger maxFeePerGas) throws TSSClientError, CustomSigningError, SignatureException { @@ -143,9 +143,9 @@ public String signTransaction(BigInteger chainID, String toAddress, Double amoun signatureResult.getSecond().toByteArray(), signatureResult.getFirst().toByteArray()); - byte[] signedMsg = TransactionEncoder.encode(rawTransaction, signatureData); + byte[] signedTransaction = TransactionEncoder.encode(rawTransaction, signatureData); - return Numeric.toHexString(signedMsg); + return Numeric.toHexString(signedTransaction); } public void sendTransaction(Web3j web3j, String signedTx) throws IOException, CustomSigningError {