1
|
|
|
<?php |
2
|
|
|
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; |
3
|
|
|
|
4
|
|
|
class Block extends Base { |
5
|
|
|
protected $table = 'blocks'; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Specific method to fetch the latest block found |
9
|
|
|
* @param none |
10
|
|
|
* @return data array Array with database fields as keys |
11
|
|
|
**/ |
12
|
|
|
public function getLast() { |
13
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height DESC LIMIT 1"); |
14
|
|
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) |
15
|
|
|
return $result->fetch_assoc(); |
16
|
|
|
return $this->sqlError(); |
|
|
|
|
17
|
|
|
} |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Specific method to fetch the latest block found that is VALID |
21
|
|
|
* @param none |
22
|
|
|
* @return data array Array with database fields as keys |
23
|
|
|
**/ |
24
|
|
|
public function getLastValid() { |
25
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE confirmations > -1 ORDER BY height DESC LIMIT 1"); |
26
|
|
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) |
27
|
|
|
return $result->fetch_assoc(); |
28
|
|
|
return $this->sqlError(); |
|
|
|
|
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Get a specific block, by block height |
33
|
|
|
* @param height int Block Height |
34
|
|
|
* @return data array Block information from DB |
35
|
|
|
**/ |
36
|
|
|
public function getBlock($height) { |
37
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1"); |
38
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result()) |
39
|
|
|
return $result->fetch_assoc(); |
40
|
|
|
return $this->sqlError(); |
|
|
|
|
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Get a specific block, by share_id |
45
|
|
|
* @param share_id int Blocks share_id |
46
|
|
|
* @return data array Block information from DB |
47
|
|
|
**/ |
48
|
|
|
public function getBlockByShareId($share_id) { |
49
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE share_id = ? LIMIT 1"); |
50
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $share_id) && $stmt->execute() && $result = $stmt->get_result()) |
51
|
|
|
return $result->fetch_assoc(); |
52
|
|
|
return $this->sqlError(); |
|
|
|
|
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Get a specific block, by id |
57
|
|
|
* @param share_id int Blocks share_id |
58
|
|
|
* @return data array Block information from DB |
59
|
|
|
**/ |
60
|
|
|
public function getBlockById($id) { |
61
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ? LIMIT 1"); |
62
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result()) |
63
|
|
|
return $result->fetch_assoc(); |
64
|
|
|
return $this->sqlError(); |
|
|
|
|
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Get our last, highest share ID inserted for a block |
69
|
|
|
* @param none |
70
|
|
|
* @return int data Share ID |
71
|
|
|
**/ |
72
|
|
|
public function getLastShareId() { |
73
|
|
|
$stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->table LIMIT 1"); |
74
|
|
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) |
75
|
|
|
return $result->fetch_object()->share_id; |
76
|
|
|
return $this->sqlError(); |
|
|
|
|
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Fetch all blocks without a share ID |
81
|
|
|
* @param order string Sort order, default ASC |
82
|
|
|
* @return data array Array with database fields as keys |
83
|
|
|
**/ |
84
|
|
|
public function getAllUnsetShareId($order='ASC') { |
85
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE ISNULL(share_id) ORDER BY height $order"); |
86
|
|
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) |
87
|
|
|
return $result->fetch_all(MYSQLI_ASSOC); |
88
|
|
|
return $this->sqlError(); |
|
|
|
|
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Fetch all unaccounted blocks |
93
|
|
|
* @param order string Sort order, default ASC |
94
|
|
|
* @return data array Array with database fields as keys |
95
|
|
|
**/ |
96
|
|
|
public function getAllUnaccounted($order='ASC') { |
97
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE accounted = 0 ORDER BY height $order"); |
98
|
|
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) |
99
|
|
|
return $result->fetch_all(MYSQLI_ASSOC); |
100
|
|
|
return $this->sqlError(); |
|
|
|
|
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Get total amount of blocks in our table |
105
|
|
|
* @param noone |
106
|
|
|
* @return data int Count of rows |
107
|
|
|
**/ |
108
|
|
|
public function getBlockCount() { |
109
|
|
|
$stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table"); |
110
|
|
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) |
111
|
|
|
return (int)$result->fetch_object()->blocks; |
112
|
|
|
return $this->sqlError(); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Fetch our average share count for the past N blocks |
117
|
|
|
* @param limit int Maximum blocks to check |
118
|
|
|
* @return data float Float value of average shares |
119
|
|
|
**/ |
120
|
|
|
public function getAvgBlockShares($height, $limit=1) { |
121
|
|
|
$stmt = $this->mysqli->prepare("SELECT AVG(x.shares) AS average FROM (SELECT shares FROM $this->table WHERE height <= ? ORDER BY height DESC LIMIT ?) AS x"); |
122
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $height, $limit) && $stmt->execute() && $result = $stmt->get_result()) |
123
|
|
|
return (float)$result->fetch_object()->average; |
124
|
|
|
return $this->sqlError(); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Fetch our average rewards for the past N blocks |
129
|
|
|
* @param limit int Maximum blocks to check |
130
|
|
|
* @return data float Float value of average shares |
131
|
|
|
**/ |
132
|
|
|
public function getAvgBlockReward($limit=1) { |
133
|
|
|
$stmt = $this->mysqli->prepare("SELECT AVG(x.amount) AS average FROM (SELECT amount FROM $this->table ORDER BY height DESC LIMIT ?) AS x"); |
134
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) |
135
|
|
|
return (float)$result->fetch_object()->average; |
136
|
|
|
return $this->sqlError(); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Fetch all unconfirmed blocks from table |
141
|
|
|
* @param confirmations int Required confirmations to consider block confirmed |
142
|
|
|
* @return data array Array with database fields as keys |
143
|
|
|
**/ |
144
|
|
|
public function getAllUnconfirmed($confirmations=120) { |
145
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE confirmations < ? AND confirmations > -1"); |
146
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param("i", $confirmations) && $stmt->execute() && $result = $stmt->get_result()) |
147
|
|
|
return $result->fetch_all(MYSQLI_ASSOC); |
148
|
|
|
return $this->sqlError(); |
|
|
|
|
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Update confirmations for an existing block |
153
|
|
|
* @param block_id int Block ID to update |
154
|
|
|
* @param confirmations int New confirmations value |
155
|
|
|
* @return bool |
156
|
|
|
**/ |
157
|
|
|
public function setConfirmations($block_id, $confirmations) { |
158
|
|
|
$stmt = $this->mysqli->prepare("UPDATE $this->table SET confirmations = ? WHERE id = ? LIMIT 1"); |
159
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $confirmations, $block_id) && $stmt->execute()) |
160
|
|
|
return true; |
161
|
|
|
return $this->sqlError(); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Fetch all blocks ordered by DESC height |
166
|
|
|
* @param order string ASC or DESC ordering |
167
|
|
|
* @return data array Array with database fields as keys |
168
|
|
|
**/ |
169
|
|
|
public function getAll($order='DESC') { |
170
|
|
|
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height $order"); |
171
|
|
|
if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) |
172
|
|
|
return $result->fetch_all(MYSQLI_ASSOC); |
173
|
|
|
return $this->sqlError(); |
|
|
|
|
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Add new new block to the database |
178
|
|
|
* @param block array Block data as an array, see bind_param |
179
|
|
|
* @return bool |
180
|
|
|
**/ |
181
|
|
|
public function addBlock($block) { |
182
|
|
|
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (height, blockhash, confirmations, amount, difficulty, time) VALUES (?, ?, ?, ?, ?, ?)"); |
183
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('isiddi', $block['height'], $block['blockhash'], $block['confirmations'], $block['amount'], $block['difficulty'], $block['time']) && $stmt->execute()) |
184
|
|
|
return true; |
185
|
|
|
return $this->sqlError(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Get our last inserted upstream ID from table |
190
|
|
|
* @param none |
191
|
|
|
* @return mixed upstream ID or 0, false on error |
192
|
|
|
**/ |
193
|
|
|
public function getLastUpstreamId() { |
194
|
|
|
$stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->table"); |
195
|
|
|
if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($share_id) && $stmt->fetch()) |
196
|
|
|
return $share_id ? $share_id : 0; |
|
|
|
|
197
|
|
|
return $this->sqlError(); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* Set finder of a block |
202
|
|
|
* @param block_id int Block ID |
203
|
|
|
* @param account_id int Account ID of finder |
204
|
|
|
* @return bool |
205
|
|
|
**/ |
206
|
|
|
public function setFinder($block_id, $account_id=NULL) { |
207
|
|
|
$field = array( 'name' => 'account_id', 'value' => $account_id, 'type' => 'i' ); |
208
|
|
|
return $this->updateSingle($block_id, $field); |
|
|
|
|
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Set finding worker of a block |
213
|
|
|
* @param block_id int Block ID |
214
|
|
|
* @param worker string Worker Name of finder |
215
|
|
|
* @return bool |
216
|
|
|
**/ |
217
|
|
|
public function setFindingWorker($block_id, $worker=NULL) { |
218
|
|
|
$field = array( 'name' => 'worker_name', 'value' => $worker, 'type' => 's' ); |
219
|
|
|
return $this->updateSingle($block_id, $field); |
|
|
|
|
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Set finding share for a block |
224
|
|
|
* @param block_id int Block ID |
225
|
|
|
* @param share_id int Upstream valid share ID |
226
|
|
|
* @return bool |
227
|
|
|
**/ |
228
|
|
|
public function setShareId($block_id, $share_id) { |
229
|
|
|
$field = array( 'name' => 'share_id', 'value' => $share_id, 'type' => 'i'); |
230
|
|
|
return $this->updateSingle($block_id, $field); |
|
|
|
|
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Set counted shares for a block |
235
|
|
|
* @param block_id int Block ID |
236
|
|
|
* @param shares int Share count |
237
|
|
|
* @return bool |
238
|
|
|
**/ |
239
|
|
|
public function setShares($block_id, $shares=NULL) { |
240
|
|
|
$field = array( 'name' => 'shares', 'value' => $shares, 'type' => 'd'); |
241
|
|
|
return $this->updateSingle($block_id, $field); |
|
|
|
|
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* Set block to be accounted for |
246
|
|
|
* @param block_id int Block ID |
247
|
|
|
* @return bool |
248
|
|
|
**/ |
249
|
|
|
public function setAccounted($block_id=NULL) { |
250
|
|
|
if (empty($block_id)) return false; |
251
|
|
|
$field = array( 'name' => 'accounted', 'value' => 1, 'type' => 'i'); |
252
|
|
|
return $this->updateSingle($block_id, $field); |
|
|
|
|
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Fetch the average amount of the past N blocks |
257
|
|
|
* @param limit int Block limit |
258
|
|
|
* @param return mixed Block array or false |
259
|
|
|
**/ |
260
|
|
|
public function getAverageAmount($limit=10) { |
261
|
|
|
$stmt = $this->mysqli->prepare("SELECT IFNULL(AVG(amount), " . $this->config['reward'] . ") as avg_amount FROM ( SELECT amount FROM $this->table ORDER BY id DESC LIMIT ?) AS t1"); |
|
|
|
|
262
|
|
|
if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) { |
263
|
|
|
return $result->fetch_object()->avg_amount; |
264
|
|
|
} else { |
265
|
|
|
$this->setErrorMessage('Failed to get average award from blocks'); |
266
|
|
|
return $this->sqlError(); |
267
|
|
|
} |
268
|
|
|
} |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
// Automatically load our class for furhter usage |
272
|
|
|
$block = new Block(); |
273
|
|
|
$block->setDebug($debug); |
274
|
|
|
$block->setMysql($mysqli); |
275
|
|
|
$block->setConfig($config); |
276
|
|
|
$block->setErrorCodes($aErrorCodes); |
277
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.