Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add recovery functions to EthereumSignedTransaction #173

Merged
merged 6 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
if: ${{ matrix.os == 'macos-latest' }}
run: xcrun llvm-cov export -format="lcov" .build/debug/Web3PackageTests.xctest/Contents/MacOS/Web3PackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov
- name: Upload Test Coverage
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
if: ${{ matrix.os == 'macos-latest' }}
with:
files: ./info.lcov
Expand Down
179 changes: 109 additions & 70 deletions Sources/Core/Transaction/EthereumTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,8 @@ public struct EthereumTransaction: Codable {
guard let nonce = nonce, let gasPrice = gasPrice, let gasLimit = gasLimit, let value = value else {
throw EthereumSignedTransaction.Error.transactionInvalid
}
let rlp = RLPItem(
nonce: nonce,
gasPrice: gasPrice,
gasLimit: gasLimit,
to: to,
value: value,
data: data,
v: chainId,
r: 0,
s: 0
)
let rawRlp = try RLPEncoder().encode(rlp)
let signature = try privateKey.sign(message: rawRlp)
let messageToSign = try self.messageToSign(chainId: chainId)
let signature = try privateKey.sign(message: messageToSign)

let v: BigUInt
if chainId.quantity == 0 {
Expand Down Expand Up @@ -190,24 +179,8 @@ public struct EthereumTransaction: Codable {
if chainId.quantity == BigUInt(0) {
throw EthereumSignedTransaction.Error.chainIdNotSet(msg: "EIP1559 transactions need a chainId")
}

let rlp = RLPItem(
nonce: nonce,
gasPrice: gasPrice ?? EthereumQuantity(integerLiteral: 0),
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
to: to,
value: value,
data: data,
chainId: chainId,
accessList: accessList,
transactionType: transactionType
)
let rawRlp = try RLPEncoder().encode(rlp)
var messageToSign = Bytes()
messageToSign.append(0x02)
messageToSign.append(contentsOf: rawRlp)

var messageToSign = try self.messageToSign(chainId: chainId)
let signature = try privateKey.sign(message: messageToSign)

let v = BigUInt(signature.v)
Expand All @@ -233,6 +206,58 @@ public struct EthereumTransaction: Codable {
}
}

public extension EthereumTransaction {

fileprivate func messageToSign(chainId: EthereumQuantity) throws -> Bytes {
let rlpEncoder = RLPEncoder()

if self.transactionType == .legacy {
guard let nonce = nonce, let gasPrice = gasPrice, let gasLimit = gasLimit, let value = value else {
throw EthereumSignedTransaction.Error.transactionInvalid
}
let rlp = RLPItem(
nonce: nonce,
gasPrice: gasPrice,
gasLimit: gasLimit,
to: to,
value: value,
data: data,
v: chainId,
r: 0,
s: 0
)
let rawRlp = try RLPEncoder().encode(rlp)
return rawRlp
} else if self.transactionType == .eip1559 {
guard let nonce = nonce, let maxFeePerGas = maxFeePerGas, let maxPriorityFeePerGas = maxPriorityFeePerGas,
let gasLimit = gasLimit, let value = value else {
throw EthereumSignedTransaction.Error.transactionInvalid
}
let rlp = RLPItem(
nonce: nonce,
gasPrice: gasPrice ?? EthereumQuantity(integerLiteral: 0),
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
to: to,
value: value,
data: data,
chainId: chainId,
accessList: accessList,
transactionType: transactionType
)
let rawRlp = try rlpEncoder.encode(rlp)
var messageToSign = Bytes()
messageToSign.append(0x02)
messageToSign.append(contentsOf: rawRlp)

return messageToSign
} else {
throw EthereumSignedTransaction.Error.transactionInvalid
}
}
}

public struct EthereumSignedTransaction {

// MARK: - Properties
Expand Down Expand Up @@ -365,54 +390,30 @@ public struct EthereumSignedTransaction {
recId = v.quantity
}
}
let rlp = RLPItem(
nonce: nonce,
gasPrice: gasPrice,
gasLimit: gasLimit,
to: to,
value: value,
data: data,
v: chainId,
r: 0,
s: 0
)
if let _ = try? EthereumPublicKey(message: RLPEncoder().encode(rlp), v: EthereumQuantity(quantity: recId), r: r, s: s) {
return true
do {
let messageToSign = try self.unsignedTransaction().messageToSign(chainId: self.chainId)
if let _ = try? EthereumPublicKey(message: messageToSign, v: EthereumQuantity(quantity: recId), r: r, s: s) {
return true
}
} catch {
return false
}

return false
}

private func verifyEip1559Signature() -> Bool {
let rlp = RLPItem(
nonce: nonce,
gasPrice: gasPrice,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: gasLimit,
to: to,
value: value,
data: data,
v: 0,
r: 0,
s: 0,
chainId: chainId,
accessList: accessList,
transactionType: transactionType
)
var messageToSign = Bytes()
messageToSign.append(0x02)
do {
try messageToSign.append(contentsOf: RLPEncoder().encode(rlp))
let messageToSign = try self.unsignedTransaction().messageToSign(chainId: self.chainId)

if let _ = try? EthereumPublicKey(message: messageToSign, v: v, r: r, s: s) {
return true
}

return false
} catch {
return false
}

if let _ = try? EthereumPublicKey(message: messageToSign, v: v, r: r, s: s) {
return true
}

return false
}

// MARK: - Errors
Expand All @@ -437,7 +438,7 @@ extension EthereumSignedTransaction {
rawTxBytes.removeFirst()
}
do {
var rlp = try RLPDecoder().decode(rawTxBytes)
let rlp = try RLPDecoder().decode(rawTxBytes)

try self.init(rlp: rlp)
} catch {
Expand Down Expand Up @@ -712,3 +713,41 @@ extension EthereumSignedTransaction: Hashable {
hasher.combine(transactionType)
}
}

extension EthereumSignedTransaction {

public func from() throws -> EthereumAddress {
return try publicKey().address
}

public func publicKey() throws -> EthereumPublicKey {
let messageToSign = try self.unsignedTransaction().messageToSign(chainId: self.chainId)
var recId: BigUInt
if v.quantity >= BigUInt(35) + (BigUInt(2) * chainId.quantity) {
recId = v.quantity - BigUInt(35) - (BigUInt(2) * chainId.quantity)
} else {
if v.quantity >= 27 {
recId = v.quantity - 27
} else {
recId = v.quantity
}
}
return try EthereumPublicKey(message: messageToSign, v: EthereumQuantity(quantity: recId), r: self.r, s: self.s)
}

public func unsignedTransaction() throws -> EthereumTransaction {
return EthereumTransaction(
nonce: self.nonce,
gasPrice: self.gasPrice,
maxFeePerGas: self.maxFeePerGas,
maxPriorityFeePerGas: self.maxPriorityFeePerGas,
gasLimit: self.gasLimit,
to: self.to,
value: self.value,
data: self.data,
accessList: self.accessList,
transactionType: self.transactionType
)
}

}
Loading
Loading