This is an automated email from the ASF dual-hosted git repository.

toulmean pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git


The following commit(s) were added to refs/heads/main by this push:
     new eb3e9c9  Add address from transaction
     new 532e941  Merge pull request #393 from atoulme/address_tx
eb3e9c9 is described below

commit eb3e9c925101158ac907f6af959a89d021ed2bf9
Author: Antoine Toulme <anto...@lunar-ocean.com>
AuthorDate: Fri Mar 25 22:32:02 2022 -0700

    Add address from transaction
---
 .../apache/tuweni/blockprocessor/BlockProcessor.kt |  9 +--
 .../main/java/org/apache/tuweni/eth/Address.java   | 15 +++++
 .../java/org/apache/tuweni/eth/Transaction.java    | 51 ++++++++------
 .../java/org/apache/tuweni/eth/AddressTest.java    | 77 ++++++++++++++++++++++
 4 files changed, 125 insertions(+), 27 deletions(-)

diff --git 
a/eth-blockprocessor/src/main/kotlin/org/apache/tuweni/blockprocessor/BlockProcessor.kt
 
b/eth-blockprocessor/src/main/kotlin/org/apache/tuweni/blockprocessor/BlockProcessor.kt
index d27ecfc..95e5718 100644
--- 
a/eth-blockprocessor/src/main/kotlin/org/apache/tuweni/blockprocessor/BlockProcessor.kt
+++ 
b/eth-blockprocessor/src/main/kotlin/org/apache/tuweni/blockprocessor/BlockProcessor.kt
@@ -78,14 +78,7 @@ class BlockProcessor {
       var to: Address
       var inputData: Bytes
       if (null == tx.to) {
-        val contractAddress = Address.fromBytes(
-          Hash.hash(
-            RLP.encodeList {
-              it.writeValue(tx.sender!!)
-              it.writeValue(tx.nonce)
-            }
-          ).slice(12)
-        )
+        val contractAddress = Address.fromTransaction(tx)
         to = contractAddress
         code = tx.payload
         inputData = Bytes.EMPTY
diff --git a/eth/src/main/java/org/apache/tuweni/eth/Address.java 
b/eth/src/main/java/org/apache/tuweni/eth/Address.java
index 9dce379..c01872a 100644
--- a/eth/src/main/java/org/apache/tuweni/eth/Address.java
+++ b/eth/src/main/java/org/apache/tuweni/eth/Address.java
@@ -18,6 +18,7 @@ import static java.util.Objects.requireNonNull;
 import org.apache.tuweni.bytes.Bytes;
 import org.apache.tuweni.bytes.DelegatingBytes;
 import org.apache.tuweni.crypto.SECP256K1;
+import org.apache.tuweni.rlp.RLP;
 
 /**
  * An Ethereum account address.
@@ -30,6 +31,20 @@ public final class Address extends DelegatingBytes {
   public static final Address ZERO = Address.fromBytes(Bytes.repeat((byte) 0, 
20));
 
   /**
+   * Derive a contract address from a transaction.
+   */
+  public static Address fromTransaction(Transaction transaction) {
+    if (transaction.getSender() == null) {
+      throw new IllegalArgumentException("Invalid transaction signature, 
cannot recover sender");
+    }
+    Bytes encoded = RLP.encodeList((writer) -> {
+      writer.writeValue(transaction.getSender());
+      writer.writeValue(transaction.getNonce().toMinimalBytes());
+    });
+    return Address.fromBytes(Hash.hash(encoded).slice(12));
+  }
+
+  /**
    * Transform a public key into an Ethereum address.
    * 
    * @param publicKey the public key
diff --git a/eth/src/main/java/org/apache/tuweni/eth/Transaction.java 
b/eth/src/main/java/org/apache/tuweni/eth/Transaction.java
index 5843a6b..9f07a71 100644
--- a/eth/src/main/java/org/apache/tuweni/eth/Transaction.java
+++ b/eth/src/main/java/org/apache/tuweni/eth/Transaction.java
@@ -103,26 +103,8 @@ public final class Transaction {
       throw new RLPException("Additional bytes present at the end of the 
encoding");
     }
 
-    byte v;
-    Integer chainId = null;
-
-    if (encodedV == V_BASE || encodedV == (V_BASE + 1)) {
-      v = (byte) (encodedV - V_BASE);
-    } else if (encodedV > 35) {
-      chainId = (encodedV - 35) / 2;
-      v = (byte) (encodedV - (2 * chainId + 35));
-    } else {
-      throw new RLPException("Invalid v encoded value " + encodedV);
-    }
-
-    SECP256K1.Signature signature;
     try {
-      signature = SECP256K1.Signature.create(v, r, s);
-    } catch (IllegalArgumentException e) {
-      throw new RLPException("Invalid signature: " + e.getMessage());
-    }
-    try {
-      return new Transaction(nonce, gasPrice, gasLimit, address, value, 
payload, chainId, signature);
+      return fromEncoded(nonce, gasPrice, gasLimit, address, value, payload, 
r, s, encodedV);
     } catch (IllegalArgumentException e) {
       throw new RLPException(e.getMessage(), e);
     }
@@ -232,6 +214,37 @@ public final class Transaction {
     this.chainId = chainId;
   }
 
+  public static Transaction fromEncoded(
+      UInt256 nonce,
+      Wei gasPrice,
+      Gas gasLimit,
+      @Nullable Address to,
+      Wei value,
+      Bytes payload,
+      BigInteger r,
+      BigInteger s,
+      int encodedV) {
+    byte v;
+    Integer chainId = null;
+
+    if (encodedV == V_BASE || encodedV == (V_BASE + 1)) {
+      v = (byte) (encodedV - V_BASE);
+    } else if (encodedV > 35) {
+      chainId = (encodedV - 35) / 2;
+      v = (byte) (encodedV - (2 * chainId + 35));
+    } else {
+      throw new RLPException("Invalid v encoded value " + encodedV);
+    }
+
+    SECP256K1.Signature signature;
+    try {
+      signature = SECP256K1.Signature.create(v, r, s);
+    } catch (IllegalArgumentException e) {
+      throw new RLPException("Invalid signature: " + e.getMessage());
+    }
+    return new Transaction(nonce, gasPrice, gasLimit, to, value, payload, 
chainId, signature);
+  }
+
   /**
    * Provides the transaction nonce
    * 
diff --git a/eth/src/test/java/org/apache/tuweni/eth/AddressTest.java 
b/eth/src/test/java/org/apache/tuweni/eth/AddressTest.java
new file mode 100644
index 0000000..085aac8
--- /dev/null
+++ b/eth/src/test/java/org/apache/tuweni/eth/AddressTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements. See the NOTICE
+ * file distributed with this work for additional information regarding 
copyright ownership. The ASF licenses this file
+ * to You under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.tuweni.eth;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.apache.tuweni.bytes.Bytes;
+import org.apache.tuweni.junit.BouncyCastleExtension;
+import org.apache.tuweni.units.bigints.UInt256;
+import org.apache.tuweni.units.ethereum.Gas;
+import org.apache.tuweni.units.ethereum.Wei;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(BouncyCastleExtension.class)
+public class AddressTest {
+  /*
+  {
+  "jsonrpc": "2.0",
+  "id": 1,
+  "result": {
+       "blockHash": 
"0xdf10741b3b0820015caba402f3a230237af86ac0302a923bba4d4f81610c13f0",
+       "blockNumber": "0x64b991",
+       "from": "0xc82a6220398714f74e2d929309f4c5b1d4f7b0f6",
+       "gas": "0x1abffe",
+       "gasPrice": "0x4a817c800",
+       "hash": 
"0x24158c06c57f4a2814b8b31809378c4a457028d57dcf3c29dfbbc13f850ebfea",
+       "input": 
"0x60806040523480156200001157600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040805190810160405280600381526020017f4332450000000000000000000000000000000000000000000000000000000000815250600290805190602001906200009f929190620001f8565b506040805190810160405280600981526020017f43324520546f6b656e000000000000000000000000000000000000000000000081525060039080519060200190620000ed929190620001f8565b5
 [...]
+       "nonce": "0x2",
+       "r": 
"0x10900d90580ce9c809937d8cedc0ae7e487ff94b672b0ecf6443f58dc417f57f",
+       "s": 
"0x3f702acb8d9bf4f6887376f3c10b3c254d0b0f136dc570df2a114784d35612e2",
+       "to": null,
+       "transactionIndex": "0x5",
+       "type": "0x0",
+       "v": "0x26",
+       "value": "0x0"
+  }
+  }
+   */
+  @Test
+  void testTransactionToAddress() {
+    UInt256 r = 
UInt256.fromHexString("0x10900d90580ce9c809937d8cedc0ae7e487ff94b672b0ecf6443f58dc417f57f");
+    UInt256 s = 
UInt256.fromHexString("0x3f702acb8d9bf4f6887376f3c10b3c254d0b0f136dc570df2a114784d35612e2");
+    Bytes v = Bytes.fromHexString("0x26");
+    Transaction tx = Transaction
+        .fromEncoded(
+            UInt256.valueOf(2),
+            Wei.valueOf(UInt256.fromHexString("0x4a817c800")),
+            Gas.valueOf(UInt256.fromHexString("0x1abffe")),
+            null,
+            Wei.valueOf(0),
+            Bytes
+                .fromHexString(
+                    
"0x60806040523480156200001157600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040805190810160405280600381526020017f4332450000000000000000000000000000000000000000000000000000000000815250600290805190602001906200009f929190620001f8565b506040805190810160405280600981526020017f43324520546f6b656e000000000000000000000000000000000000000000000081525060039080519060200190620000ed92919062000
 [...]
+            r.toUnsignedBigInteger(),
+            s.toUnsignedBigInteger(),
+            v.toInt());
+    assertEquals(
+        
Hash.fromHexString("0x24158c06c57f4a2814b8b31809378c4a457028d57dcf3c29dfbbc13f850ebfea"),
+        tx.getHash());
+
+    assertEquals("0xc82a6220398714f74e2d929309f4c5b1d4f7b0f6", 
tx.getSender().toHexString());
+    Address result = Address.fromTransaction(tx);
+    assertEquals("0xceb46d90598cd81c4e10557c0050a27569e2163e", 
result.toHexString());
+  }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@tuweni.apache.org
For additional commands, e-mail: commits-h...@tuweni.apache.org

Reply via email to