@@ -6,25 +6,46 @@ public typealias Signature = [UInt8]
66public typealias NetworkID = BigInt
77public typealias SubnetID = BigInt
88
9+ public enum BlockSignature : Hashable {
10+ case single( Signature )
11+ case multi( [ Signature ] )
12+
13+ func toHexString( ) -> String {
14+ switch self {
15+ case . single( let signature) : signature. toHexString ( )
16+ case . multi( let signatures) : " Multi-signatures not implemented "
17+ }
18+ }
19+ }
20+
921public struct Block {
10- public typealias Version = Int
22+ public typealias Signature = BlockSignature
1123
1224 public let rawData : RawBlockData
1325 public let opening : Bool
1426 public let hash : String
1527 public let signature : Signature
1628
17- static let asn1Schema : Schema = . sequence( [
18- " version " : . integer( ) ,
19- " network " : . integer( ) ,
20- " subnet " : . choiceOf( [ . integer( ) , . null] ) ,
21- " date " : . time( kind: . generalized) ,
22- " signer " : . octetString( ) ,
23- " account " : . choiceOf( [ . octetString( ) , . null] ) ,
24- " previous " : . octetString( ) ,
25- " operations " : . sequenceOf( . any) ,
26- " signature " : . integer( )
27- ] )
29+ public enum Version : Int , CaseIterable {
30+ case v1
31+ case v2
32+
33+ public var value : BigInt { BigInt ( rawValue) }
34+ public var tag : UInt8 { UInt8 ( rawValue) }
35+ public static var all : [ Self ] { allCases }
36+ public static var latest : Self { all. last! }
37+
38+ public static func > ( lhs: Version , rhs: Version ) -> Bool {
39+ lhs. rawValue > rhs. rawValue
40+ }
41+ }
42+
43+ public enum Purpose : Int {
44+ case generic
45+ case fee
46+
47+ public var value : BigInt { BigInt ( rawValue) }
48+ }
2849
2950 public static func accountOpeningHash( for account: Account ) throws -> String {
3051 let publicKeyBytes = try account. keyPair. publicKey. toBytes ( )
@@ -37,13 +58,18 @@ public struct Block {
3758
3859 let verifiedSignature : Signature
3960 if let signature = signature {
40- let verified = try rawBlock. signer. verify ( data: Data ( hashBytes) , signature: signature)
41- guard verified else {
42- throw BlockError . invalidSignature
61+ switch signature {
62+ case . single( let signature) :
63+ let verified = try rawBlock. signer. verify ( data: Data ( hashBytes) , signature: signature)
64+ guard verified else {
65+ throw BlockError . invalidSignature
66+ }
67+ verifiedSignature = . single( signature)
68+ case . multi:
69+ throw NSError ( domain: " Multi-signatures not implemented " , code: 0 )
4370 }
44- verifiedSignature = signature
4571 } else {
46- verifiedSignature = try rawBlock. signer. sign ( data: Data ( hashBytes) )
72+ verifiedSignature = . single ( try rawBlock. signer. sign ( data: Data ( hashBytes) ) )
4773 }
4874
4975 self . rawData = rawBlock
@@ -55,17 +81,82 @@ public struct Block {
5581 public init ( from data: Data ) throws {
5682 let asn1 = try ASN1Serialization . asn1 ( fromDER: data)
5783
58- guard let sequence = asn1. first? . sequenceValue else {
59- throw BlockError . invalidASN1Sequence
84+ let data : [ ASN1 ]
85+ let version : Block . Version
86+
87+ if let sequence = asn1. first? . sequenceValue {
88+ data = sequence
89+
90+ guard let rawVersion = data [ 0 ] . integerValue,
91+ let versionValue = Block . Version ( rawValue: Int ( rawVersion) ) ,
92+ versionValue == . v1 else {
93+ throw BlockError . invalidVersion
94+ }
95+ version = versionValue
96+ } else if let tagged = asn1. first? . taggedValue {
97+ let asn1 = try ASN1Serialization . asn1 ( fromDER: tagged. data)
98+ guard let sequence = asn1. first? . sequenceValue else {
99+ throw BlockError . invalidASN1Sequence
100+ }
101+ data = sequence
102+
103+ guard let rawVersion = tagged. contextSpecificTag,
104+ let versionValue = Block . Version ( rawValue: Int ( rawVersion) ) ,
105+ versionValue > . v1 else {
106+ throw BlockError . invalidVersion
107+ }
108+ version = versionValue
109+ } else {
110+ throw BlockError . invalidASN1Schema
60111 }
61- guard sequence. count == 9 else {
112+
113+ guard data. count == 8 || data. count == 9 else {
62114 throw BlockError . invalidASN1SequenceLength
63115 }
64116
65- guard let version = sequence [ 0 ] . integerValue else {
66- throw BlockError . invalidVersion
117+ let rawBlock : RawBlockData
118+ let signature : Signature
119+ let opening : Bool
120+
121+ switch version {
122+ case . v1: ( rawBlock, signature, opening) = try Self . blockDataV1 ( for: data)
123+ case . v2: ( rawBlock, signature, opening) = try Self . blockDataV2 ( for: data)
67124 }
68125
126+ self . rawData = rawBlock
127+ self . opening = opening
128+ self . hash = try rawBlock. hash ( )
129+ self . signature = signature
130+ }
131+
132+ public func toAsn1( ) throws -> [ ASN1 ] {
133+ let rawASN1 = try rawData. asn1Values ( )
134+
135+ return switch signature {
136+ case . single( let signature) :
137+ rawASN1 + [ . octetString( . init( signature) ) ]
138+ case . multi( let signatures) :
139+ rawASN1 + signatures. map { . octetString( . init( $0) ) }
140+ }
141+ }
142+
143+ public func toData( ) throws -> Data {
144+ switch rawData. version {
145+ case . v1:
146+ return try toAsn1 ( ) . toData ( )
147+ case . v2:
148+ let tag = try TaggedValue . contextSpecific ( tag: rawData. version. tag, try toAsn1 ( ) )
149+ return try tag. toData ( )
150+ }
151+ }
152+
153+ public func base64String( ) throws -> String {
154+ try toData ( ) . base64EncodedString ( )
155+ }
156+
157+ // MARK: Helper
158+
159+ private static func blockDataV1( for sequence: [ ASN1 ] ) throws -> ( RawBlockData , Signature , Bool ) {
69160 guard let network = sequence [ 1 ] . integerValue else {
70161 throw BlockError . invalidNetwork
71162 }
@@ -108,7 +199,8 @@ public struct Block {
108199 let opening = previousHash == account. publicKeyString
109200
110201 let rawBlock = RawBlockData (
111- version: Int ( version) + 1 ,
202+ version: . v1,
203+ purpose: . generic,
112204 previous: previousHash,
113205 network: network,
114206 subnet: subnet,
@@ -118,22 +210,74 @@ public struct Block {
118210 created: anyTime. zonedDate. utcDate
119211 )
120212
121- self . rawData = rawBlock
122- self . opening = opening
123- self . hash = try rawBlock. hash ( )
124- self . signature = signature. bytes
125- }
126-
127- public func toAsn1( ) throws -> [ ASN1 ] {
128- let rawASN1 = try rawData. asn1Values ( )
129- return rawASN1 + [ . octetString( . init( signature) ) ]
130- }
131-
132- public func toData( ) throws -> Data {
133- try toAsn1 ( ) . toData ( )
213+ return ( rawBlock, . single( signature. bytes) , opening)
134214 }
135215
136- public func base64String( ) throws -> String {
137- try toData ( ) . base64EncodedString ( )
216+ private static func blockDataV2( for sequence: [ ASN1 ] ) throws -> ( RawBlockData , Signature , Bool ) {
217+ guard let network = sequence [ 0 ] . integerValue else {
218+ throw BlockError . invalidNetwork
219+ }
220+ let subnet = sequence [ 1 ] . integerValue
221+
222+ let offset = subnet != nil ? 0 : 1
223+
224+ guard let anyTime = sequence [ 2 - offset] . generalizedTimeValue else {
225+ throw BlockError . invalidDate
226+ }
227+ guard let purposeRaw = sequence [ 3 - offset] . integerValue,
228+ let purpose = Block . Purpose ( rawValue: Int ( purposeRaw) ) else {
229+ throw BlockError . invalidPurpose
230+ }
231+
232+ guard let accountData = sequence [ 4 - offset] . octetStringValue else {
233+ throw BlockError . invalidSigner
234+ }
235+ let account = try Account ( data: accountData)
236+
237+ let signerContainer = sequence [ 5 - offset]
238+ let signer : Account
239+ if signerContainer. isNull {
240+ signer = account
241+ } else if let signerData = signerContainer. octetStringValue {
242+ signer = try Account ( data: signerData)
243+ } else {
244+ // TODO: implement 'this.signer = parseBlockSignerFieldContainer(signersContainer).parsed;'
245+ throw NSError ( domain: " Multi-signatures not implemented " , code: 0 )
246+ }
247+
248+ guard let previousHashData = sequence [ 6 - offset] . octetStringValue else {
249+ throw BlockError . invalidHash
250+ }
251+ let previousHash = previousHashData. toHexString ( )
252+
253+ guard let operationsSequence = sequence [ 7 - offset] . sequenceValue else {
254+ throw BlockError . invalidOperationsSequence
255+ }
256+ let operations = try operationsSequence. map { try BlockOperationBuilder . create ( from: $0) }
257+
258+ let signatureContainer = sequence [ 8 - offset]
259+ let signature : Signature
260+ if let signatureValue = signatureContainer. octetStringValue {
261+ signature = . single( signatureValue. bytes)
262+ } else {
263+ // TODO: implement 'assertBlockSignatureField(signatureContainer);'
264+ throw BlockError . invalidSignature
265+ }
266+
267+ let opening = previousHash == account. publicKeyString
268+
269+ let rawBlock = RawBlockData (
270+ version: . v2,
271+ purpose: purpose,
272+ previous: previousHash,
273+ network: network,
274+ subnet: subnet,
275+ signer: signer,
276+ account: account,
277+ operations: operations,
278+ created: anyTime. zonedDate. utcDate
279+ )
280+
281+ return ( rawBlock, signature, opening)
138282 }
139283}
0 commit comments