|
| 1 | +[PHPCoin Docs](../) > [ePoW](./) |
| 2 | + |
| 3 | + |
| 4 | +--- |
| 5 | + |
| 6 | +# ePoW (Elapsed Proof of Work) |
| 7 | + |
| 8 | +## Table of Contents |
| 9 | +- [A Simple Explanation](#a-simple-explanation) |
| 10 | +- [Technical Workflow](#technical-workflow) |
| 11 | +- [Code Examples and Source Files](#code-examples-and-source-files) |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## A Simple Explanation |
| 16 | + |
| 17 | +ePoW, or Elapsed Proof of Work, is the consensus mechanism that keeps the phpcoin network secure and fair. If you're familiar with Bitcoin's Proof of Work (PoW), you'll find ePoW has some interesting differences. |
| 18 | + |
| 19 | +In a traditional PoW system, miners are given a single puzzle for each new block, and its difficulty is static while they work on it. |
| 20 | + |
| 21 | +The defining feature of ePoW is completely different: the puzzle gets easier to solve with every passing second. |
| 22 | + |
| 23 | +**Here's the core idea:** |
| 24 | + |
| 25 | +To use an analogy: |
| 26 | +* **Traditional PoW** gives miners one single, very hard puzzle to solve. |
| 27 | +* **phpcoin's ePoW** gives miners a new puzzle every second, with each new puzzle being a little bit easier than the last. |
| 28 | + |
| 29 | +This unique mechanism is driven by the "elapsed time" since the last block was found. As miners work, this elapsed time increases, and for each second that passes, the mining target becomes easier to hit. This means the longer a block goes unsolved, the easier it becomes to solve. |
| 30 | + |
| 31 | +This has two key effects: |
| 32 | +* **Stabilizes Block Time:** It ensures that blocks are found, on average, every 60 seconds. If a block isn't found quickly, the constantly decreasing difficulty makes it more and more likely to be found as time goes on. |
| 33 | +* **Fairness:** It reduces the advantage of large mining pools. The difficulty adjusts so rapidly that it creates a more level playing field for all miners on the network. |
| 34 | + |
| 35 | +In short, ePoW is a more responsive version of PoW that uses the "elapsed time" between blocks to regulate the network's block time and difficulty. This helps to maintain a fair and stable environment for all participants in the phpcoin network. |
| 36 | + |
| 37 | +<!-- Diagram placeholder: An SVG diagram illustrating the relationship between elapsed time and mining difficulty --> |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +## Technical Workflow |
| 42 | + |
| 43 | +The ePoW consensus mechanism is a process that involves miners and the node working together to create and validate new blocks. Here's a step-by-step breakdown of the technical workflow. |
| 44 | + |
| 45 | +### 1. Block Creation and Mining Information |
| 46 | + |
| 47 | +When a miner is ready to mine a new block, they first request mining information from a phpcoin node. The node provides the following critical data: |
| 48 | + |
| 49 | +* The `height` of the new block. |
| 50 | +* The `difficulty` for the current block. |
| 51 | +* The `date` (timestamp) of the previous block. |
| 52 | + |
| 53 | +### 2. The Role of "Elapsed Time" |
| 54 | + |
| 55 | +The core of ePoW is the concept of "elapsed time." This is the time, in seconds, that has passed since the previous block was mined. The miner calculates this value continuously as they are working on a new block. |
| 56 | + |
| 57 | +**Pseudo-code:** |
| 58 | +``` |
| 59 | +previous_block_date = node.get_latest_block().date |
| 60 | +current_timestamp = get_current_time() |
| 61 | +elapsed_time = current_timestamp - previous_block_date |
| 62 | +``` |
| 63 | + |
| 64 | +The `elapsed_time` is crucial because it directly influences the mining `target`. |
| 65 | + |
| 66 | +### 3. Calculating the Mining Target |
| 67 | + |
| 68 | +In phpcoin, a **higher** `hit` value is better. To find a valid block, a miner's `hit` must be greater than the `target`. The `target` is dynamically calculated using the `difficulty` and the `elapsed` time. |
| 69 | + |
| 70 | +**Pseudo-code:** |
| 71 | +``` |
| 72 | +target = (difficulty * BLOCK_TIME) / elapsed_time |
| 73 | +``` |
| 74 | + |
| 75 | +* `BLOCK_TIME`: A constant, which is 60 seconds in phpcoin. |
| 76 | +* `difficulty`: The difficulty of the current block. |
| 77 | +* `elapsed_time`: The time passed since the last block. |
| 78 | + |
| 79 | +This formula means that as `elapsed_time` increases, the `target` decreases, making it easier to find a valid block. Conversely, if a block is found quickly, `elapsed_time` is small, the `target` is high, and it's harder to find a valid block. |
| 80 | + |
| 81 | +### 4. Hashing and Finding a Valid Block |
| 82 | + |
| 83 | +Unlike traditional Proof of Work where miners search for a random `nonce`, in ePoW, the mining process is an iteration over the **`elapsed` time**. The `nonce` is calculated deterministically, not found. |
| 84 | + |
| 85 | +Here is the mining loop: |
| 86 | + |
| 87 | +1. For each second that passes, the miner increments the `elapsed_time` value. |
| 88 | +2. A new **Argon2id hash** is calculated using the `previous_block_date` and the new `elapsed_time`. Argon2id is a memory-hard hashing algorithm, making it resistant to ASIC miners. |
| 89 | +3. A deterministic **`nonce`** is then calculated using a SHA-256 hash of the miner's address, the `elapsed_time`, and the new Argon2 hash. There is no randomness here; for the same inputs, the output `nonce` is always the same. |
| 90 | +4. The miner calculates the `hit` value. |
| 91 | +5. The miner recalculates the `target`, which changes with each increment of `elapsed_time`. |
| 92 | +6. If `hit > target`, a valid block has been found. The miner can then submit the block with the successful `elapsed_time`, the calculated `argon` hash, and the deterministic `nonce`. |
| 93 | + |
| 94 | +**Pseudo-code:** |
| 95 | +``` |
| 96 | +previous_block_date = node.get_latest_block().date |
| 97 | +
|
| 98 | +while (true): |
| 99 | + current_timestamp = get_current_time() |
| 100 | + elapsed_time = current_timestamp - previous_block_date |
| 101 | +
|
| 102 | + // Recalculate target for the current elapsed_time |
| 103 | + target = (difficulty * BLOCK_TIME) / elapsed_time |
| 104 | +
|
| 105 | + // Calculate hashes deterministically |
| 106 | + argon_hash = argon2("previous_block_date-elapsed_time") |
| 107 | + nonce = sha256("miner_address-previous_block_date-elapsed_time-argon_hash") |
| 108 | +
|
| 109 | + hit = calculate_hit(miner_address, nonce, block_height, difficulty) |
| 110 | +
|
| 111 | + if (hit > target): |
| 112 | + // Block found! |
| 113 | + submit_block(nonce, argon_hash, elapsed_time) |
| 114 | + break |
| 115 | +
|
| 116 | + // Wait for the next second to increment elapsed_time |
| 117 | + wait(1_second) |
| 118 | +``` |
| 119 | + |
| 120 | +### 5. Submitting a Block |
| 121 | + |
| 122 | +Once a miner finds a valid `nonce` and `argon` hash, they submit them along with the `elapsed` time and their address to the node. This is typically done via an API call to `mine.php`. |
| 123 | + |
| 124 | +### 6. Block Validation by the Node |
| 125 | + |
| 126 | +The node receives the submitted block data and performs a series of checks to validate it: |
| 127 | + |
| 128 | +1. **Verify Elapsed Time**: The node checks if the `elapsed` time is valid (i.e., greater than zero). |
| 129 | +2. **Verify Argon Hash**: The node recalculates the Argon2 hash using the previous block's date and the submitted `elapsed` time. It then compares this to the `argon` hash submitted by the miner. |
| 130 | +3. **Verify Nonce**: The node recalculates the `nonce` using the miner's address, the previous block's date, the `elapsed` time, and the submitted `argon` hash. It checks if this matches the `nonce` sent by the miner. |
| 131 | +4. **Verify Hit and Target**: The node calculates the `hit` using the submitted data and recalculates the `target` using the `elapsed` time. It then verifies that `hit > target`. |
| 132 | + |
| 133 | +If all of these checks pass, the node considers the block valid, adds it to the blockchain, and propagates it to other peers on the network. |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +## Code Examples and Source Files |
| 138 | + |
| 139 | +Here are some relevant code snippets from the phpcoin source code that illustrate the ePoW mechanism. |
| 140 | + |
| 141 | +### Calculating the Target |
| 142 | + |
| 143 | +This function from `include/class/Block.php` shows how the target is calculated based on the `elapsed` time and `difficulty`. |
| 144 | + |
| 145 | +```php |
| 146 | +function calculateTarget($elapsed) { |
| 147 | + global $_config; |
| 148 | + if($elapsed == 0) { |
| 149 | + return 0; |
| 150 | + } |
| 151 | + $target = gmp_div(gmp_mul($this->difficulty , BLOCK_TIME), $elapsed); |
| 152 | + if($target == 0 && DEVELOPMENT) { |
| 153 | + $target = 1; |
| 154 | + } |
| 155 | + if($target > 100 && DEVELOPMENT) { |
| 156 | + $target = 100; |
| 157 | + } |
| 158 | + return $target; |
| 159 | +} |
| 160 | +``` |
| 161 | +[Link to `include/class/Block.php`](../../include/class/Block.php) |
| 162 | + |
| 163 | +### Calculating the Nonce and Argon Hash |
| 164 | + |
| 165 | +These functions from `include/class/Block.php` are used to calculate and verify the `nonce` and `argon` hash. |
| 166 | + |
| 167 | +```php |
| 168 | +function calculateNonce($prev_block_date, $elapsed, $chain_id = CHAIN_ID) { |
| 169 | + $nonceBase = "{$chain_id}{$this->miner}-{$prev_block_date}-{$elapsed}-{$this->argon}"; |
| 170 | + $calcNonce = hash("sha256", $nonceBase); |
| 171 | + _log("calculateNonce nonceBase=$nonceBase argon={$this->argon} calcNonce=$calcNonce", 5); |
| 172 | + return $calcNonce; |
| 173 | +} |
| 174 | + |
| 175 | +function calculateArgonHash($prev_block_date, $elapsed) { |
| 176 | + $base = "{$prev_block_date}-{$elapsed}"; |
| 177 | + $options = self::hashingOptions($this->height); |
| 178 | + if($this->height < UPDATE_3_ARGON_HARD) { |
| 179 | + $options['salt']=substr($this->miner, 0, 16); |
| 180 | + } |
| 181 | + $argon = @password_hash( |
| 182 | + $base, |
| 183 | + HASHING_ALGO, |
| 184 | + $options |
| 185 | + ); |
| 186 | + return $argon; |
| 187 | +} |
| 188 | +``` |
| 189 | +[Link to `include/class/Block.php`](../../include/class/Block.php) |
| 190 | + |
| 191 | +### Block Submission |
| 192 | + |
| 193 | +When a miner submits a block, the `web/mine.php` file handles the request. This snippet shows how the submitted data is received and used to create a new `Block` object. |
| 194 | + |
| 195 | +```php |
| 196 | +$nonce = san($_POST['nonce']); |
| 197 | +$version = Block::versionCode($height); |
| 198 | +$address = san($_POST['address']); |
| 199 | +$elapsed = intval($_POST['elapsed']); |
| 200 | +$difficulty = san($_POST['difficulty']); |
| 201 | +$argon = $_POST['argon']; |
| 202 | + |
| 203 | +// ... |
| 204 | + |
| 205 | +$block = new Block($generator, $address, $height, $date, $nonce, $data, $difficulty, $version, $argon, $prev_block['id']); |
| 206 | +``` |
| 207 | +[Link to `web/mine.php`](../../web/mine.php) |
| 208 | + |
| 209 | +### Block Validation (Mine Check) |
| 210 | + |
| 211 | +The `mine()` function in `include/class/Block.php` is where the core validation logic for a new block resides. This snippet shows the checks for the `argon` hash, `nonce`, `hit`, and `target`. |
| 212 | + |
| 213 | +```php |
| 214 | +public function mine(&$err=null) |
| 215 | +{ |
| 216 | + // ... |
| 217 | + if(!$this->verifyArgon($prev_date, $elapsed)) { |
| 218 | + throw new Exception("Invalid argon={$this->argon}"); |
| 219 | + } |
| 220 | + |
| 221 | + $calcNonce = $this->calculateNonce($prev_date, $elapsed); |
| 222 | + // ... (nonce comparison logic) |
| 223 | + |
| 224 | + $hit = $this->calculateHit(); |
| 225 | + $target = $this->calculateTarget($elapsed); |
| 226 | + $res = $this->checkHit($hit, $target, $this->height); |
| 227 | + if(!$res && $this->height > UPDATE_3_ARGON_HARD) { |
| 228 | + throw new Exception("invalid hit or target"); |
| 229 | + } |
| 230 | + |
| 231 | + return true; |
| 232 | +} |
| 233 | +``` |
| 234 | +[Link to `include/class/Block.php`](../../include/class/Block.php) |
0 commit comments