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