Skip to content

Commit

Permalink
V3.9.0
Browse files Browse the repository at this point in the history
- Fix issue with Ethereum RLP encoding related to leading zero in signature S.
  • Loading branch information
mrtnetwork committed Aug 20, 2024
1 parent 756817b commit fbade03
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.9.0

- Fix issue with Ethereum RLP encoding related to leading zero in signature S.

## 3.8.0

- Update dependencies.
Expand Down
49 changes: 37 additions & 12 deletions lib/ethereum/src/transaction/eth_transaction.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ class _ETHTransactionUtils {
return (BigInt.from(v) - BigInt.from(35)) ~/ BigInt.two;
}

static List<int> trimLeadingZero(List<int> bytes) {
List<int> data = bytes;
while (data.isNotEmpty) {
if (data[0] != 0) break;
data = data.sublist(1);
}
return data;
}

static List<int> leadingZero32Bytes(List<int> bytes) {
if (bytes.length >= 32) return bytes;
final data = List.filled(32, 0);
data.setAll(32 - bytes.length, bytes);
return data;
}

/// Returns the parity for a given integer [v].
/// Returns 0 if [v] is 27, otherwise returns 1.
static int parity(int v) => (v == 27) ? 0 : 1;
Expand Down Expand Up @@ -92,8 +108,10 @@ class _ETHTransactionUtils {
ETHSignature? sig;
BigInt chainId = BigInt.zero;
if (decode.length > 6) {
final List<int> rBytes = List<int>.from(decode[7]);
final List<int> sBytes = List<int>.from(decode[8]);
final List<int> rBytes =
_ETHTransactionUtils.leadingZero32Bytes(List<int>.from(decode[7]));
final List<int> sBytes =
_ETHTransactionUtils.leadingZero32Bytes(List<int>.from(decode[8]));
final v = IntUtils.fromBytes(decode[6]);
if (rBytes.isEmpty && sBytes.isEmpty) {
chainId = BigInt.from(v);
Expand Down Expand Up @@ -130,8 +148,11 @@ class _ETHTransactionUtils {
.toList();
ETHSignature? sig;
if (decode.length > 8) {
final sigBytes =
List<int>.from([...decode[9], ...decode[10], ...decode[8]]);
final List<int> rBytes =
_ETHTransactionUtils.leadingZero32Bytes(List<int>.from(decode[9]));
final List<int> sBytes =
_ETHTransactionUtils.leadingZero32Bytes(List<int>.from(decode[10]));
final sigBytes = List<int>.from([...rBytes, ...sBytes, ...decode[8]]);
sig = ETHSignature.fromBytes(sigBytes);
}
return ETHTransaction._(
Expand Down Expand Up @@ -164,8 +185,11 @@ class _ETHTransactionUtils {
.toList();
ETHSignature? sig;
if (decode.length > 9) {
final sigBytes =
List<int>.from([...decode[10], ...decode[11], ...decode[9]]);
final List<int> rBytes =
_ETHTransactionUtils.leadingZero32Bytes(List<int>.from(decode[10]));
final List<int> sBytes =
_ETHTransactionUtils.leadingZero32Bytes(List<int>.from(decode[11]));
final sigBytes = List<int>.from([...rBytes, ...sBytes, ...decode[9]]);
sig = ETHSignature.fromBytes(sigBytes);
}
return ETHTransaction._(
Expand Down Expand Up @@ -374,9 +398,10 @@ class ETHTransaction {
if (sig != null) {
fields.add(
_ETHTransactionUtils.intToBytes(_ETHTransactionUtils.parity(sig.v)));
fields.add(sig.rBytes);
fields.add(sig.sBytes);
fields.add(_ETHTransactionUtils.trimLeadingZero(sig.rBytes));
fields.add(_ETHTransactionUtils.trimLeadingZero(sig.sBytes));
}

return [ETHTransactionType.eip1559.prefix, ...RLPEncoder.encode(fields)];
}

Expand All @@ -396,8 +421,8 @@ class ETHTransaction {
if (sig != null) {
fields.add(
_ETHTransactionUtils.intToBytes(_ETHTransactionUtils.parity(sig.v)));
fields.add(sig.rBytes);
fields.add(sig.sBytes);
fields.add(_ETHTransactionUtils.trimLeadingZero(sig.rBytes));
fields.add(_ETHTransactionUtils.trimLeadingZero(sig.sBytes));
}
return [ETHTransactionType.eip2930.prefix, ...RLPEncoder.encode(fields)];
}
Expand Down Expand Up @@ -432,8 +457,8 @@ class ETHTransaction {
throw const MessageException("Mismatch chainID/Signature.V");
}
fields.add(BigintUtils.toBytes(v, length: BigintUtils.bitlengthInBytes(v)));
fields.add(sig.rBytes);
fields.add(sig.sBytes);
fields.add(_ETHTransactionUtils.trimLeadingZero(sig.rBytes));
fields.add(_ETHTransactionUtils.trimLeadingZero(sig.sBytes));
return RLPEncoder.encode(fields);
}

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: on_chain
description: Streamline Ethereum, Tron, Solana and Cardano operations. Effortlessly create transactions, interact with smart contracts, sign, and send transactions.
version: 3.8.0
version: 3.9.0
homepage: "https://github.com/mrtnetwork/on_chain"
repository: "https://github.com/mrtnetwork/on_chain"
Author: [email protected]
Expand Down
30 changes: 30 additions & 0 deletions test/etherum/transaction_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:on_chain/ethereum/ethereum.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:test/test.dart';

void main() {
/// transaction with leading zero s bytes.
test("transaction leading zero s", () {
final addr = ETHAddress("0x084937B3f86ea7BbCA86F2809809A65ED8A7ADa9");
final signer = ETHSigner.fromKeyBytes(BytesUtils.fromHexString(
"e9f4fe38ffc54abd156dd4b8a39611fce696af62841ee6422ee36ba7b26c53f5"));

final receiver = ETHAddress("0x4fAfB33f0e492FD10e91b55ED88872104fFd94ee");
final transaction = ETHTransaction(
nonce: 0,
from: addr,
type: ETHTransactionType.legacy,
to: receiver,
gasLimit: BigInt.from(21000),
data: const [],
value: ETHHelper.toWei("0.01"),
chainId: BigInt.from(97),
gasPrice: BigInt.from(5000000000));
final serialize = transaction.serialized;
final sign = signer.sign(serialize);
final signedSerialize = transaction.signedSerialized(sign);

final decode = ETHTransaction.fromSerialized(signedSerialize);
expect(decode.signedSerialized(), signedSerialize);
});
}

0 comments on commit fbade03

Please sign in to comment.