Share   F
last analyzed

Complexity

Total Complexity 108

Size/Duplication

Total Lines 384
Duplicated Lines 20.57 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 108
lcom 1
cbo 1
dl 79
loc 384
ccs 0
cts 268
cp 0
rs 2
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getArchiveTableName() 0 3 1
A getShareById() 0 3 1
B updateShareById() 3 25 7
A getLastInsertedShareId() 0 6 4
A getRoundShares() 0 12 5
A getSharesForAccounts() 0 18 5
A getMaxArchiveShareId() 0 6 4
B getArchiveShares() 0 24 7
B purgeArchive() 5 28 9
A moveArchive() 0 22 5
B deleteAccountedShares() 5 19 7
A setLastUpstreamId() 0 3 2
A getLastUpstreamId() 0 3 2
A getUpstreamFinder() 0 3 1
A getUpstreamWorker() 0 3 1
A getUpstreamShareId() 0 3 1
D findUpstreamShare() 35 76 36
A getMinimumShareId() 15 15 5
A getMinArchiveShareId() 16 16 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Share often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Share, and based on these observations, apply Extract Interface, too.

1
<?php
2
$defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1;
3
4
class Share Extends Base {
5
  protected $table = 'shares';
6
  protected $tableArchive = 'shares_archive';
7
  private $oUpstream;
8
  private $iLastUpstreamId;
9
  // This defines each share
10
  public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time, $difficulty;
11
12
  /**
13
   * Fetch archive tables name for this class
14
   * @param none
15
   * @return data string Table name
16
   **/
17
  public function getArchiveTableName() {
18
    return $this->tableArchive;
19
  }
20
21
  /**
22
   * Fetch a single share by ID
23
   * @param id int Share ID
24
   * @return array Share data
25
   **/
26
  public function getShareById($id) {
27
    return $this->getSingleAssoc($id);
28
  }
29
30
  /**
31
   * Update an entire shares data
32
   **/
33
  public function updateShareById($id, $data) {
34
    $this->debug->append("STA " . __METHOD__, 4);
35
    $sql = "UPDATE $this->table SET";
36
    $start = true;
37
    // Remove ID column
38
    unset($data['id']);
39
    foreach ($data as $column => $value) {
40
      $start == true ? $sql .= " $column = ? " : $sql .= ", $column = ?";
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
41
      $start = false;
42
      switch($column) {
43
      case 'difficulty':
44
        $this->addParam('d', $value);
45
        break;
46
      default:
47
        $this->addParam('s', $value);
48
        break;
49
      }
50
    }
51
    $sql .= " WHERE id = ? LIMIT 1";
52
    $this->addParam('i', $id);
53
    $stmt = $this->mysqli->prepare($sql);
54 View Code Duplication
    if ($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute())
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
55
      return true;
56
    return $this->sqlError();
57
  }
58
59
  /**
60
   * Get last inserted Share ID from Database
61
   * Used for PPS calculations without moving to archive
62
   **/
63
  public function getLastInsertedShareId() {
64
    $stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->table");
65
    if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
66
      return $result->fetch_object()->id;
67
    return $this->sqlError();
68
  }
69
70
  /**
71
   * Get all valid shares for this round
72
   * @param previous_upstream int Previous found share accepted by upstream to limit results
73
   * @param current_upstream int Current upstream accepted share
74
   * @return data int Total amount of counted shares
75
   **/
76
  public function getRoundShares($previous_upstream=0, $current_upstream) {
77
    $stmt = $this->mysqli->prepare("SELECT
78
      IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) AS total
0 ignored issues
show
Bug introduced by
The property config does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
79
      FROM $this->table AS s
80
      LEFT JOIN " . $this->user->getTableName() . " AS a
0 ignored issues
show
Bug introduced by
The property user does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
81
      ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
82
      WHERE s.id > ? AND s.id <= ? AND s.our_result = 'Y' AND a.is_locked != 2
83
      ");
84
    if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
85
      return $result->fetch_object()->total;
86
    return $this->sqlError();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->sqlError(); (boolean) is incompatible with the return type documented by Share::getRoundShares of type data.

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
87
  }
88
89
  /**
90
   * Fetch all shares grouped by accounts to count share per account
91
   * @param previous_upstream int Previous found share accepted by upstream to limit results
92
   * @param current_upstream int Current upstream accepted share
93
   * @param limit int Limit to this amount of shares for PPLNS
94
   * @return data array username, valid and invalid shares from account
95
   **/
96
  public function getSharesForAccounts($previous_upstream=0, $current_upstream) {
97
    $stmt = $this->mysqli->prepare("
98
      SELECT
99
        a.id,
100
        SUBSTRING_INDEX( s.username , '.', 1 ) as username,
101
        a.no_fees AS no_fees,
102
        IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) AS valid,
103
        IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) AS invalid
104
      FROM $this->table AS s
105
      LEFT JOIN " . $this->user->getTableName() . " AS a
106
      ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
107
      WHERE s.id > ? AND s.id <= ? AND a.is_locked != 2
108
      GROUP BY username DESC
109
      ");
110
    if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result())
