-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTransactionScript.swift
More file actions
164 lines (119 loc) · 4.65 KB
/
Copy pathTransactionScript.swift
File metadata and controls
164 lines (119 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//
// Script.swift
// CryptoCoin
//
// Created by Sjors Provoost on 11-08-14.
//
import Foundation
import RIPEMD
import ECurve
import UInt256
public protocol TransactionScriptItem {}
public protocol TransactionStackItem {}
extension Int : TransactionScriptItem, TransactionStackItem {}
extension Bool : TransactionStackItem {}
extension NSData : TransactionScriptItem, TransactionStackItem {}
public enum Op : TransactionScriptItem {
case Add
case Equal
case Dup
case Hash160
case CheckSig
}
public struct TransactionScript {
struct Stack {
var stack: [TransactionStackItem] = []
mutating func popTwoIntegers () -> (Int , Int) {
assert(stack.count >= 2, "Not enough stuff on the stack")
let a = stack.removeLast()
let b = stack.removeLast()
assert(a is Int && b is Int, "Looking for two integers")
return (a as Int, b as Int)
}
mutating func push (item: TransactionStackItem) {
stack.append(item)
}
mutating func pop () -> (TransactionStackItem) {
return stack.removeLast()
}
var top: TransactionStackItem {
assert(stack.count >= 1, "Not enough stuff on the stack")
return stack.first!
}
var count: Int {
return stack.count
}
}
let items: [TransactionScriptItem]
public init (_ items: [TransactionScriptItem]) {
self.items = items
}
public func evaluate() -> Bool {
var stack = Stack()
for item in items {
if item is Int {
stack.push(item as Int)
} else if item is NSData {
stack.push(item as NSData)
} else if item is Op {
switch item as Op {
case .Add:
let (a,b) = stack.popTwoIntegers()
stack.push(a + b)
case .Dup:
stack.push(stack.top)
case .Equal:
let a = stack.pop()
let b = stack.pop()
if a is Int && b is Int {
stack.push(a as Int == b as Int)
} else if a is NSData && b is NSData {
stack.push((a as NSData).isEqualToData(b as NSData))
} else {
assert(false, "Invalid script")
}
case .Hash160:
let message = stack.pop()
if message is NSData {
let digest: NSData = RIPEMD.digest(message as NSData)
stack.push(digest)
} else {
assert(false, "Invalid script")
return false
}
case .CheckSig:
let pubKey = stack.pop()
let signature = stack.pop()
if pubKey is NSData && signature is NSData {
let curve = ECurve(domain: .Secp256k1)
let P = curve.point(pubKey as NSData)
if let theP = P {
let (r,s) = ECKey.importDer(signature as NSData)
let digest: UInt256 = 0x32 // TODO: use transaction
// This line crashes the compiler with a segfault. This workaround fixes the crash, but unless you use -O instead of -Ounchecked, you'll get segfaults in other places.
let result: Bool = true // ECKey.verifySignature(digest, r: r, s: s, publicKey: theP)
stack.push(result)
} else {
assert(false, "Failed import")
}
} else {
assert(false, "Invalid script")
return false
}
default:
assert(false, "Invalid script")
}
} else {
assert(false, "Invalid script")
return false
}
}
if stack.count == 1 {
let item = stack.pop()
if item is Bool {
return (item as Bool)
}
}
return false
}
}