Skip to content

Commit

Permalink
Merge branch 'develop' into pr/2
Browse files Browse the repository at this point in the history
  • Loading branch information
pharms-eth committed Nov 16, 2022
2 parents d8dd9a2 + 915bcd9 commit efba907
Show file tree
Hide file tree
Showing 21 changed files with 510 additions and 274 deletions.
2 changes: 1 addition & 1 deletion Sources/Core/EthereumABI/ABIElements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ extension ABI.Element.Function {
// set a flag to detect the request succeeded
}

if returnArray.isEmpty {
if returnArray.isEmpty && !outputs.isEmpty && data.isEmpty{
return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ import Foundation

extension APIRequest {
var method: REST {
switch self {
default: return .POST
}
.POST
}

public var encodedBody: Data {
let request = RequestBody(method: self.call, params: self.parameters)
let request = RequestBody(method: call, params: parameters)
// this is safe to force try this here
// Because request must failed to compile if it not conformable with `Encodable` protocol
return try! JSONEncoder().encode(request)
Expand Down
228 changes: 119 additions & 109 deletions Sources/Core/KeystoreManager/BIP32HDNode.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// web3swift
//
// Created by Alex Vlasov.
// Copyright © 2018 Alex Vlasov. All rights reserved.
Expand All @@ -23,12 +24,11 @@ extension UInt32 {
}

public class HDNode {
public struct HDversion {
public var privatePrefix: Data = Data.fromHex("0x0488ADE4")!
public var publicPrefix: Data = Data.fromHex("0x0488B21E")!
public init() {

}
private struct HDversion{
public static var privatePrefix: Data? = Data.fromHex("0x0488ADE4")
public static var publicPrefix: Data? = Data.fromHex("0x0488B21E")

}
public var path: String? = "m"
public var privateKey: Data?
Expand All @@ -51,11 +51,6 @@ public class HDNode {
}
}
}
public var hasPrivate: Bool {
get {
return privateKey != nil
}
}

init() {
publicKey = Data()
Expand All @@ -72,7 +67,7 @@ public class HDNode {
guard data.count == 82 else {return nil}
let header = data[0..<4]
var serializePrivate = false
if header == HDNode.HDversion().privatePrefix {
if header == HDversion.privatePrefix {
serializePrivate = true
}
depth = data[4..<5].bytes[0]
Expand All @@ -94,8 +89,10 @@ public class HDNode {

public init?(seed: Data) {
guard seed.count >= 16 else {return nil}
let hmacKey = "Bitcoin seed".data(using: .ascii)!
let hmac: Authenticator = HMAC(key: hmacKey.bytes, variant: HMAC.Variant.sha2(.sha512))

guard let hmacKey = "Bitcoin seed".data(using: .ascii) else {return nil}
let hmac:Authenticator = HMAC(key: hmacKey.bytes, variant: HMAC.Variant.sha2(.sha512))

guard let entropy = try? hmac.authenticate(seed.bytes) else {return nil}
guard entropy.count == 64 else { return nil}
let I_L = entropy[0..<32]
Expand All @@ -104,7 +101,7 @@ public class HDNode {
let privKeyCandidate = Data(I_L)
guard SECP256K1.verifyPrivateKey(privateKey: privKeyCandidate) else {return nil}
guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: privKeyCandidate, compressed: true) else {return nil}
guard pubKeyCandidate.bytes[0] == 0x02 || pubKeyCandidate.bytes[0] == 0x03 else {return nil}
guard pubKeyCandidate.bytes.first == 0x02 || pubKeyCandidate.bytes.first == 0x03 else {return nil}
publicKey = pubKeyCandidate
privateKey = privKeyCandidate
depth = 0x00
Expand All @@ -120,91 +117,99 @@ public class HDNode {
}

extension HDNode {
public func derive (index: UInt32, derivePrivateKey: Bool, hardened: Bool = false) -> HDNode? {
if derivePrivateKey {
if self.hasPrivate { // derive private key when is itself extended private key
var entropy: [UInt8]
var trueIndex: UInt32
if index >= (UInt32(1) << 31) || hardened {
trueIndex = index
if trueIndex < (UInt32(1) << 31) {
trueIndex = trueIndex + (UInt32(1) << 31)
}
let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
var inputForHMAC = Data()
inputForHMAC.append(Data([UInt8(0x00)]))
inputForHMAC.append(self.privateKey!)
inputForHMAC.append(trueIndex.serialize32())
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
guard ent.count == 64 else { return nil }
entropy = ent
} else {
trueIndex = index
let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
var inputForHMAC = Data()
inputForHMAC.append(self.publicKey)
inputForHMAC.append(trueIndex.serialize32())
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
guard ent.count == 64 else { return nil }
entropy = ent
}
let I_L = entropy[0..<32]
let I_R = entropy[32..<64]
let cc = Data(I_R)
let bn = BigUInt(Data(I_L))
if bn > HDNode.curveOrder {
if trueIndex < UInt32.max {
return self.derive(index: index+1, derivePrivateKey: derivePrivateKey, hardened: hardened)
}
return nil
}
let newPK = (bn + BigUInt(self.privateKey!)) % HDNode.curveOrder
if newPK == BigUInt(0) {
if trueIndex < UInt32.max {
return self.derive(index: index+1, derivePrivateKey: derivePrivateKey, hardened: hardened)
}
return nil
}
guard let privKeyCandidate = newPK.serialize().setLengthLeft(32) else {return nil}
guard SECP256K1.verifyPrivateKey(privateKey: privKeyCandidate) else {return nil }
guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: privKeyCandidate, compressed: true) else {return nil}
guard pubKeyCandidate.bytes[0] == 0x02 || pubKeyCandidate.bytes[0] == 0x03 else {return nil}
guard self.depth < UInt8.max else {return nil}
let newNode = HDNode()
newNode.chaincode = cc
newNode.depth = self.depth + 1
newNode.publicKey = pubKeyCandidate
newNode.privateKey = privKeyCandidate
newNode.childNumber = trueIndex
guard let fprint = try? RIPEMD160.hash(message: self.publicKey.sha256())[0..<4] else {
return nil
}
newNode.parentFingerprint = fprint
var newPath = String()
if newNode.isHardened {
newPath = self.path! + "/"
newPath += String(newNode.index % HDNode.hardenedIndexPrefix) + "'"
} else {
newPath = self.path! + "/" + String(newNode.index)
}
newNode.path = newPath
return newNode
} else {
return nil // derive private key when is itself extended public key (impossible)
public func derive(index: UInt32, derivePrivateKey:Bool, hardened: Bool = false) -> HDNode? {
derivePrivateKey ?
deriveAlongPrivateKey(index: index, derivePrivateKey: derivePrivateKey, hardened: hardened)
:
deriveWithoutPrivateKey(index: index, derivePrivateKey: derivePrivateKey, hardened: hardened)
}
public func deriveAlongPrivateKey(index: UInt32, derivePrivateKey:Bool, hardened: Bool = false) -> HDNode? {
guard let privateKey = self.privateKey else {
return nil
}// derive private key when is itself extended private key
var entropy:Array<UInt8>
var trueIndex: UInt32
if index >= (UInt32(1) << 31) || hardened {
trueIndex = index
if trueIndex < (UInt32(1) << 31) {
trueIndex = trueIndex + (UInt32(1) << 31)
}
} else { // deriving only the public key
var entropy: [UInt8] // derive public key when is itself public key
if index >= (UInt32(1) << 31) || hardened {
return nil // no derivation of hardened public key from extended public key
} else {
let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
var inputForHMAC = Data()
inputForHMAC.append(self.publicKey)
inputForHMAC.append(index.serialize32())
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
guard ent.count == 64 else { return nil }
entropy = ent
let hmac:Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
var inputForHMAC = Data()
inputForHMAC.append(Data([UInt8(0x00)]))
inputForHMAC.append(privateKey)
inputForHMAC.append(trueIndex.serialize32())
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
guard ent.count == 64 else { return nil }
entropy = ent
} else {
trueIndex = index
let hmac:Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
var inputForHMAC = Data()
inputForHMAC.append(self.publicKey)
inputForHMAC.append(trueIndex.serialize32())
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
guard ent.count == 64 else { return nil }
entropy = ent
}
let I_L = entropy[0..<32]
let I_R = entropy[32..<64]
let cc = Data(I_R)
let bn = BigUInt(Data(I_L))
if bn > HDNode.curveOrder {
if trueIndex < UInt32.max {
return self.derive(index:index+1, derivePrivateKey: derivePrivateKey, hardened:hardened)
}
return nil
}
let newPK = (bn + BigUInt(privateKey)) % HDNode.curveOrder
if newPK == BigUInt(0) {
if trueIndex < UInt32.max {
return self.derive(index:index+1, derivePrivateKey: derivePrivateKey, hardened:hardened)
}
return nil
}
guard let privKeyCandidate = newPK.serialize().setLengthLeft(32) else {return nil}
guard SECP256K1.verifyPrivateKey(privateKey: privKeyCandidate) else {return nil }
guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: privKeyCandidate, compressed: true) else {return nil}
guard pubKeyCandidate.bytes.first == 0x02 || pubKeyCandidate.bytes.first == 0x03 else {return nil}
guard self.depth < UInt8.max else {return nil}
let newNode = HDNode()
newNode.chaincode = cc
newNode.depth = self.depth + 1
newNode.publicKey = pubKeyCandidate
newNode.privateKey = privKeyCandidate
newNode.childNumber = trueIndex
guard let fprint = try? RIPEMD160.hash(message: self.publicKey.sha256())[0..<4] else {
return nil
}
newNode.parentFingerprint = fprint
var newPath = String()
if newNode.isHardened {
newPath = (self.path ?? "") + "/"
newPath += String(newNode.index % HDNode.hardenedIndexPrefix) + "'"
} else {
newPath = (self.path ?? "") + "/" + String(newNode.index)
}
newNode.path = newPath
return newNode
}

public func deriveWithoutPrivateKey(index: UInt32, derivePrivateKey:Bool, hardened: Bool = false) -> HDNode? {
// deriving only the public key
var entropy:Array<UInt8> // derive public key when is itself public key
guard !hardened && index < (UInt32(1) << 31) else {
return nil // no derivation of hardened public key from extended public key
}

let hmac:Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512))
var inputForHMAC = Data()
inputForHMAC.append(self.publicKey)
inputForHMAC.append(index.serialize32())
guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil }
guard ent.count == 64 else { return nil }
entropy = ent

let I_L = entropy[0..<32]
let I_R = entropy[32..<64]
let cc = Data(I_R)
Expand All @@ -218,9 +223,9 @@ extension HDNode {
guard let tempKey = bn.serialize().setLengthLeft(32) else {return nil}
guard SECP256K1.verifyPrivateKey(privateKey: tempKey) else {return nil }
guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: tempKey, compressed: true) else {return nil}
guard pubKeyCandidate.bytes[0] == 0x02 || pubKeyCandidate.bytes[0] == 0x03 else {return nil}
guard pubKeyCandidate.bytes.first == 0x02 || pubKeyCandidate.bytes.first == 0x03 else {return nil}
guard let newPublicKey = SECP256K1.combineSerializedPublicKeys(keys: [self.publicKey, pubKeyCandidate], outputCompressed: true) else {return nil}
guard newPublicKey.bytes[0] == 0x02 || newPublicKey.bytes[0] == 0x03 else {return nil}
guard newPublicKey.bytes.first == 0x02 || newPublicKey.bytes.first == 0x03 else {return nil}
guard self.depth < UInt8.max else {return nil}
let newNode = HDNode()
newNode.chaincode = cc
Expand All @@ -233,17 +238,17 @@ extension HDNode {
newNode.parentFingerprint = fprint
var newPath = String()
if newNode.isHardened {
newPath = self.path! + "/"
newPath = (self.path ?? "") + "/"
newPath += String(newNode.index % HDNode.hardenedIndexPrefix) + "'"
} else {
newPath = self.path! + "/" + String(newNode.index)
newPath = (self.path ?? "") + "/" + String(newNode.index)
}
newNode.path = newPath
return newNode
}
}

public func derive (path: String, derivePrivateKey: Bool = true) -> HDNode? {
public func derive(path: String, derivePrivateKey: Bool = true) -> HDNode? {

let components = path.components(separatedBy: "/")
var currentNode: HDNode = self
var firstComponent = 0
Expand All @@ -262,19 +267,24 @@ extension HDNode {
return currentNode
}

public func serializeToString(serializePublic: Bool = true, version: HDversion = HDversion()) -> String? {
guard let data = self.serialize(serializePublic: serializePublic, version: version) else {return nil}
public func serializeToString(serializePublic: Bool = true) -> String? {
guard let data = self.serialize(serializePublic: serializePublic) else {return nil}
let encoded = Base58.base58FromBytes(data.bytes)
return encoded
}

public func serialize(serializePublic: Bool = true, version: HDversion = HDversion()) -> Data? {
public func serialize(serializePublic: Bool = true) -> Data? {

var data = Data()
if !serializePublic && !self.hasPrivate {return nil}

guard serializePublic || privateKey != nil else {
return nil
}

if serializePublic {
data.append(version.publicPrefix)
data.append(HDversion.publicPrefix!)
} else {
data.append(version.privatePrefix)
data.append(HDversion.privatePrefix!)
}
data.append(contentsOf: [self.depth])
data.append(self.parentFingerprint)
Expand Down
Loading

0 comments on commit efba907

Please sign in to comment.