111
      return $result->fetch_all(MYSQLI_ASSOC);
112
    return $this->sqlError();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->sqlError(); (boolean) is incompatible with the return type documented by Share::getSharesForAccounts of type data.

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
113
  }
114
115
  /**
116
   * Fetch the highest available share ID from archive
117
   **/
118
  function getMaxArchiveShareId() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
119
    $stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->tableArchive");
120
    if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result())
121
      return $result->fetch_object()->share_id;
122
    return $this->sqlError();
123
  }
124
125
  /**
126
   * We need a certain amount of valid archived shares
127
   * param left int Left/lowest share ID
128
   * param right int Right/highest share ID
129
   * return array data Returns an array with usernames as keys for easy access
130
   **/
131
  function getArchiveShares($iCount) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
132
    $iMaxId = $this->getMaxArchiveShareId();
133
    $iMinId = $this->getMinArchiveShareId($iCount);
134
    $stmt = $this->mysqli->prepare("
135
      SELECT
136
        a.id,
137
        SUBSTRING_INDEX( s.username , '.', 1 ) as account,
138
        a.no_fees AS no_fees,
139
        IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) AS valid,
140
        IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) AS invalid
141
      FROM $this->tableArchive AS s
142
      LEFT JOIN " . $this->user->getTableName() . " AS a
143
      ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
144
      WHERE s.share_id > ? AND s.share_id <= ? AND a.is_locked != 2
145
      GROUP BY account DESC");
146
    if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iMinId, $iMaxId) && $stmt->execute() && $result = $stmt->get_result()) {
147
      $aData = NULL;
148
      while ($row = $result->fetch_assoc()) {
149
        $aData[strtolower($row['account'])] = $row;
150
      }
151
      if (is_array($aData)) return $aData;
152
    }
153
    return $this->sqlError();
154
  }
155
156
  /**
157
   * We keep shares only up to a certain point
158
   * This can be configured by the user.
159
   * @return return bool true or false
160
   **/
161
  public function purgeArchive() {
162
    // Fallbacks if unset
163
    if (!isset($this->config['archive']['purge'])) $this->config['archive']['purge'] = 5;
164
165
    $stmt = $this->mysqli->prepare("SELECT CEIL(COUNT(id) / 100 * ?) AS count FROM $this->tableArchive");
166 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['purge']) && $stmt->execute() && $result = $stmt->get_result()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
      $limit = $result->fetch_object()->count;
168
    } else {
169
      return $this->sqlError();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->sqlError(); (boolean) is incompatible with the return type documented by Share::purgeArchive of type Return.

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
170
    }
171
    $stmt->close();
172
    $stmt = $this->mysqli->prepare("
173
      DELETE FROM $this->tableArchive WHERE time < (
174
        SELECT MIN(time) FROM (
175
          SELECT MIN(time) AS time
176
          FROM $this->tableArchive
177
          WHERE block_id = (
178
            SELECT MIN(id) AS minid FROM (
179
              SELECT id FROM " . $this->block->getTableName() . " ORDER BY height DESC LIMIT ?
0 ignored issues
show
Bug introduced by
The property block does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
180
            ) AS minheight
181
          ) UNION SELECT DATE_SUB(now(), INTERVAL ? MINUTE) AS time
182
        ) AS mintime
183
      ) LIMIT $limit
184
    ");
185
    if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['archive']['maxrounds'], $this->config['archive']['maxage']) && $stmt->execute())
