if ($forwardedFromRoot != 0) { // Some other root forwarded the request here. Just return my challenge: echo $myChallenge; exit; } // Forward this request to other root nodes, stating that this root node is leading it. // Custom forward call here because domain is almost always given over GET. // I.e. php://input isn't being set to what we want it to be. // pHeader (2nd parameter of sendToRoot) is the same string as seen inside the forward() function. // Base64 the payload: $payload = base64_encode('{"domain":"' . $domain . '"}'); // Forward now! $results = sendToRoot($payload, 'eyJmd2QiOjF9'); // See above about this base64 text // Results is now a set of successful responses. Each contains the signature // of the challenge for a given endpoint and the public key. // Verify each signature (except our own - we pass that in instead) to test if we have a majority. // If we do, forward the whole set to complete the creation of a new entity! $fullSet = testMajority($results, $hexPublicKey, $mySignature); // Majority formed! - send it to the root so everyone can verify all the signatures too. // Add the domain to it: $fullSet = '{"domain":"' . $domain . '","challenges":' . $fullSet . '}'; // For info on the base64 string in the middle, see the forward() function. sendToRoot(base64_encode($fullSet), 'eyJmd2QiOjF9', false, 'entity/challenge/success'); // Remove from pending: $dz->query('delete from `Root.Entities.Pending` where `Endpoint`="' . $domain . '"'); // Transfer the entity to being an official entity: $dz->query('insert into `Root.Entities` (`Key`,`Endpoint`,`Type`,`Group`,`Name`,`Country`) values (unhex("' . $hexPublicKey . '"),"' . $domain . '",' . $row['Type'] . ',' . $thisEntity['Group'] . ',"' . $row['Name'] . '",' . $row['Country'] . ')'); // Create the change event too: changed('entity', array('key' => $hexPublicKey, 'type' => $row['Type'], 'endpoint' => $domain, 'group' => $thisEntity['Group'], 'name' => $row['Name'], 'country' => $row['Country'])); echo '{"entity":"' . $domain . '"}';
// A root node collected all those signatures and then sent the whole lot to every root node. // This allows them to independently form a proof of majority. // In this case, when the majority is successful, an issue occurs. // Always JSON posted here: postedTo(); if ($forwardedFromRoot == 0) { // Must have been forwarded by a root node. error('entity/notroot'); } // Get the challenges: // We want it as-is (it's an array) $challenges = safe('challenges', VALID_ARRAY); // Amount must be a positive non-zero number: $amount = safe('amount', VALID_NUMBER); $amount = (int) $amount; if ($amount == 0) { // They specified 0 - this isn't valid: error('field/invalid', 'amount'); } // Get the address to issue into: $hexAddress = safe('address', VALID_HEX); // Test for a majority. This time we check all signatures including our own // because the whole set came from somewhere else (we're not leading the majority process here). testMajority($challenges, $hexAddress . $amount); // Majority formed! // Note: From the previous test a few ms ago, we know the balance exists and is of the correct commodity. // Because of the locking process, we know that the balance can't have been emptied and deleted. // Update the amount now! $dz->query('update `Root.Balances` set Balance=Balance+' . $amount . ',LockedBalance=LockedBalance-' . $amount . ' where `Key`=UNHEX("' . $hexAddress . '")'); // Create an issue record (occurs in issue/success as well): changed('issue', array('amount' => $amount, 'to' => $hexAddress, 'tag' => $tag));
// In this case, when the majority is successful, an entity is created. // Always JSON posted here: postedTo(); if ($forwardedFromRoot == 0) { // Must have been forwarded by a root node. error('entity/notroot'); } // Domain must be a valid domain name only: $domain = safe('domain', VALID_DOMAIN); // Get the challenges: $challenges = safe('challenges', VALID_ARRAY); // Get the pending request: $row = $dz->get_row('select * from `Root.Entities.Pending` where `Endpoint`="' . $domain . '"'); if (!$row) { // Not found! error('entity/notfound'); } // Get the public key bytes: $publicKey = $row['Key']; // Get the public key in hex: $hexPublicKey = bin2hex($publicKey); // Test for a majority. This time we check all signatures including our own // because the whole set came from somewhere else (we're not leading the majority process here). testMajority($challenges, $hexPublicKey); // Majority formed! // Remove from pending: $dz->query('delete from `Root.Entities.Pending` where `Endpoint`="' . $domain . '"'); // Transfer the entity to being an official entity: $dz->query('insert into `Root.Entities` (`Key`,`Endpoint`,`Type`,`Group`,`Name`,`Country`) values (unhex("' . $hexPublicKey . '"),"' . $domain . '",' . $row['Type'] . ',' . $thisEntity['Group'] . ',"' . $row['Name'] . '",' . $row['Country'] . ')'); // Create the change event too: changed('entity', array('key' => $hexPublicKey, 'type' => $row['Type'], 'endpoint' => $domain, 'group' => $thisEntity['Group'], 'name' => $row['Name'], 'country' => $row['Country']));
// Validate the signature: $signed = bin2hex($publicKey) . '.' . $balance; if (!verify($signature, $signed, $address)) { // Invalid signature: error('signature/invalid'); } // Valid claim! // Sign it to say this node agrees, using some additional random data: $challenge = randomString(45); $myChallenge = '{"challenge":"' . $challenge . '","signature":"' . base64_encode(sign($challenge . $signed)) . '"}'; // Was this request forwarded? if ($forwardedFromRoot != 0) { // Yes - just output my challenge. We'll wait for a majority. echo $myChallenge; exit; } // Forward: $responses = forward(); // Did it obtain a majority? $fullSet = testMajority($responses, $signed, $myChallenge); // Yes - majority obtained! Forward the key set to the root now: // Add the address: $fullSet = '{"address":"' . $address . '","challenges":' . $fullSet . '}'; // For info on the base64 string in the middle, see the forward() function. $encodedFullSet = base64_encode($fullSet); // Send now: sendToRoot($encodedFullSet, 'eyJmd2QiOjF9', false, 'balance/claim/success'); // Update the balance: $dz->query('update `Root.Balances` set `Entity`=' . $verifiedEntity . ' where `Key`=unhex("' . $address . '")'); // Create a clm record (occurs in balance/claim/success as well): changed('clm', array('entity' => $verifiedEntityEndpoint, 'address' => $address, 'signature' => $signature, 'balance' => $balance));
$myPair = '{"challenge":"' . $challenge . '","signature":"' . base64_encode($rootSignature) . '"}'; // Is this already forwarded? If so, just return my signature. if ($forwardedFromRoot != 0) { // Some other root forwarded the request here. // Create a lock for this balance. This is for if a majority is not obtained and we need to reverse the lock: $dz->query('insert into `Root.Balances.Locks`(`RequestID`,`Amount`) values("' . $signedPublicID['id'] . '",' . $amount . ')'); // Output the signature and stop: echo $myPair; exit; } // Forward to the group: $results = forward(); // Next, verify all the signatures. If we have a valid majority, forward it to the group. // At which point, a successful issue has occured. // Note that the false turns off the error (and instead we look out for a 'false') $fullSet = testMajority($results, $signed, $myPair, false); if ($fullSet === false) { $majority = false; // No majority formed. Unlike transfer/create/success, this can occur in normal circumstances. // As this is the same file which creates the balance locks in the first place, we can (and must) safely // reverse those locks here: // Send the from amount back to it's balance: $dz->query('update `Root.Balances` set LockedBalance=LockedBalance-' . $amount . ',Balance=Balance+' . $amount . ' where `Key`=UNHEX("' . $fromAddress . '")'); // And the to amount needs to simply have the locked amount reduced: $dz->query('update `Root.Balances` set LockedBalance=LockedBalance-' . $amount . ' where `Key`=UNHEX("' . $toAddress . '")'); // Now we need to remove any remote locks too. // We do it by calling the same function but with an empty results set, which is set like so: $fullSet = '{}'; } else { $majority = true; // Majority obtained! Forward all the signatures (fullSet) to everyone else.
<?php // This occurs when every root node has signed to say it believes an issue was valid. // A root node collected all those signatures and then sent the whole lot to every root node. // This allows them to independently form a proof of majority. // In this case, when the majority is successful, an issue occurs. // Always JSON posted here: $publicKey = postedTo(true); if ($forwardedFromRoot == 0) { // Must have been forwarded by a root node. error('entity/notroot'); } // Get the challenges: // We want it as-is (it's an array) $challenges = safe('challenges', VALID_ARRAY); // Get the signature, address and current balance: $signature = safe('signature', VALID_BASE64); $address = safe('address', VALID_HEX); $balance = safe('balance', VALID_NUMBER); // The signed data is as follows: $signed = bin2hex($publicKey) . '.' . $balance; // Test for a majority. This time we check all signatures including our own // because the whole set came from somewhere else (we're not leading the majority process here). $majority = testMajority($challenges, $signed); // Majority formed! // Update the balance: $dz->query('update `Root.Balances` set `Entity`=' . $verifiedEntity . ' where `Key`=unhex("' . $address . '")'); // Create a clm record (occurs in balance/claim/success as well): changed('clm', array('entity' => $verifiedEntityEndpoint, 'address' => $address, 'signature' => $signature, 'balance' => $balance));
} // Get the address to issue into: $hexAddress = safe('address', VALID_HEX); // Lock the given amount into the given receiving address: receiveLocked($hexAddress, $amount, $tag, $issuerID); // Generate some random challenge data: $challenge = randomString(45); // Sign the challenge data along with the hex public key and amount: $signature = sign($challenge . $hexAddress . $amount); // Build my signature JSON: $myPair = '{"challenge":"' . $challenge . '","signature":"' . base64_encode($signature) . '"}'; // Is this already forwarded? If so, just return my signature. if ($forwardedFromRoot != 0) { // Some other root forwarded the request here. Just output the signature and stop: echo $myPair; exit; } // Forward to the group: $results = forward(); // Next, verify all the signatures. If we have a valid majority, forward it to the group. // At which point, a successful issue has occured. $fullSet = testMajority($results, $hexAddress . $amount, $myPair); // Majority obtained! Forward all the signatures to everyone else. // Add the amount and address: $fullSet = '{"amount":"' . $amount . '","address":"' . $hexAddress . '","challenges":' . $fullSet . '}'; // For info on the base64 string in the middle, see the forward() function. sendToRoot(base64_encode($fullSet), 'eyJmd2QiOjF9', false, 'commodity/issue/success'); // Update the amount now! $dz->query('update `Root.Balances` set Balance=Balance+' . $amount . ',LockedBalance=LockedBalance-' . $amount . ' where `Key`=UNHEX("' . $hexAddress . '")'); // Create an issue record (occurs in issue/success as well): changed('issue', array('amount' => $amount, 'to' => $hexAddress, 'tag' => $tag));
$toGroup = safe('group', VALID_NUMBER, $to); // Is it going to another group? $outbound = $toGroup != $thisEntity['Group']; // Get the amount to transfer: $amount = safe('amount', VALID_NUMBER); if ($amount == 0) { // Must be non-zero (we know it's positive as VALID_NUMBER doesn't accept -): error('field/invalid', 'amount'); } // Get the signature for the address itself: $signature = safe('signature', VALID_BASE64); // The signed data is as follows: $signed = $fromGroup . '/' . $fromAddress . '-' . $toGroup . '/' . $toAddress . '-' . $amount . '-' . $fromBalance; // Test for a majority. This time we check all signatures including our own // because the whole set came from somewhere else (we're not leading the majority process here). $majority = testMajority($challenges, $signed, null, false); if (!$majority) { // No majority formed. We were told about this so we can reverse our locks. // We must first check if we actually had a lock for this request like so: $lock = $dz->get_row('select Amount from `Root.Balances.Locks` where RequestID="' . $signedPublicID['id'] . '"'); if ($lock) { // Remove the complete lock now, provided amounts match: if ($lock['Amount'] != $amount) { // Nope - invalid input. error('field/invalid', 'amount'); } // Send the from amount back to it's balance: $dz->query('update `Root.Balances` set LockedBalance=LockedBalance-' . $amount . ',Balance=Balance+' . $amount . ' where `Key`=UNHEX("' . $fromAddress . '")'); // Delete the lock row: $dz->query('delete from `Root.Balances.Locks` where RequestID="' . $signedPublicID['id'] . '"'); // And the to amount needs to simply have the locked amount reduced: