-
Notifications
You must be signed in to change notification settings - Fork 834
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Txparse subcommand implementation (#6268)
* txparse subcommand Signed-off-by: garyschulte <garyschulte@gmail.com> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
- Loading branch information
1 parent
1d3eb93
commit 9f8f121
Showing
3 changed files
with
201 additions
and
0 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
129 changes: 129 additions & 0 deletions
129
besu/src/main/java/org/hyperledger/besu/cli/subcommands/TxParseSubCommand.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,129 @@ | ||
/* | ||
* Copyright Hyperledger Besu Contributors. | ||
* | ||
* Licensed 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. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
package org.hyperledger.besu.cli.subcommands; | ||
|
||
import static java.nio.charset.StandardCharsets.UTF_8; | ||
import static org.hyperledger.besu.cli.subcommands.TxParseSubCommand.COMMAND_NAME; | ||
|
||
import org.hyperledger.besu.cli.util.VersionProvider; | ||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; | ||
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext; | ||
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.InputStreamReader; | ||
import java.io.PrintWriter; | ||
import java.math.BigInteger; | ||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
import java.util.stream.Stream; | ||
|
||
import org.apache.tuweni.bytes.Bytes; | ||
import picocli.CommandLine; | ||
|
||
/** | ||
* txparse sub command implementing txparse spec from | ||
* https://github.com/holiman/txparse/tree/main/cmd/txparse | ||
*/ | ||
@CommandLine.Command( | ||
name = COMMAND_NAME, | ||
description = "Parse input transactions and return the sender, or an error.", | ||
mixinStandardHelpOptions = true, | ||
versionProvider = VersionProvider.class) | ||
public class TxParseSubCommand implements Runnable { | ||
|
||
/** The constant COMMAND_NAME. */ | ||
public static final String COMMAND_NAME = "txparse"; | ||
|
||
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. | ||
@CommandLine.Option( | ||
names = "--corpus-file", | ||
arity = "1..1", | ||
description = "file to read transaction data lines from, otherwise defaults to stdin") | ||
private String corpusFile = null; | ||
|
||
static final BigInteger halfCurveOrder = | ||
SignatureAlgorithmFactory.getInstance().getHalfCurveOrder(); | ||
static final BigInteger chainId = new BigInteger("1", 10); | ||
|
||
private final PrintWriter out; | ||
|
||
/** | ||
* Instantiates a new TxParse sub command. | ||
* | ||
* @param out the PrintWriter where validation results will be reported. | ||
*/ | ||
public TxParseSubCommand(final PrintWriter out) { | ||
this.out = out; | ||
} | ||
|
||
@SuppressWarnings("StreamResourceLeak") | ||
Stream<String> fileStreamReader(final String filePath) { | ||
try { | ||
return Files.lines(Paths.get(filePath)) | ||
// skip comments | ||
.filter(line -> !line.startsWith("#")) | ||
// strip out non-alphanumeric characters | ||
.map(line -> line.replaceAll("[^a-zA-Z0-9]", "")); | ||
} catch (Exception ex) { | ||
throw new RuntimeException(ex); | ||
} | ||
} | ||
|
||
@Override | ||
public void run() { | ||
|
||
Stream<String> txStream; | ||
if (corpusFile != null) { | ||
txStream = fileStreamReader(corpusFile); | ||
} else { | ||
txStream = new BufferedReader(new InputStreamReader(System.in, UTF_8)).lines(); | ||
} | ||
|
||
txStream.forEach( | ||
line -> { | ||
try { | ||
Bytes bytes = Bytes.fromHexStringLenient(line); | ||
dump(bytes); | ||
} catch (Exception ex) { | ||
err(ex.getMessage()); | ||
} | ||
}); | ||
} | ||
|
||
void dump(final Bytes tx) { | ||
try { | ||
var transaction = TransactionDecoder.decodeOpaqueBytes(tx, EncodingContext.BLOCK_BODY); | ||
|
||
// https://github.com/hyperledger/besu/blob/5fe49c60b30fe2954c7967e8475c3b3e9afecf35/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java#L252 | ||
if (transaction.getChainId().isPresent() && !transaction.getChainId().get().equals(chainId)) { | ||
throw new Exception("wrong chain id"); | ||
} | ||
|
||
// https://github.com/hyperledger/besu/blob/5fe49c60b30fe2954c7967e8475c3b3e9afecf35/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java#L270 | ||
if (transaction.getS().compareTo(halfCurveOrder) > 0) { | ||
throw new Exception("signature s out of range"); | ||
} | ||
out.println(transaction.getSender()); | ||
|
||
} catch (Exception ex) { | ||
err(ex.getMessage()); | ||
} | ||
} | ||
|
||
void err(final String message) { | ||
out.println("err: " + message); | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
besu/src/test/java/org/hyperledger/besu/cli/subcommands/TxParseSubCommandTest.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,69 @@ | ||
/* | ||
* Copyright Hyperledger Besu Contributors. | ||
* | ||
* Licensed 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. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
package org.hyperledger.besu.cli.subcommands; | ||
|
||
import static java.nio.charset.StandardCharsets.UTF_8; | ||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.OutputStreamWriter; | ||
import java.io.PrintWriter; | ||
|
||
import org.apache.tuweni.bytes.Bytes; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class TxParseSubCommandTest { | ||
|
||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
PrintWriter writer = new PrintWriter(new OutputStreamWriter(baos, UTF_8), true); | ||
TxParseSubCommand txParseSubCommand = new TxParseSubCommand(writer); | ||
|
||
@Test | ||
public void smokeTest() { | ||
txParseSubCommand.dump( | ||
Bytes.fromHexString( | ||
"0x02f875018363bc5a8477359400850c570bd200825208943893d427c770de9b92065da03a8c825f13498da28702fbf8ccf8530480c080a0dd49141ecf93eeeaed5f3499a6103d6a9778e24a37b1f5c6a336c60c8a078c15a0511b51a3050771f66c1b779210a46a51be521a2446a3f87fc656dcfd1782aa5e")); | ||
String output = baos.toString(UTF_8); | ||
assertThat(output).isEqualTo("0xeb2629a2734e272bcc07bda959863f316f4bd4cf\n"); | ||
} | ||
|
||
@Test | ||
public void assertInvalidRlp() { | ||
txParseSubCommand.dump( | ||
Bytes.fromHexString( | ||
"0x03f90124f9011e010516058261a89403040500000000000000000000000000000000006383000000f8b6f859940000000000000000000000000000000074657374f842a00100000000000000000000000000000000000000000000000000000000000000a00200000000000000000000000000000000000000000000000000000000000000f859940000000000000000000000000000000074657374f842a00100000000000000000000000000000000000000000000000000000000000000a002000000000000000000000000000000000000000000000000000000000000000fc080a082f96cae43a3f2554cad899a6e9168a3cd613454f82e93488f9b4c1a998cc814a06b7519cd0e3159d6ce9bf811ff3ca4e9c5d7ac63fc52142370a0725e4c5273b2c0c0c0")); | ||
String output = baos.toString(UTF_8); | ||
assertThat(output).startsWith("err: "); | ||
assertThat(output).contains("arbitrary precision scalar"); | ||
} | ||
|
||
@Test | ||
public void assertValidRlp() { | ||
txParseSubCommand.dump( | ||
Bytes.fromHexString( | ||
"0x03f9013f010516058261a89403040500000000000000000000000000000000006383000000f8b6f859940000000000000000000000000000000074657374f842a00100000000000000000000000000000000000000000000000000000000000000a00200000000000000000000000000000000000000000000000000000000000000f859940000000000000000000000000000000074657374f842a00100000000000000000000000000000000000000000000000000000000000000a002000000000000000000000000000000000000000000000000000000000000000fe1a0010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c44401401a0d98465c5489a09933e6428657f1fc4461d49febd8556c1c7e7053ed0be49cc4fa02c0d67e40aed75f87ea640cc47064d79f4383e24dec283ac6eb81e19da46cc26")); | ||
String output = baos.toString(UTF_8); | ||
assertThat(output).isEqualTo("0x730aa72228b55236de378f5267dfc77723b1380c\n"); | ||
} | ||
|
||
@Test | ||
public void assertInvalidChainId() { | ||
txParseSubCommand.dump( | ||
Bytes.fromHexString( | ||
"0xf901fc303083303030803030b901ae30303030303030303030433030303030303030303030303030303030303030303030303030303030203030413030303030303030303000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038390030000000002800380000413159000021000000002b0000000000003800000000003800000000000000000000002b633279315a00633200303041374222610063416200325832325a002543323861795930314130383058435931317a633043304239623900310031254363384361000023433158253041380037000027285839005a007937000000623700002761002b5a003937622e000000006300007863410000002a2e320000000000005a0000000000000037242778002039006120412e6362002138300000002a313030303030303030303030373030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030a03030303030303030303030303030303030303030303030303030303030303030a03030303030303030303030303030303030303030303030303030303030303030")); | ||
String output = baos.toString(UTF_8); | ||
assertThat(output).isEqualTo("err: wrong chain id\n"); | ||
} | ||
} |