186
      return $stmt->affected_rows;
187
    return $this->sqlError();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->sqlError(); (boolean) is incompatible with the return type documented by Share::purgeArchive of type Return.

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
188
  }
189
190
  /**
191
   * Move accounted shares to archive table, this step is optional
192
   * @param previous_upstream int Previous found share accepted by upstream to limit results
193
   * @param current_upstream int Current upstream accepted share
194
   * @param block_id int Block ID to assign shares to a specific block
195
   * @return bool
196
   **/
197
  public function moveArchive($current_upstream, $block_id, $previous_upstream=0) {
198
    if ($this->config['payout_system'] != 'pplns') {
199
      // We don't need archived shares that much, so only archive as much as configured
200
      $sql = "
201
        INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time, difficulty)
202
          SELECT id, username, our_result, upstream_result, ?, time, IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS difficulty
203
          FROM $this->table
204
          WHERE id > ? AND id <= ?
205
            AND time >= DATE_SUB(now(), INTERVAL " . $this->config['archive']['maxage'] . " MINUTE)";
206
    } else {
207
      // PPLNS needs archived shares for later rounds, so we have to copy them all
208
      $sql = "
209
        INSERT INTO $this->tableArchive (share_id, username, our_result, upstream_result, block_id, time, difficulty)
210
          SELECT id, username, our_result, upstream_result, ?, time, IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS difficulty
211
          FROM $this->table
212
          WHERE id > ? AND id <= ?";
213
    }
214
    $archive_stmt = $this->mysqli->prepare($sql);
215
    if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute())
216
      return true;
217
    return $this->sqlError();
218
  }
219
220
  /**
221
   * Delete accounted shares from shares table
222
   * @param current_upstream int Current highest upstream ID
223
   * @param previous_upstream int Previous upstream ID
224
   * @return bool true or false
225
   **/
226
  public function deleteAccountedShares($current_upstream, $previous_upstream=0) {
227
    // Fallbacks if unset
228
    if (!isset($this->config['purge']['shares'])) $this->config['purge']['shares'] = 25000;
229
    if (!isset($this->config['purge']['sleep'])) $this->config['purge']['sleep'] = 1;
230
231
    $affected = 1;
232
    while ($affected > 0) {
233
      // Sleep first to allow any IO to cleanup
234
      sleep($this->config['purge']['sleep']);
235
      $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ? ORDER BY id LIMIT " . $this->config['purge']['shares']);
236
      $start = microtime(true);
0 ignored issues
show
Unused Code introduced by
$start is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
237 View Code Duplication
      if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238
        $affected = $stmt->affected_rows;
239
      } else {
240
        return $this->sqlError();
241
      }
242
    }
243
    return true;
244
  }
245
246
  /**
247
   * Set/get last found share accepted by upstream: id and accounts
248
   **/
249
  public function setLastUpstreamId() {
250
    $this->iLastUpstreamId = @$this->oUpstream->id ? $this->oUpstream->id : 0;
251
  }
252
  public function getLastUpstreamId() {
253
    return @$this->iLastUpstreamId ? @$this->iLastUpstreamId : 0;
254
  }
255
  public function getUpstreamFinder() {
256
    return @$this->oUpstream->account;
257
  }
258
  public function getUpstreamWorker() {
259
    return @$this->oUpstream->worker;
260
  }
261
  public function getUpstreamShareId() {
262
    return @$this->oUpstream->id;
263
  }
264
  /**
265
   * Find upstream accepted share that should be valid for a specific block
266
   * Assumptions:
267
   *  * Shares are matching blocks in ASC order
268
   *  * We can skip all upstream shares of previously found ones used in a block
269
   * @param last int Skips all shares up to last to find new share
270
   * @return bool
271
   **/
