function testBalanceLookupElegantlyHandlesUnhelpfulResponsesFromBlockchainDotInfo() { $addresses = array('1GivesBlankContentiTDhViXbrogKqzbt', '1GivesLockWaitTimeoutTDhViXbrogKqzbt'); foreach ($addresses as $a) { try { BlockchainDotInfo\getBalanceInSatoshis($a); fail("Expected to get exception for lookup on address {$a}"); } catch (NetworkError $e) { } } }
function testGetBalanceUsesLocallyCachedValueWhenAppropriate() { $address = '1K7dyLY6arFRXBidQhrtnyqksqJZdj2F37'; $actualBalance = BlockchainDotInfo\getBalanceInSatoshis($address); $cachedBalance = $actualBalance + 1000; DB\delete('bitcoin_addresses', 'address = ?', array($address)); DB\insertOne('bitcoin_addresses', array('address' => $address, 'satoshis' => $cachedBalance, 'updated_at' => new DateTime('now'))); $balance = Bitcoin\getBalance($address, null); assertEqual($cachedBalance, $balance->numSatoshis); assertEqual($cachedBalance / Bitcoin\satoshisPerBTC(), $balance->numBTC); }
function getBalance($address, \DateInterval $maxCacheAge = null) { if (empty($address) || trim($address) == '') { throw new InvalidAddress("Empty string/value is not valid"); } $satoshis = null; $updatedAt = null; $needsUpdated = false; try { $row = DB\selectExactlyOne('bitcoin_addresses', 'address = ?', array($address)); $satoshis = intval($row['satoshis']); if ($maxCacheAge) { $updatedAt = new DateTime($row['updated_at']); $expiresAt = $updatedAt->add($maxCacheAge); $now = new DateTime('now'); if ($expiresAt->getTimestamp() < $now->getTimestamp()) { $needsUpdated = true; } } } catch (DB\NoMatchingRecords $_) { $needsUpdated = true; } if ($needsUpdated) { # Since we don't want to have two or three (or more) processes all trying to query # Blockchain.info at the same time, we use a lock to assure only one attempt is made # to update the cache. $lockObtained = withLock($address, function () use($address) { $satoshis = BlockchainDotInfo\getBalanceInSatoshis($address); cacheBalance($address, $satoshis); }); if (!$lockObtained) { if ($satoshis === null) { Log\error("Failed to obtain lock for and no cached balance exists for Bitcoin address " . "{$address}; defaulting to zero"); } $oneHourAgo = new DateTime('1 hour ago'); if ($updatedAt && $updatedAt->getTimestamp() < $oneHourAgo->getTimestamp()) { Log\error("Balance for Bitcoin address {$address} has not been updated for " . "more than one hour"); } } } // $btcBalance = $satoshis / satoshisPerBTC(); // $balanceWithPrecision = $currency == 'BTC' ? $btcBalance : fromBTC($btcBalance, $currency); // return $balanceWithPrecision; return new AmountOfBitcoin($satoshis); }