272
  public function findUpstreamShare($aBlock, $last=0) {
273
    // Many use stratum, so we create our stratum check first
274
    $version = pack("I*", sprintf('%08d', $aBlock['version']));
275
    $previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash']));
276
    $merkleroot = pack("H*", swapEndian($aBlock['merkleroot']) );
277
    $time = pack("I*", $aBlock['time']);
278
    $bits = pack("H*", swapEndian($aBlock['bits']));
279
    $nonce = pack("I*", $aBlock['nonce']);
280
    $header_bin = $version .  $previousblockhash . $merkleroot . $time .  $bits . $nonce;
281
    $header_hex = implode(unpack("H*", $header_bin));
0 ignored issues
show
Unused Code introduced by
$header_hex is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
282
283
    // Stratum supported blockhash solution entry
284
    $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution = ? LIMIT 1");
285 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('s', $aBlock['hash']) && $stmt->execute() && $result = $stmt->get_result()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286
      $this->oUpstream = $result->fetch_object();
287
      $this->share_type = 'stratum_blockhash';
0 ignored issues
show
Bug introduced by
The property share_type does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
288
      if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
289
        return true;
290
    }
291
292
    // Stratum scrypt hash check
293
    $scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32)));
294
    $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution = ? LIMIT 1");
295 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
296
      $this->oUpstream = $result->fetch_object();
297
      $this->share_type = 'stratum_solution';
298
      if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
299
        return true;
300
    }
301
302
    // Failed to fetch via startum solution, try pushpoold
303
    // Fallback to pushpoold solution type
304
    $ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']);
305
    $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1");
306 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
307
      $this->oUpstream = $result->fetch_object();
308
      $this->share_type = 'pp_solution';
309
      if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
310
        return true;
311
    }
312
313
    // Still no match, try upstream result with timerange
314
    $stmt = $this->mysqli->prepare("
315
      SELECT
316
      SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id
317
      FROM $this->table
318
      WHERE upstream_result = 'Y'
319
      AND id > ?
320
      AND UNIX_TIMESTAMP(time) >= ?
321
      AND UNIX_TIMESTAMP(time) <= ( ? + 60 )
322
      ORDER BY id ASC LIMIT 1");
323 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
324
      $this->oUpstream = $result->fetch_object();
325
      $this->share_type = 'upstream_share';
326
      if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
327
        return true;
328
    }
329
330
    // We failed again, now we take ANY result matching the timestamp
331
    $stmt = $this->mysqli->prepare("
332
      SELECT
333
      SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id
334
      FROM $this->table
335
      WHERE our_result = 'Y'
336
      AND id > ?
337
      AND UNIX_TIMESTAMP(time) >= ?
338
      ORDER BY id ASC LIMIT 1");
339 View Code Duplication
    if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
340
      $this->oUpstream = $result->fetch_object();
341
      $this->share_type = 'any_share';
342
      if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id))
343
        return true;
344
    }
345
    $this->setErrorMessage($this->getErrorMsg('E0052', $aBlock['height']));
346
    return false;
347
  }
348
349
  /**
350
   * Fetch the lowest needed share ID from shares
351
   **/
352 View Code Duplication
  function getMinimumShareId($iCount, $current_upstream) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
353
    $stmt = $this->mysqli->prepare("
354
      SELECT MIN(b.id) AS id FROM
355
      (
356
        SELECT id, @total := @total + IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS total
357
        FROM $this->table, (SELECT @total := 0) AS a
358
        WHERE our_result = 'Y'
359
        AND id <= ? AND @total < ?
360
        ORDER BY id DESC
361
      ) AS b
362
      WHERE total <= ?");
363
    if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $current_upstream, $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
364
      return $result->fetch_object()->id;
365
    return $this->sqlError();
366
  }
367
368
  /**
369
   * Fetch the lowest needed share ID from archive
370
   **/
371 View Code Duplication
  function getMinArchiveShareId($iCount) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
    $stmt = $this->mysqli->prepare("
373
      SELECT MIN(b.share_id) AS share_id FROM
374
      (
375
        SELECT share_id, @total := @total + IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS total
376
        FROM $this->tableArchive, (SELECT @total := 0) AS a
377
        WHERE our_result = 'Y'
378
        AND @total < ?
379
        ORDER BY share_id DESC
380
      ) AS b
381
      WHERE total <= ?
382
      ");
383
    if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result())
384
      return $result->fetch_object()->share_id;
385
    return $this->sqlError();
386
  }
387
}
388
389
$share = new Share();
390
$share->setDebug($debug);
391
$share->setMysql($mysqli);
392
$share->setConfig($config);
393
$share->setUser($user);
394
$share->setBlock($block);
395
$share->setErrorCodes($aErrorCodes);
396