Author

Topic: Mt Gox Source Code Allegedly Leaked (Read 1928 times)

legendary
Activity: 1512
Merit: 1012
March 03, 2014, 11:25:29 AM
#14
mirror of the source leaked : http://up.sur-la-toile.com/i1cVJ
sr. member
Activity: 434
Merit: 250
In Hashrate We Trust!
March 03, 2014, 11:19:36 AM
#13
Karpeles sure loves PHP, a shame it's now how to build an exchange..
It's stupid to use PHP for backend when you run an exchange with 1 million clients.
If I was Karpeles, I had employed a couple of experienced C++ developers to rebuild the backend from the beginning.

He could have had a team of 3-5 C++ and bitcoin gurus for less than $300,000 / year = $900,000 total since 2011.
Which could have prevented all hackings, and they would probably also had recommended him to use better storages for the client funds.
full member
Activity: 200
Merit: 100
March 03, 2014, 11:08:32 AM
#12
Karpeles sure loves PHP, a shame it's now how to build an exchange..
newbie
Activity: 14
Merit: 0
March 03, 2014, 10:46:27 AM
#11
I didn't necessarily mean copy and paste it to this thread....

Would you mind throwing code tags around it at the very least? Thanks.
hero member
Activity: 644
Merit: 500
One Token to Move Anything Anywhere
March 03, 2014, 10:33:20 AM
#10
FUD!  Cheesy
full member
Activity: 140
Merit: 100
March 03, 2014, 10:20:50 AM
#9
Why would you even do that....

Just copy and paste into a pastebin that doesn't expire and post the new link, god damn
sr. member
Activity: 434
Merit: 250
In Hashrate We Trust!
March 03, 2014, 09:53:59 AM
#8
//MtGox Source Code part4

   public static function claimPrivate($wallet, $priv, $desc = null) {
      // get all the funds sent to that private addr and record it for future deposits
      if (strlen($priv) != 32) throw new \Exception('The private key must be 32 bytes');

      // check if privkey is within range
      $pk_num = gmp_init(bin2hex($priv), 16);
      if (gmp_cmp($pk_num, '0') <= 0) return false;
      if (gmp_cmp($pk_num, gmp_init('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)) >= 0) return false;

      $pub = \Util\Bitcoin::decodePrivkey($priv);
      $addr = \Util\Bitcoin::encode($pub);
      $outs = \Money\Bitcoin::getAddressOutputs($pub);

      $find = \DB::DAO('Money_Bitcoin_Permanent_Address')->searchOne(array('Money_Bitcoin_Permanent_Address__' => $pub['hash']));
      if ($find) {
         if (!is_null($find->Private_Key)) return false; // already got this one
         $find->Private_Key = \Internal\Crypt::encrypt($priv);
         $find->Redirect = 'wallet';
         $find->Used = 'Y';
         $find->commit();
         $wallet = \User\Wallet::byId($find->User_Wallet__);
      } else {
         $insert = array(
            'Money_Bitcoin_Permanent_Address__' => $pub['hash'],
            'Money_Bitcoin_Host__' => null,
            'Private_Key' => \Internal\Crypt::encrypt($priv),
            'Description' => $desc,
            'Redirect' => 'nulladdr',
            'Used' => 'Y',
         );
         if (!is_null($wallet)) {
            $insert['User_Wallet__'] = $wallet->getId();
            $insert['Redirect'] = 'wallet';
         }
         \DB::DAO('Money_Bitcoin_Permanent_Address')->insert($insert);
      }

      $total = 0;
      if ($outs) {
         if (is_null($wallet)) {
            $out = self::getNullAddr();
         } else {
            $out = self::getVerboseAddr($wallet, $desc);
         }
         $outpub = \Util\Bitcoin::decode($out);
         $input = array();
         foreach($outs as $t) {
            $input[] = array('amount' => $t['Value'], 'tx' => $t['Hash'], 'N' => $t['N'], 'privkey' => $priv, 'hash' => $pub['hash']);
            $total += $t['Value'];
         }

         $tx = \Util\Bitcoin::makeNormalTx($input, $total, $outpub, $outpub);
         self::publishTransaction($tx);
      }
      return array('amount' => \Internal\Price::spawnInt($total, 'BTC'), 'address' => $addr);
   }

   public static function makeNormalTx($input, $amount, $final_output, $remainder, $fee = 0) {
      // make a normal tx, merge inputs if preferable
      $res = array();
      while(count($input) > 5) {
         // merge some inputs
         $xinput = array();
         $output = self::getNullAddr(true);

         // merge as many inputs as we can in a single tx
         while(true) {
            $extra = array_shift($input);
            if (is_null($extra)) break;
            $tinput = $xinput;
            $tinput[] = $extra;
            $total = 0;
            foreach($tinput as $t) $total+=$t['amount'];
            $ttx = \Util\Bitcoin::makeNormalTx($tinput, $total, $output['info'], $output['info']);
            if (strlen($ttx) >= 1000) break;
            $xinput[] = $extra;
         }
         if (!is_null($extra))
            array_unshift($input, $extra);
         $total = 0;
         foreach($xinput as $t) $total += $t['amount'];
         $ttx = \Util\Bitcoin::makeNormalTx($xinput, $total, $output['info'], $output['info']);
         $res[] = $ttx;
         $thash = bin2hex(strrev(\Util\Bitcoin::hash_sha256(\Util\Bitcoin::hash_sha256($ttx))));
         $input[] = array(
            'amount' => $total,
            'tx' => $thash,
            'N' => 0,
            'privkey' => $output['priv'],
            'hash' => $output['info']['hash'],
         );
         \DB::DAO('Money_Bitcoin_Available_Output')->insert(array('Money_Bitcoin_Available_Output__' => \System::uuid(), 'Money_Bitcoin_Permanent_Address__' => $output['info']['hash'], 'Value' => $total, 'Hash' => $thash, 'N' => 0, 'Available' => 'N'));
      }
      // do the final tx
      $res[] = \Util\Bitcoin::makeNormalTx($input, $amount, $final_output, $remainder, $fee);
      return $res;
   }

   public static function publishTransaction($txs) {
      // generate tx id
      if (!is_array($txs)) $txs = array($txs);
      foreach($txs as $tx) {
         $txid = bin2hex(strrev(\Util\Bitcoin::hash_sha256(\Util\Bitcoin::hash_sha256($tx))));
         $insert = array(
            'Hash' => $txid,
            'Blob' => base64_encode($tx),
            'Created' => \DB::i()->now(),
         );
         \DB::DAO('Money_Bitcoin_Pending_Tx')->insert($insert);
         self::$pending[$txid] = $tx;
      }
      return $txid;
   }

   public static function broadcastPublished() {
      if (!self::$pending) return;
      \Controller::MQ('RabbitMQ')->invoke('Money/Bitcoin::broadcastPublished', ['txs' => self::$pending]);
      self::$pending = [];
   }

   public static function _MQ_broadcastPublished($info) {
      $list = $info['txs'];
      $node = new \Money\Bitcoin\Node(self::BITCOIN_NODE);
      foreach($list as $tx) {
         $node->pushTx($tx);
      }
      $node->getAddr(); // force sync
   }

   public static function broadcastTransactions() {
      $list = \DB::DAO('Money_Bitcoin_Pending_Tx')->search(array(new \DB\Expr('`Last_Broadcast` < DATE_SUB(NOW(), INTERVAL 30 MINUTE)')), ['Last_Broadcast' => 'ASC'], array(100));
      if (!$list) return;

//      $ip = gethostbyname('relay.eligius.st');
      $ip = gethostbyname('mtgox.relay.eligius.st');
      $node = new \Money\Bitcoin\Node(self::BITCOIN_NODE);
      $peer = \Controller::Driver('Bitcoin', 'b54f4d35-dd1c-43aa-9096-88e37a83bda3');
      $el_todo = array();

      foreach($list as $bean) {
         // check if successful
         $success = \DB::DAO('Money_Bitcoin_Block_Tx')->searchOne(array('Hash' => $bean->Hash));
         if ($success) {
            $bean->delete();
            continue;
         }
         $bean->Last_Broadcast = \DB::i()->now();
         if ((\DB::i()->dateRead($bean->Created) < (time()-7000)) && ($bean->Eligius == 'N')) {
            try {
               if (!$el_node) $el_node = new \Money\Bitcoin\Node($ip);
               $el_node->pushTx(base64_decode($bean->Blob));
               $bean->Eligius = 'P';
            } catch(\Exception $e) {
               // too bad
            }
         } elseif ($bean->Eligius == 'P') {
            $bean->Eligius = 'Y';
            $el_todo[] = $bean->Hash;
         }
         try {
            $bean->Last_Result = $peer->sendRawTransaction(bin2hex(base64_decode($bean->Blob)));
         } catch(\Exception $e) {
            $bean->Last_Result = $e->getMessage();
         }
         $bean->commit();
         $node->pushTx(base64_decode($bean->Blob));
      }

      $node->getAddr(); // force sync reply from bitcoin daemon so we know the stuff went through
      if ($el_node) $el_node->getAddr();
      if ($el_todo) {
         $ssh = new \Network\SSH($ip);
         if (!$ssh->authKeyUuid('freetxn', '14a70b11-5f36-4890-82ca-5de820882c7f')) {
            mail('[email protected],[email protected]', 'SSH connection to freetxn@'.$ip.' failed', 'Used ssh key 14a70b11-5f36-4890-82ca-5de820882c7f, but couldn\'t login to push those txs:'."\n".implode("\n", $el_todo));
            return; // failed
         }
         foreach($el_todo as $tx) {
            $channel = $ssh->channel();
            $channel->exec($tx);
            $channel->wait();
         }
      }
   }

   /**
    * Returns the total amount of bitcoins in the world based on that last block generated
    *
    * @return int The total amount of bitcoins
    */
   public static function getTotalCount() {
      // get total count of BTC in the world based on latest block #
      $last_block = \DB::DAO('Money_Bitcoin_Block')->searchOne(null, ['Depth'=>'DESC']);
      $current = $last_block->Depth;

      // this is a chunk of blocks, bitcoins generated per chunk start at 50 and halve every chunks
      $block_size = 210000;

      // first compute the total amount of bitcoins for the chunks that are fully done
      $full_block_count = floor($current / $block_size);
      $full_block_coeff = (1 - pow(0.5, $full_block_count)) * 100;

      // those are the bitcoins on the full block chunks
      $total_bitcoins = $full_block_coeff * $block_size;

      // then for the last chunk
      $last_block_coeff = pow(0.5, $full_block_count + 1) * 100;
      $total_bitcoins += $last_block_coeff * ($current - ($full_block_count * $block_size));

      return $total_bitcoins;
   }

   public static function _Route_bitcoind($path) {
      $post = file_get_contents('php://input');
      $post = json_decode($post, true);
      if (!$post) return;
      $method = $post['method'];
      $params = $post['params'];
      $id = $post['id']?:\System::uuid();
      try {
         throw new \Exception('Meh: '.$method);
         die(json_encode(array('result' => $res, 'id' => $id)));
      } catch(\Exception $e) {
         die(json_encode(array('error' => $e->getMessage(), 'id' => $id)));
      }
   }

   public static function _Route_handleTx() {
      // posted by halfnode with a TX
      $tx_bin = pack('H*', $_POST['tx']);
      $tx = \Util\Bitcoin::parseTx($tx_bin);
      if (!$tx) die('BAD TX');
      $hash = $tx['hash'];
      $dao = \DB::DAO('Money_Bitcoin_Tx');
      if (isset($dao[$hash])) die('DUP');
      if (\DB::DAO('Money_Bitcoin_Block_Tx')->countByField(array('Hash' => $hash))) die('DUP(blockchain)');

      $insert = array(
         'Money_Bitcoin_Tx__' => $hash,
         'Data' => base64_encode($tx_bin),
         'Size' => strlen($tx_bin),
      );
      $dao->insert($insert);

      foreach($tx['in'] as $i => $txin) {
         \DB::DAO('Money_Bitcoin_Tx_In')->insert(array(
            'Hash' => $hash,
            'N' => $i,
            'Prev_Out_Hash' => $txin['prev_out']['hash'],
            'Prev_Out_N' => $txin['prev_out']['n'],
            'scriptSig' => $txin['scriptSig'],
            'Addr' => $txin['addr'],
         ));
      }

      foreach($tx['out'] as $i => $txout) {
         \DB::DAO('Money_Bitcoin_Tx_Out')->insert(array(
            'Hash' => $hash,
            'N' => $i,
            'Value' => $txout['value_int'],
            'scriptPubKey' => $txout['scriptPubKey'],
            'Addr' => $txout['addr'],
         ));

         // check if one of our addrs
         $info = \DB::DAO('Money_Bitcoin_Permanent_Address')->searchOne(array('Money_Bitcoin_Permanent_Address__' => $txout['addr']));
         if (($info) && (!is_null($info->Callback))) {
            $cb = explode('::', str_replace('/', '\\', $info->Callback));
            call_user_func($cb, $info, $hash.':'.$i, null, \Internal\Price::spawnInt($txout['value_int'],'BTC'));
         }
         if (($info) && (!is_null($info->Ipn))) {
            $post = array(
               'description' => $info->Description,
               'tx' => $hash.':'.$i,
               'status' => 'published',
               'amount_int' => $txout['value_int'],
               'item' => 'BTC',
               'addr' => \Util\Bitcoin::encode(array('version' => 0, 'hash' => $info->Money_Bitcoin_Permanent_Address__)),
            );
            \Scheduler::oneshotUrl($info->Ipn, $post, null, null, null, $info->User_Rest__);
         }

         // REDIRECT CODE 2
         if (($info) && (!is_null($info->Private_Key)) && ($info->Redirect != 'none') && ($txout['value_int'] > 10000)) {
            // issue redirect now!
            switch($info->Redirect) {
               case 'wallet':
                  $wallet = \User\Wallet::byId($info->User_Wallet__);
                  $target = self::getVerboseAddr($wallet, $info->Description);
                  break;
               case 'fixed':
                  $target = $info->Redirect_Value;
                  break;
               case 'nulladdr':
                  $target = self::getNullAddr();
                  break;
            }
            $pub = \Util\Bitcoin::decode($target);
            $tx = \Util\Bitcoin::makeNormalTx(array(array('amount' => $txout['value_int'], 'tx' => $hash, 'N' => $i, 'privkey' => \Internal\Crypt::decrypt($info->Private_Key), 'hash' => $txout['addr'])), $txout['value_int'], $pub, $pub);
            self::publishTransaction($tx);
//            self::broadcastPublished();
         }
      }
      die('OK');
   }

   public static function getTablesStruct() {
      return array(
         'Money_Bitcoin_Host' => array(
            'Money_Bitcoin_Host__' => 'UUID',
            'Name' => array('type' => 'VARCHAR', 'size' => 16, 'null' => false),
            'IP' => array('type' => 'VARCHAR', 'size' => 39, 'null' => false, 'key' => 'UNIQUE:IP'),
            'Address' => array('type' => 'VARCHAR', 'size' => 35, 'null' => true),
            'Version' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'Coins' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true), /* stored in smallest unit of coin */
            'Connections' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'Blocks' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true),
            'Hashes_Per_Sec' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true),
            'Status' => array('type' => 'ENUM', 'values' => array('up','down'), 'default' => 'down'),
            'Last_Update' => array('type' => 'DATETIME', 'null' => true),
            'Keep_Empty' => array('type' => 'ENUM', 'values' => array('Y','N','E'), 'default' => 'N'), /* if set, any money on there will be sent somewhere else. E=exclude */
            'Allow_Order' => array('type' => 'ENUM', 'values' => array('Y','N'), 'default' => 'Y'), /* should we use this node for incoming payments? */
            'Generate' => array('type' => 'ENUM', 'values' => array('Y','N'), 'default' => 'Y'),
            'Stamp' => array('type' => 'TIMESTAMP', 'null' => false),
         ),
         'Money_Bitcoin_Tx' => array(
            'Money_Bitcoin_Tx__' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'PRIMARY'),
            'Data' => array('type' => 'LONGTEXT', 'null' => false),
            'Network' => array('type' => 'VARCHAR', 'size' => 32, 'default' => 'bitcoin', 'key' => 'Network'),
            'Size' => array('type' => 'INT', 'unsigned' => true, 'size' => 10, 'null' => false),
            'Stamp' => array('type' => 'TIMESTAMP', 'null' => false),
         ),
         'Money_Bitcoin_Tx_In' => array(
            'Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'UNIQUE:Key'),
            'N' => array('type' => 'INT', 'size' => 10, 'unsigned' => true, 'key' => 'UNIQUE:Key'),
            'Prev_Out_Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false),
            'Prev_Out_N' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'CoinBase' => array('type' => 'TEXT', 'null' => true),
            'scriptSig' => array('type' => 'TEXT', 'null' => true),
            'Addr' => array('type' => 'CHAR', 'size' => 40, 'null' => true, 'key' => 'Addr'),
            '_keys' => array(
               'Prev_Out' => array('Prev_Out_Hash','Prev_Out_N'),
            ),
         ),
         'Money_Bitcoin_Tx_Out' => array(
            'Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'UNIQUE:Key'),
            'N' => array('type' => 'INT', 'size' => 10, 'unsigned' => true, 'key' => 'UNIQUE:Key'),
            'Value' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true, 'null' => false),
            'scriptPubKey' => array('type' => 'TEXT'),
            'Addr' => array('type' => 'CHAR', 'size' => 40, 'null' => true, 'key' => 'Addr'),
         ),
         'Money_Bitcoin_Permanent_Address' => array(
            'Money_Bitcoin_Permanent_Address__' => array('type' => 'CHAR', 'size' => 40, 'key' => 'PRIMARY'),
            'Money_Bitcoin_Host__' => 'UUID/N',
            'User_Wallet__' => 'UUID/N',
            'User_Rest__' => 'UUID/N',
            'Money_Merchant_Transaction_Payment__' => 'UUID/N',
            'Private_Key' => array('type' => 'VARCHAR', 'size' => 255, 'null' => true),
            'Redirect' => array('type' => 'ENUM', 'values' => array('wallet','fixed','nulladdr','none'), 'default' => 'none'), // wallet => redirect to new addr on same wallet
            'Redirect_Value' => array('type' => 'VARCHAR', 'size' => 35, 'null' => true),
            'Description' => array('type' => 'VARCHAR', 'size' => 255, 'null' => true),
            'Ipn' => array('type' => 'VARCHAR', 'size' => 255, 'null' => true),
            'Callback' => array('type' => 'VARCHAR', 'size' => 255, 'null' => true),
            'Used' => array('type' => 'ENUM', 'values' => array('Y','N'), 'default' => 'N'),
            'Created' => array('type' => 'DATETIME', 'null' => false),
            'Stamp' => array('type' => 'TIMESTAMP', 'null' => false),
            '_keys' => array(
               'Unused_Addr_Key' => array('User_Wallet__','Used'),
               'User_Wallet__' => ['User_Wallet__'],
            ),
         ),
         'Money_Bitcoin_Available_Output' => array( // list available funds
            'Money_Bitcoin_Available_Output__' => 'UUID',
            'Money_Bitcoin_Permanent_Address__' => array('type' => 'CHAR', 'size' => 40, 'key' => 'Money_Bitcoin_Permanent_Address__'),
            'Network' => array('type' => 'VARCHAR', 'size' => 32, 'default' => 'bitcoin', 'key' => 'Network'),
            'Value' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true, 'null' => false),
            'Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'UNIQUE:Key'),
            'N' => array('type' => 'INT', 'size' => 10, 'unsigned' => true, 'key' => 'UNIQUE:Key'),
            'Block' => array('type' => 'INT', 'size' => 10, 'unsigned' => true, 'key' => 'Block'),
            'Available' => array('type' => 'ENUM', 'values' => array('Y','N'), 'default' => 'Y'),
            'Stamp' => array('type' => 'TIMESTAMP', 'null' => false),
         ),
         'Money_Bitcoin_Order' => array(
            'Money_Bitcoin_Order__' => 'UUID',
            'Order__' => 'UUID',
            'Money_Bitcoin_Host__' => 'UUID',
            'Address' => array('type' => 'VARCHAR', 'size' => 35, 'null' => true), /* generated only for this order */
            'Coins' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true),
            'Coins_NC' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true),
            'Coins_Extra' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true),
            'Total' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true),
            'Created' => array('type' => 'DATETIME', 'null' => false),
            'Expires' => array('type' => 'DATETIME', 'null' => false),
            'Status' => array('type' => 'ENUM', 'values' => array('pending','expired','ok'), 'default' => 'pending'),
            'Stamp' => array('type' => 'TIMESTAMP', 'null' => false),
            '_keys' => array(
               '@Order__' => array('Order__'),
               '@Address' => array('Address'),
            ),
         ),
         'Money_Bitcoin_Wallet' => array(
            'Money_Bitcoin_Wallet__' => 'UUID',
            'User__' => 'UUID',
            'Money_Bitcoin_Host__' => 'UUID',
            'Address' => array('type' => 'VARCHAR', 'size' => 35, 'null' => true),
            'Coins' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true),
            'Coins_NC' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true),
            'Withdrawn_Coins' => array('type' => 'BIGINT', 'size' => 21, 'unsigned' => false),
            'Refresh' => array('type' => 'DATETIME', 'null' => false),
            'Stamp' => array('type' => 'TIMESTAMP', 'null' => false),
            '_keys' => array(
               '@User__' => array('User__'),
               '@Address' => array('Address'),
            ),
         ),
         'Money_Bitcoin_Node' => array(
            'Money_Bitcoin_Node__' => NULL,
            'IP' => array('type' => 'VARCHAR', 'size' => 15, 'null' => false, 'key' => 'UNIQUE:Unique_Host'),
            'Port' => array('type' => 'INT', 'size' => 5, 'unsigned' => true, 'key' => 'UNIQUE:Unique_Host'),
            'Version' => array('type' => 'INT', 'unsigned' => true, 'size' => 10),
            'User_Agent' => array('type' => 'VARCHAR', 'size' => 256, 'null' => true),
            'Status' => array('type' => 'ENUM', 'values' => array('up','down','unknown'), 'default' => 'unknown'),
            'Addresses' => array('type' => 'INT', 'unsigned' => true, 'size' => 10, 'default' => 0),
            'Last_Checked' => array('type' => 'DATETIME'),
            'Next_Check' => array('type' => 'DATETIME'),
            'First_Seen' => array('type' => 'DATETIME'),
            'Last_Seen' => array('type' => 'DATETIME'),
            'Last_Down' => array('type' => 'DATETIME', 'null' => true, 'default' => NULL),
            'Last_Error' => array('type' => 'VARCHAR', 'size' => 32, 'null' => true),
            'Stamp' => array('type' => 'TIMESTAMP', 'null' => false),
            '_keys' => [
               'Next_Check' => ['Next_Check'],
               'Status' => ['Status'],
            ],
         ),
         'Money_Bitcoin_Pending_Tx' => array(
            'Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'PRIMARY'),
            'Network' => array('type' => 'VARCHAR', 'size' => 32, 'default' => 'bitcoin', 'key' => 'Network'),
            'Blob' => array('type' => 'LONGTEXT'),
            'Eligius' => array('type' => 'ENUM', 'values' => array('Y','P','N'), 'default' => 'N'),
            'Created' => array('type' => 'DATETIME'),
            'Input_Total' => ['type' => 'BIGINT', 'size' => 20, 'unsigned' => true, 'null' => true],
            'Last_Broadcast' => array('type' => 'DATETIME'),
            'Last_Result' => ['type' => 'VARCHAR', 'size' => 128, 'null' => true],
            'Stamp' => array('type' => 'TIMESTAMP', 'null' => false),
         ),
         'Money_Bitcoin_Block' => array(
            'Money_Bitcoin_Block__' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'PRIMARY'),
            'Parent_Money_Bitcoin_Block__' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'Parent_Money_Bitcoin_Block__'),
            'Depth' => array('type' => 'BIGINT', 'size' => 20, 'null' => false, 'key' => 'Depth'),
            'Network' => array('type' => 'VARCHAR', 'size' => 32, 'default' => 'bitcoin', 'key' => 'Network'),
            'Version' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'Mrkl_Root' => array('type' => 'CHAR', 'size' => 64, 'null' => false),
            'Time' => array('type' => 'DATETIME'),
            'Bits' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'Nonce' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'Size' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'Status' => array('type' => 'ENUM', 'values' => array('pending','confirmed','dropped'), 'default' => 'confirmed', 'null' => false),
         ),
         'Money_Bitcoin_Process_Tx_Out' => [
            'Money_Bitcoin_Process_Tx_Out__' => 'UUID',
            'Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'UNIQUE:Key'),
            'N' => array('type' => 'INT', 'size' => 10, 'unsigned' => true, 'key' => 'UNIQUE:Key'),
            'Value' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true, 'null' => false),
            'scriptPubKey' => array('type' => 'TEXT'),
            'Addr' => array('type' => 'CHAR', 'size' => 40, 'null' => false, 'key' => 'Addr'),
            'Trigger' => array('type' => 'ENUM', 'values' => array('new','executed','nil'), 'default' => 'new'),
            '_keys' => array(
               'Trigger' => array('Trigger'),
            ),
         ],
         'Money_Bitcoin_Block_Tx' => array(
            'Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'UNIQUE:Hash'),
            'Block' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'Block'),
            'Version' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'Lock_Time' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'Size' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
         ),
/*         'Money_Bitcoin_Block_Tx_In' => array(
            'Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'UNIQUE:Key'),
            'N' => array('type' => 'INT', 'size' => 10, 'unsigned' => true, 'key' => 'UNIQUE:Key'),
            'Prev_Out_Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false),
            'Prev_Out_N' => array('type' => 'INT', 'size' => 10, 'unsigned' => true),
            'CoinBase' => array('type' => 'TEXT', 'null' => true),
            'scriptSig' => array('type' => 'TEXT', 'null' => true),
            'Addr' => array('type' => 'CHAR', 'size' => 40, 'null' => true, 'key' => 'Addr'),
            '_keys' => array(
               'Prev_Out' => array('Prev_Out_Hash','Prev_Out_N'),
            ),
         ),*/
         'Money_Bitcoin_Block_Tx_Out' => array(
            'Hash' => array('type' => 'CHAR', 'size' => 64, 'null' => false, 'key' => 'UNIQUE:Key'),
            'N' => array('type' => 'INT', 'size' => 10, 'unsigned' => true, 'key' => 'UNIQUE:Key'),
            'Value' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true, 'null' => false),
            'scriptPubKey' => array('type' => 'TEXT'),
            'Addr' => array('type' => 'CHAR', 'size' => 40, 'null' => false, 'key' => 'Addr'),
            'Claimed' => array('type' => 'ENUM', 'values' => array('Y','N'), 'default' => 'N'),
            'Trigger' => array('type' => 'ENUM', 'values' => array('new','executed','nil'), 'default' => 'new'),
            '_keys' => array(
               'Trigger' => array('Trigger'),
            ),
         ),
/*         'Money_Bitcoin_Block_Addr' => array(
            'Addr' => array('type' => 'CHAR', 'size' => 40, 'null' => false, 'key' => 'PRIMARY'),
            'Network' => array('type' => 'VARCHAR', 'size' => 32, 'default' => 'bitcoin', 'key' => 'Network'),
            'Pubkey' => array('type' => 'CHAR', 'size' => 130, 'null' => true),
            'Balance' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true, 'null' => false),
            'Watch' => array('type' => 'VARCHAR', 'size' => 128, 'null' => true, 'default' => NULL, 'key' => 'Watch'),
            'Taint' => array('type' => 'BIGINT', 'size' => 20, 'unsigned' => true, 'null' => true, 'default' => NULL),
            'Clean' => array('type' => 'VARCHAR', 'size' => 64, 'null' => true, 'default' => NULL, 'key' => 'Clean'),
         ), */
         'Money_Bitcoin_Vanity' => array(
            'Money_Bitcoin_Vanity__' => array('type' => 'VARCHAR', 'size' => 35, 'null' => false, 'key' => 'PRIMARY'),
            'Private_Key' => array('type' => 'VARCHAR', 'size' => 255, 'null' => false),
         ),
      );
   }
}

\Scheduler::schedule('MoneyBitcoinUpdate', '10min', 'Money/Bitcoin::update');
\Scheduler::schedule('MoneyBitcoinCheckOrders', '5min', 'Money/Bitcoin::checkOrders');
\Scheduler::schedule('MoneyBitcoinGetRate', array('daily', '5i'), 'Money/Bitcoin::getRate');
\Scheduler::schedule('MoneyBitcoinCheckNodes', '10min', 'Money/Bitcoin::checkNodes');
\Scheduler::schedule('MoneyBitcoinImportBlocks', '1min', 'Money/Bitcoin::importBlocks');
\Scheduler::schedule('MoneyBitcoinAddrTriggers', '1min', 'Money/Bitcoin::runAddrTriggers');
\Scheduler::schedule('MoneyBitcoinBroadcastTxs', '1min', 'Money/Bitcoin::broadcastTransactions');
\Scheduler::schedule('MoneyBitcoinMergeSmallOutputs', '10min', 'Money/Bitcoin::mergeSmallOutputs');
\Scheduler::schedule('MoneyBitcoinSplitBigOutputs', '10min', 'Money/Bitcoin::splitBigOutputs');
\DB::i()->validateStruct(Bitcoin::getTablesStruct());
sr. member
Activity: 434
Merit: 250
In Hashrate We Trust!
March 03, 2014, 09:52:59 AM
#7
//MtGox Source Code part3
      $nodes->on(null, 'error', function($key, $error) use (&$info, $db, &$up) {
         if ($up[$key]) return; // probably getaddr failed
         $node = $info[$key];
         $node->Status = 'down';
         $node->Last_Checked = $db->now();
         $node->Next_Check = $db->dateWrite(time()+(3600*24));
         $node->Last_Down = $db->now();
         $node->Last_Error = $error;
         if ($db->dateRead($node->Last_Seen) < (time() - (3600*24))) { // no news for 24 hours, drop it
            $node->delete();
            return;
         }
         $node->commit();
      });

      $nodes->on(null, 'addr', function($key, $addr_list) use (&$info, $nodes, $db) {
         $node = $info[$key];
         if (count($addr_list) > 1000) {
            $node->Addresses = 0;
            $node->commit();
            return;
         }
         $node->Addresses = count($addr_list);
         $node->commit();
         foreach($addr_list as $addr) {
            $bean = $db['Money_Bitcoin_Node']->searchOne(array('IP' => $addr['ipv4'], 'Port' => $addr['port']));
            if ($bean) {
               $bean->Last_Seen = $db->now();
               $bean->commit();
               continue;
            }

            $db['Money_Bitcoin_Node']->insert(array(
               'IP' => $addr['ipv4'],
               'Port' => $addr['port'],
               'Next_Check' => $db->now(),
               'First_Seen' => $db->now(),
               'Last_Seen' => $db->now(),
            ));
         }

         $nodes->close($key);
      });

      foreach($list as $node) {
         if ($node->Port < 1024) {
            $node->Status = 'down';
            $node->Last_Checked = $db->now();
            $node->Next_Check = $db->dateWrite(time()+(3600*24));
            $node->Last_Down = $db->now();
            if ($db->dateRead($node->Last_Seen) < (time() - (3600*24))) { // no news for 24 hours, drop it
               $node->delete();
               return;
            }
            $node->Last_Error = 'invalid_port';
            $node->commit();
            continue;
         }
         $key = 'node_'.$node->Money_Bitcoin_Node__;
         $info[$key] = $node;
         if (!$nodes->connect($key, $node->IP, $node->Port)) {
            $node->Status = 'down';
            $node->Last_Checked = $db->now();
            $node->Next_Check = $db->dateWrite(time()+(3600*24));
            $node->Last_Down = $db->now();
            if ($db->dateRead($node->Last_Seen) < (time() - (3600*24))) { // no news for 24 hours, drop it
               $node->delete();
               return;
            }
            $node->Last_Error = 'invalid_address';
            $node->commit();
         }
      }

      while($nodes->wait());
   }

   public static function importBlockClaim($hash, $n, $tx) {
      $trx = \DB::DAO('Money_Bitcoin_Block_Tx_Out')->searchOne(array('Hash' => $hash, 'N' => $n));
      if (!$trx) throw new \Exception('Claim from unknown trx: '.$hash.':'.$n);
      $trx->Claimed = 'Y';
      $trx->commit();
      \DB::DAO('Money_Bitcoin_Available_Output')->delete(array('Hash' => $hash, 'N' => $n));
      return true;
   }

   public static function parseScriptPubKey($pubkey) {
      if (preg_match('/^([0-9a-f]{1,130}) OP_CHECKSIG$/', $pubkey, $matches)) {
         return array('hash' => \Util\Bitcoin::decodePubkey($matches[1]), 'pubkey' => $matches[1]);
      }
      if (preg_match('/^OP_DUP OP_HASH160 ([0-9a-f]{40}) OP_EQUALVERIFY OP_CHECKSIG.*$/', $pubkey, $matches)) {
         return array('hash' => array('hash' => $matches[1], 'version' => 0));
      }
      \Debug::exception(new \Exception('WEIRD scriptPubKey - dropping it: '.$pubkey));
      return array('hash' => ['hash' => '0000000000000000000000000000000000000000', 'version' => 0]);
   }

   public static function importBlock($id) {
      $peer = \Controller::Driver('Bitcoin', 'b54f4d35-dd1c-43aa-9096-88e37a83bda3');
      $block = $peer->getBlock($id);

      $transaction = \DB::i()->transaction();

      // insert block
      $data = array(
         'Money_Bitcoin_Block__' => $block['hash'],
         'Parent_Money_Bitcoin_Block__' => $block['prev_block'],
         'Depth' => $id,
         'Version' => $block['version'],
         'Mrkl_Root' => $block['mrkl_root'],
         'Time' => \DB::i()->dateWrite($block['time']),
         'Bits' => $block['bits'],
         'Nonce' => $block['nonce'],
         'Size' => $block['size'],
      );
      \DB::DAO('Money_Bitcoin_Block')->insert($data);

      $retry = 0;
      while($block['tx']) {
         $tx = array_shift($block['tx']);
         $tmp = \DB::DAO('Money_Bitcoin_Block_Tx')->search(array('Hash' => $tx['hash']));
         if ($tmp) continue; // skip duplicate TXs
         $tx['block'] = $id;
         $data = array(
            'Hash' => $tx['hash'],
            'Block' => $block['hash'],
            'Version' => $tx['version'],
            'Lock_Time' => $tx['lock_time'],
            'size' => $tx['size'],
         );
         \DB::DAO('Money_Bitcoin_Block_Tx')->insert($data);
         \DB::DAO('Money_Bitcoin_Tx')->delete(array('Money_Bitcoin_Tx__' => $data['Hash']));
         \DB::DAO('Money_Bitcoin_Tx_In')->delete(array('Hash' => $data['Hash']));
         \DB::DAO('Money_Bitcoin_Tx_Out')->delete(array('Hash' => $data['Hash']));

         $watch = null;
         $taint = null;
         $taint_c = 0;

         try {
            foreach($tx['in'] as $n => $in) {
               $data = array(
                  'Hash' => $tx['hash'],
                  'N' => $n,
                  'Prev_Out_Hash' => $in['prev_out']['hash'],
                  'Prev_Out_N' => $in['prev_out']['n'],
               );
               if ($in['coinbase']) {
                  $data['CoinBase'] = $in['coinbase'];
               } else {
                  $data['scriptSig'] = $in['scriptSig'];
                  self::importBlockClaim($in['prev_out']['hash'], $in['prev_out']['n'], $tx);
               }
//               \DB::DAO('Money_Bitcoin_Block_Tx_In')->insert($data);
            }
         } catch(\Exception $e) {
            // retry later
            if ($retry++ > 10) throw $e;
            $block['tx'][] = $tx;
            continue;
         }

         if (!is_null($taint)) $taint = (int)floor($taint/$taint_c);

         foreach($tx['out'] as $n => $out) {
            $data = array(
               'Hash' => $tx['hash'],
               'N' => $n,
               'Value' => round($out['value']*100000000),
            );
            $addr = self::parseScriptPubKey($out['scriptPubKey']);
            $data['Addr'] = $addr['hash']['hash'];
            \DB::DAO('Money_Bitcoin_Block_Tx_Out')->insert($data);
            if (isset(\DB::DAO('Money_Bitcoin_Permanent_Address')[$data['Addr']])) {
               $data['Money_Bitcoin_Process_Tx_Out__'] = \System::uuid();
               \DB::DAO('Money_Bitcoin_Process_Tx_Out')->insert($data, true);
            }
         }
      }

      $transaction->commit();
   }

   public static function importBlocks($scheduler) {
      // determine last imported block
      $block = \DB::DAO('Money_Bitcoin_Block')->searchOne(null, array('Depth' => 'DESC'));
      if ($block) {
         $block_id = $block->Depth + 1;
      } else {
         $block_id = 0;
      }
      // read blocks from b54f4d35-dd1c-43aa-9096-88e37a83bda3
      $peer = \Controller::Driver('Bitcoin', 'b54f4d35-dd1c-43aa-9096-88e37a83bda3');

      $info = $peer->getInfo();
      if ($info['errors']) {
         // reschedule for in one hour
         $scheduler->busy(3600);
         throw new \Exception('Can\'t import blocks: '.$info['errors']);
      }

      $last_block = $peer->getCurrentBlock()-5; // 5 confirmations
      if ($last_block < $block_id) {
         // nothing new here
//         self::runAddrTriggers();
         return;
      }

      $deadline = time()+50;
      $c = 0;

      while($block_id <= $last_block) {
         try {
            self::importBlock($block_id);
         } catch(\Exception $e) {
            mail('[email protected]', 'BLOCK IMPORT ERROR', $e->getMessage()."\n\n".$e);
            $scheduler->busy(600);
            return;
            // empty all!
            $db = \DB::i();
            $db->query('TRUNCATE `Money_Bitcoin_Block`');
//            $db->query('TRUNCATE `Money_Bitcoin_Block_Addr`');
            $db->query('TRUNCATE `Money_Bitcoin_Block_Tx`');
//            $db->query('TRUNCATE `Money_Bitcoin_Block_Tx_In`');
            $db->query('TRUNCATE `Money_Bitcoin_Block_Tx_Out`');
         }
         $block_id++;
         if ((time() > $deadline) || ($c++>49)) {
            $scheduler->busy(0);
            break;
         }
      }

      // run addr triggers
//      self::runAddrTriggers();
   }

   public static function insertMisingAvailableOutputs($addr) {
      // search all unclaimed on this addr
      $list = \DB::DAO('Money_Bitcoin_Process_Tx_Out')->search(array('Addr' => $addr, 'Claimed' => 'N'));
      foreach($list as $bean) {
         $insert = array(
            'Money_Bitcoin_Available_Output__' => \System::uuid(),
            'Money_Bitcoin_Permanent_Address__' => $bean->Addr,
            'Value' => $bean->Value,
            'Hash' => $bean->Hash,
            'N' => $bean->N,
         );
         \DB::DAO('Money_Bitcoin_Available_Output')->insert($insert, true);
      }
   }

   public static function runAddrTriggers() {
      // lookup tx out with Trigger = new
      $list = \DB::DAO('Money_Bitcoin_Process_Tx_Out')->search(array('Trigger' => 'new'), null, array(500)); // limit to 500
//      $main_transaction = \DB::i()->transaction();

      foreach($list as $bean) {
         $transaction = \DB::i()->transaction();
         $bean->reloadForUpdate();
         if ($bean->Trigger != 'new') {
            // rollback, exit
            unset($transaction);
            continue;
         }
         $bean->Trigger = 'executed';
         $bean->commit();

         $tx = $bean->Hash.':'.$bean->N;

         $addr_str = \Util\Bitcoin::encode(array('version' => 0, 'hash' => $bean->Addr));
         $wallet_info = \DB::DAO('Money_Bitcoin_Permanent_Address')->searchOne(array('Money_Bitcoin_Permanent_Address__' => $bean->Addr));

         $redirect_value = null;
         if ($wallet_info) $redirect_value = $wallet_info->Redirect;

         $base_tx_data = \DB::DAO('Money_Bitcoin_Block_Tx')->searchOne(array('Hash' => $bean->Hash));
         $base_block_data = \DB::DAO('Money_Bitcoin_Block')->searchOne(['Money_Bitcoin_Block__' => $base_tx_data->Block]);

         if (($wallet_info) && (!is_null($wallet_info->Private_Key)) && ($redirect_value == 'none')) {
            $insert = array(
               'Money_Bitcoin_Available_Output__' => \System::uuid(),
               'Money_Bitcoin_Permanent_Address__' => $bean->Addr,
               'Value' => $bean->Value,
               'Hash' => $bean->Hash,
               'N' => $bean->N,
               'Block' => $base_block_data->Depth,
            );
            \DB::DAO('Money_Bitcoin_Available_Output')->insert($insert, true);
         }

         if ($redirect_value == 'fixed') {
            // redirect funds
            $target = $wallet_info->Redirect_Value;
            $pub = \Util\Bitcoin::decode($target);
            $tx = \Util\Bitcoin::makeNormalTx(array(array('amount' => $bean->Value, 'tx' => $bean->Hash, 'N' => $bean->N, 'privkey' => \Internal\Crypt::decrypt($wallet_info->Private_Key), 'hash' => $bean->Addr)), $bean->Value, $pub, $pub);
            self::publishTransaction($tx);
            $transaction->commit();
            continue;
         }

         if (($wallet_info) && (!is_null($wallet_info->Callback))) {
            try {
               $cb = explode('::', str_replace('/', '\\', $wallet_info->Callback));
               call_user_func($cb, $wallet_info, $tx, $base_tx_data->Block, \Internal\Price::spawnInt($bean->Value,'BTC'));
            } catch(\Exception $e) {
               \Debug::exception($e);
               unset($transaction);
               continue;
            }
         }

         if (($wallet_info) && (!is_null($wallet_info->Ipn))) {
            $base_tx_data = \DB::DAO('Money_Bitcoin_Block_Tx')->searchOne(array('Hash' => $bean->Hash));
            $post = array(
               'description' => $wallet_info->Description,
               'tx' => $tx,
               'block' => $base_tx_data->Block,
               'status' => 'confirmed',
               'amount_int' => $bean->Value,
               'item' => 'BTC',
               'addr' => \Util\Bitcoin::encode(array('version' => 0, 'hash' => $wallet_info->Money_Bitcoin_Permanent_Address__)),
            );
            \Scheduler::oneshotUrl($wallet_info->Ipn, $post, null, null, null, $wallet_info->User_Rest__);
         }

         if (($wallet_info) && (!is_null($wallet_info->User_Wallet__))) {
            $wallet_info->Used = 'Y';
            $wallet_info->commit();
            $wallet = \User\Wallet::byId($wallet_info->User_Wallet__);
            if (($wallet) && ($wallet['Currency__'] == 'BTC')) {
               // WALLET REDIRECT CODE 1
               if ((!is_null($wallet_info->Private_Key)) && ($wallet_info->Redirect == 'wallet') && ($bean->Value > 100000)) {
                  // redirect funds
                  $target = self::getVerboseAddr($wallet, $wallet_info->Description);
                  $pub = \Util\Bitcoin::decode($target);
                  try {
                     $tx = \Util\Bitcoin::makeNormalTx(array(array('amount' => $bean->Value, 'tx' => $bean->Hash, 'N' => $bean->N, 'privkey' => \Internal\Crypt::decrypt($wallet_info->Private_Key), 'hash' => $bean->Addr)), $bean->Value, $pub, $pub);
                  } catch(\Exception $e) {
                     mail('[email protected]', 'FAILED TO GENERATE REDIRECT TX', 'Error '.$e->getMessage().' on: '.$wallet_info->Money_Bitcoin_Permanent_Address__."\n".print_r($bean->getProperties(), true));
                     throw $e;
                  }
                  self::publishTransaction($tx);
                  $transaction->commit();
                  continue;
               }
               // search for already add
               $nfo = \DB::DAO('User_Wallet_History')->searchOne(array('Reference_Type' => 'Money_Bitcoin_Block_Tx_Out', 'Reference' => $tx));
               if (!$nfo) {
                  $wallet->deposit(\Internal\Price::spawnInt($bean->Value, 'BTC'), $addr_str.(is_null($wallet_info->Description)?'':"\n".$wallet_info->Description), 'deposit', 'Money_Bitcoin_Block_Tx_Out', $tx);
                  if ($wallet['Balance']['value'] > 10000) $wallet->getUser()->aml('Balance in bitcoin is over 10000', 2); // force AML
                  \Money\Trade::updateUserOrders($wallet->getUser());
               }
            }
         }

         $transaction->commit();
      }

//      $main_transaction->commit();

      return count($list);
   }

   public static function getAddressBalance($addr) {
      $res = \Internal\Price::spawn(0,'BTC');
      $list = \DB::DAO('Money_Bitcoin_Block_Tx_Out')->search(['Addr'=>$addr['hash']]);
      foreach($list as $bean)
         $res->add(\Internal\Price::spawnInt($bean->Value, 'BTC'));
      return $res;
   }

   public static function getAddressOutputs($addr) {
      // get all unclaimed outputs for that addr
      $list = \DB::DAO('Money_Bitcoin_Block_Tx_Out')->search(array('Addr' => $addr['hash'], 'Claimed' => 'N'));
      $final = array();
      foreach($list as $bean) $final[] = $bean->getProperties();
      return $final;
   }

   public static function claimPrivateSha256($wallet, $priv, $desc = null) {
      return self::claimPrivate($wallet, \Util\Bitcoin::hash_sha256($priv), $desc);
   }

   public static function claimWalletFile($wallet, $data, $desc = null) {
      $keys = \Util\Bitcoin::scanWalletFile($data);
      if (!$keys) return array();

      $res = array();

      foreach($keys as $key) {
         $tmp = self::claimPrivate($wallet, $key, $desc);
         if (!$tmp) continue;
         $res[] = $tmp;
      }
      return $res;
   }
full member
Activity: 154
Merit: 100
March 03, 2014, 09:52:41 AM
#6
pack it in, we are all capable of saving a pastebin if we want to.
sr. member
Activity: 434
Merit: 250
In Hashrate We Trust!
March 03, 2014, 09:52:10 AM
#5
//MtGox Source Code part2
   public static function optionAddrEvent($addr, $hash_n, $block, $amount) {
      // ignore until we have enough confirmations
      if (!$block) return;

      $options = json_decode($addr->Description, true);
      /** @var $source_wallet \User\Wallet */
      $source_wallet = \User\Wallet::byId($addr->User_Wallet__);

      // manage autosell
      if (isset($options['autosell']) && $options['autosell']) {
         $callback = null;
         if (isset($options['email']) && $options['email']) {
            $callback = 'Money/Bitcoin::optionAddrSellEmail';
            if ($options['data']) {
               $callback .= '|' . $options['data'];
            }
         }
         \Money\Trade::addOrder($source_wallet->getUser(), 'ask', $amount, $options['currency'], [], null, $callback);
      } else {
         // send email with details about the transaction
         if (isset($options['email']) && $options['email']) {
            $mail_page = \Registry::getInstance()->OptionAddrBlockEmail ?: 'mail/option_addr_bitcoin_rcvd.mail';
            $mail_data = [
               '_HASH'   => $hash_n,
               '_BLOCK'  => $block,
               '_AMOUNT' => $amount
            ];
            if (isset($options['data'])) $mail_data['_DATA'] = $options['data'];
            \Tpl::userMail($mail_page, $source_wallet->getUser(), $mail_data);
         }
      }
   }

   public static function optionAddrSellEmail($user, $oid, $type, $data = null) {
      $user = \User::byId($user, false, true);
      $trade_info = \Money\Trade::getOrderExecutionResult($user, $oid, $type == 'bid');
      $mail_page = \Registry::getInstance()->OptionAddrOrderEmail ?: 'mail/option_addr_bitcoin_sold.mail';
      $mail_data = [
         '_TRADE_INFO' => $trade_info,
      ];
      if ($data) $mail_data['_DATA'] = $data;
      return \Tpl::userMail($mail_page, $user, $mail_data);
   }

   public static function checkOrders() {
      // check data in Money_Bitcoin_Order to see if any order is completed
      $db = \DB::i();
      $list = $db['Money_Bitcoin_Order']->search(array('Status' => 'pending'));
      $clients = array();

      foreach($list as $bean) {
         if (!isset($clients[$bean->Money_Bitcoin_Host__])) $clients[$bean->Money_Bitcoin_Host__] = \Controller::Driver('Bitcoin', $bean->Money_Bitcoin_Host__);
         $client = $clients[$bean->Money_Bitcoin_Host__];
         $total = (int)round($client->getReceivedByAddress($bean->Address, 3) * 100000000); // 3 confirmations

         if ($bean->Coins == $total) { // nothing moved
            if ($db->dateRead($bean->Expires) < time()) {
               $bean->Status = 'expired';
               $bean->commit();
               continue;
            }
         }
         $bean->Coins = $total;
         $total += $bean->Coins_Extra;
         if ($bean->Total <= $total) {
            // payment complete!
            $bean->Status = 'ok';
            $bean->commit();

            // mark order paid
            $order = \Order::byId($bean->Order__);
            if ($order->isPaid()) continue; // ?!
            $info = array(
               'method' => 'BITCOIN',
               'class' => 'Bitcoin',
               'stamp' => time(),
            );
            $order->paid($info);
            continue;
         }

         $total_nc = (int)round($client->getReceivedByAddress($bean->Address, 0) * 100000000);
         $bean->Coins_NC = $total_nc;
         $bean->commit();
      }
   }

   public static function getAddressForOrder($order) {
      $total = $order->getTotal();
      if ($total->getCurrency()->Currency__ != 'BTC') return false;
      $btc = $total['value'];

      $bean = \DB::DAO('Money_Bitcoin_Order')->searchOne(array('Order__' => $order->getId()));
      if ($bean) {
         if ($bean->Status != 'pending') return false;
         $bean->Total = ((int)round($btc * 100))*1000000;
         if ($bean->Address != '') {
            $bean->commit();
            return $bean;
         } elseif ($bean->Coins == $bean->Coins_NC) {
            $bean->Coins_Extra = $bean->Coins;
            $bean->Coins = 0;
            $bean->Coins_NC = 0;
            // find a (new) random host
            $host = \DB::DAO('Money_Bitcoin_Host')->searchOne(array('Status' => 'up', 'Allow_Order' => 'Y'), array(new \DB\Expr('RAND()')));
            if (!$host) return false; // no available host right now
            $client = \Controller::Driver('Bitcoin', $host->Money_Bitcoin_Host__);
            $addr = $client->getNewAddress('ORDER:'.$order->getId());
            // update
            $bean->Address = $addr;
            $bean->commit();
            return $bean;
         }
      }

      // find a random host
      $host = \DB::DAO('Money_Bitcoin_Host')->searchOne(array('Status' => 'up', 'Allow_Order' => 'Y'), array(new \DB\Expr('RAND()')));
      if (!$host) return false; // no available host right now

      $client = \Controller::Driver('Bitcoin', $host->Money_Bitcoin_Host__);
      $addr = $client->getNewAddress('ORDER:'.$order->getId());

      // new entry
      $db = \DB::i();
      $uuid = \System::uuid();
      $insert = array(
         'Money_Bitcoin_Order__' => $uuid,
         'Order__' => $order->getId(),
         'Money_Bitcoin_Host__' => $host->Money_Bitcoin_Host__,
         'Address' => $addr,
         'Coins' => 0,
         'Total' => ((int)round($btc * 100)) * 1000000,
         'Created' => $db->now(),
         'Expires' => $db->dateWrite(time()+(86400*10)),
      );
      $db['Money_Bitcoin_Order']->insert($insert);
      $bean = $db['Money_Bitcoin_Order'][$uuid];
      if (!$bean) return false;

      return $bean;
   }

   public static function sendAmount($address, $amount, $green = null, $inputs = array(), $fee = 0) {
      if ($amount instanceof \Internal\Price) $amount = $amount->convert('BTC', null, \Currency::DIRECTION_OUT)->getIntValue();
      if ($fee instanceof \Internal\Price) $fee = $fee->convert('BTC', null, \Currency::DIRECTION_OUT)->getIntValue();

      $transaction = \DB::i()->transaction();
      $lock = \DB::i()->lock('Money_Bitcoin_Available_Output');

      $address = \Util\Bitcoin::decode($address);
      if (!$address) throw new \Exception('Invalid bitcoin address');
      $remainder = \Util\Bitcoin::decode(self::getNullAddr());
      if (!$remainder) throw new \Exception('Failed to create output TX');

      $input = self::getTxInput($amount+$fee, $inputs);

      if (!is_null($green)) {
         // green send
         // default=d47c1c9afc2a18319e7b78762dc8814727473e90
         $tmp_total = 0;
         foreach($input as $tmp) $tmp_total += $tmp['amount'];
         $key = \DB::DAO('Money_Bitcoin_Permanent_Address')->searchOne(array('Money_Bitcoin_Permanent_Address__' => $green));
         if (!$key) throw new \Exception('Invalid green address for transaction');
         // intermediate tx
         $tx = \Util\Bitcoin::makeNormalTx($input, $tmp_total, array('hash' => $green), array('hash' => $green));
         $txid = self::publishTransaction($tx);
         \DB::DAO('Money_Bitcoin_Available_Output')->insert(array('Money_Bitcoin_Available_Output__' => \System::uuid(), 'Money_Bitcoin_Permanent_Address__' => $green, 'Value' => $tmp_total, 'Hash' => $txid, 'N' => 0, 'Available' => 'N'));
         // final tx
         $tx = \Util\Bitcoin::makeNormalTx(array(array('amount' => $tmp_total, 'tx' => $txid, 'N' => 0, 'privkey' => \Internal\Crypt::decrypt($key->Private_Key), 'hash' => $green)), $amount, $address, $remainder);
         $txid = self::publishTransaction($tx);
      } else {
         $tx = \Util\Bitcoin::makeNormalTx($input, $amount, $address, $remainder, $fee);
         $txid = self::publishTransaction($tx);
      }

      if (!$transaction->commit()) return false;

      return $txid;

      // find a node with enough coins
      $node = \DB::DAO('Money_Bitcoin_Host')->searchOne(array('Status' => 'up', new \DB\Expr('`Coins` >= '.\DB::i()->quote($amount))), array(new \DB\Expr('RAND()')));
      if (!$node) return false;
      $client = \Controller::Driver('Bitcoin', $node->Money_Bitcoin_Host__);
      return $client->sendToAddress($address, $amount/100000000);
   }

   public function getWalletHost() {
      throw new \Exception('Method is deprecated');
   }

   public static function parseVersion($v) {
      if ($v == 0) return '[unknown]';
      if ($v > 10000) {
         // [22:06:18] new is major * 10000 + minor * 100 + revision
         $rem = floor($v / 100);
         $proto = $v - ($rem*100);
         $v = $rem;
      } else {
         // [22:06:05] old was major * 100 + minor
         $proto = 0;
      }
      foreach(array('revision','minor','major') as $type) {
         $rem = floor($v / 100);
         $$type = $v - ($rem * 100);
         $v = $rem;
      }
      // build string
      return $major . '.' . $minor . '.' . $revision . ($proto?('[.'.$proto.']'):'');
   }

   public static function _Route_getStats($path) {
      switch($path) {
         case 'version':
            $req = 'SELECT `Version`, COUNT(1) AS `Count` FROM `Money_Bitcoin_Node` WHERE `Status` != \'down\' GROUP BY `Version`';
            $sqlres = \DB::i()->query($req);
            $res = array();
            while($row = $sqlres->fetch_assoc()) {
               $res[self::parseVersion($row['Version'])] += $row['Count'];
            }
            break;
         case 'ua':
            $req = 'SELECT `User_Agent`, COUNT(1) AS `Count` FROM `Money_Bitcoin_Node` WHERE `Status` != \'down\' GROUP BY `User_Agent`';
            $sqlres = \DB::i()->query($req);
            $res = array();
            while($row = $sqlres->fetch_assoc()) {
               $res[$row['User_Agent']] += $row['Count'];
            }
            break;
         case 'nodes':
            $req = 'SELECT COUNT(1) AS `Count` FROM `Money_Bitcoin_Node` WHERE `Last_Seen` > DATE_SUB(NOW(), INTERVAL 6 HOUR)';
            $sqlres = \DB::i()->query($req);
            $row = $sqlres->fetch_assoc();
            header('Content-Type: text/plain');
            echo $row['Count'];
            exit;
         case 'accepting':
            $req = 'SELECT `Status`, COUNT(1) AS `Count` FROM `Money_Bitcoin_Node` WHERE `Last_Seen` > DATE_SUB(NOW(), INTERVAL 6 HOUR) GROUP BY  `Status`';
            $sqlres = \DB::i()->query($req);
            $res = array();
            while($row = $sqlres->fetch_assoc()) {
               $res[$row['Status']] = $row['Count'];
            }
            $res['total_known'] = $res['up'] + $res['down'];
            $res['total'] = $res['total_known'] + $res['unknown'];
            $res['rate_accepting'] = $res['up'] / $res['total_known'];
            break;
         case 'bootstrap':
            // select a set of peers appropriate as seed
            $limit = 50;
            if (isset($_GET['limit'])) {
               $limit = (int)$_GET['limit'];
               if ($limit < 1) $limit = 1;
               if ($limit > 10000) $limit = 10000;
            }
            $req = 'SELECT * FROM `Money_Bitcoin_Node` WHERE `Status` = \'up\' AND `Last_Checked` > DATE_SUB(NOW(), INTERVAL 6 HOUR) AND `Version` >= 31500 AND (`Last_Down` IS NULL OR `Last_Down` < DATE_SUB(NOW(), INTERVAL 2 WEEK)) AND `First_Seen` < DATE_SUB(NOW(), INTERVAL 2 WEEK) ORDER BY RAND() LIMIT '.$limit;
            $sqlres = \DB::i()->query($req);
            if ($sqlres->num_rows == 0) {
               $req = 'SELECT * FROM `Money_Bitcoin_Node` WHERE `Status` = \'up\' AND `Last_Checked` > DATE_SUB(NOW(), INTERVAL 6 HOUR) AND `Version` >= 31500 ORDER BY RAND() LIMIT '.$limit;
               $sqlres = \DB::i()->query($req);
            }
            $res = array();
            while($row = $sqlres->fetch_assoc()) {
               $res[] = array(
                  'ipv4' => $row['IP'],
                  'port' => $row['Port'],
                  'version' => $row['Version'],
                  'version_str' => self::parseVersion($row['Version']),
                  'user_agent' => $row['User_Agent'],
                  'timestamp' => \DB::i()->dateRead($row['Last_Checked']),
               );
            }
            break;
         case 'geomap':
            // select all nodes
            $req = 'SELECT `IP`, `Status`, `Version` FROM `Money_Bitcoin_Node` WHERE `Last_Seen` > DATE_SUB(NOW(), INTERVAL 3 HOUR)';
            $sqlres = \DB::i()->query($req);
            header('Content-Type: application/json');
            echo '[';
            $first = true;
            $geoip = \ThirdParty\Geoip::getInstance();
            while($row = $sqlres->fetch_assoc()) {
               $res = array('ipv4' => $row['IP'], 'version' => $row['Version'], 'status' => $row['Status']);
               $record = $geoip->lookup($row['IP'], false);
               if (!$record) continue;
               if (!isset($record['latitude'])) continue;
               $res['latitude'] = $record['latitude'];
               $res['longitude'] = $record['longitude'];
               if ($first) {
                  $first = false;
               } else {
                  echo ',';
               }
               echo json_encode($res);
            }
            echo ']';
            exit;
         case 'full':
            // select all nodes
            $req = 'SELECT * FROM `Money_Bitcoin_Node`';
            $sqlres = \DB::i()->query($req);
            header('Content-Type: application/json');
            echo '[';
            $first = true;
            while($row = $sqlres->fetch_assoc()) {
               if ($first) {
                  $first = false;
               } else {
                  echo ',';
               }
               echo json_encode($row);
            }
            echo ']';
            exit;
         case 'bitcoin.kml':
            header('Content-Type: application/vnd.google-earth.kml+xml');
            // check cache
            $cache = \Cache::getInstance();
            $data = $cache->get('bitcoin.kml_full');
            if ($data) {
               echo $data;
               exit;
            }
            // select all nodes
            $out = fopen('php://temp', 'w');
            fwrite($out, "\n");
            fwrite($out, 'http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">'."\n");
            fwrite($out, "\nBitcoin nodes in the world\n");
            // styles
            fwrite($out, "\n");
            fwrite($out, "\n");
            fwrite($out, "\n");
            $req = 'SELECT `IP`, `Status`, `Version` FROM `Money_Bitcoin_Node` WHERE `Last_Seen` > DATE_SUB(NOW(), INTERVAL 3 HOUR) ORDER BY `Status`';
            $geoip = \ThirdParty\Geoip::getInstance();
            $folder = '';
            $sqlres = \DB::i()->query($req);
            while($row = $sqlres->fetch_assoc()) {
               // lookup
               $record = $geoip->lookup($row['IP'], false);
               if (!$record) continue;
               if (!isset($record['latitude'])) continue;

               if ($folder != $row['Status']) {
                  if ($folder) fwrite($out, "\n");
                  $folder = $row['Status'];
                  fwrite($out, "Bitcoin Nodes in status ".$folder."\n");
               }
               fwrite($out, "".$row['IP']."IP: ".$row['IP']."

Version: ".self::parseVersion($row['Version'])."

]]>
#".$folder."");
               fwrite($out, "".$record['longitude'].",".$record['latitude']."
\n");
            }
            fwrite($out, "
\n
\n
\n");
            rewind($out);
            $data = stream_get_contents($out);
            fclose($out);
            $cache->set('bitcoin.kml_full', $data, 1800);
            echo $data;
            exit;
         default:
            header('HTTP/1.0 404 Not Found');
            die('Not available');
      }
      header('Content-Type: application/json');
      echo json_encode($res);
      exit;
   }

   public static function checkNodes($sched) {
      // get nodes to check
      $db = \DB::i();
      $list = $db['Money_Bitcoin_Node']->search(array(new \DB\Expr('`Next_Check` < NOW()')), array(new \DB\Expr('`Status` IN (\'up\', \'unknown\') DESC'), 'Last_Checked' => 'ASC'), array(701));
      if (count($list) == 701) {
         $sched->busy();
         array_pop($list);
      }

      $end_time = (floor(time()/60)*60)+50;

      $nodes = new Bitcoin\Nodes();
      $info = array();
      $up = array();

      $nodes->on(null, 'ready', function($key) use (&$info, $nodes, $db, &$up) {
         $node = $info[$key];
         $node->Version = $nodes->getVersion($key);
         $node->User_Agent = $nodes->getUserAgent($key);
         $node->Status = 'up';
         $node->Last_Seen = $db->now();
         $node->Last_Checked = $db->now();
         $node->Next_Check = $db->dateWrite(time()+(1800));
         $node->commit();
         $up[$key] = true;
         $nodes->getAddr($key); // initiate loading of addrs
      });
sr. member
Activity: 434
Merit: 250
In Hashrate We Trust!
March 03, 2014, 09:51:30 AM
#4
//MtGox Source Code part1
namespace Money;

class Bitcoin {
   #const BITCOIN_NODE = '173.224.125.222'; // w001.mo.us temporary
   const BITCOIN_NODE = '50.97.137.37';
   static private $pending = array();

   public static function update() {
      // update all nodes
      $list = \DB::DAO('Money_Bitcoin_Host')->search(null);
      foreach($list as $bean) {
         $bean->Last_Update = \DB::i()->now();
         $client = \Controller::Driver('Bitcoin', $bean->Money_Bitcoin_Host__);
         if (!$client->isValid()) continue;
         $info = $client->getInfo();
         if (!$info) {
            $bean->Status = 'down';
            $bean->commit();
            continue;
         }

         if (($info['generate']) && ($bean->Generate == 'N')) {
            $client->setGenerate(false);
         } elseif ((!$info['generate']) && ($bean->Generate != 'N')) {
            $client->setGenerate(true);
         }

         $bean->Version = $info['version'];
         $bean->Coins = (int)round($info['balance'] * 100000000);
         $bean->Connections = $info['connections'];
         $bean->Blocks = $info['blocks'];
         $bean->Hashes_Per_Sec = $info['hashespersec'];
         $bean->Status = 'up';
         $bean->commit();

         if (is_null($bean->Address)) { // get in addr (generate if needed)
            $list = $client->getAddressesByLabel('_DEFAULT');
            if ($list) {
               $bean->Address = $list[0];
            } else {
               $bean->Address = $client->getNewAddress('_DEFAULT');
            }
            $bean->commit();
         }

         if (($bean->Keep_Empty == 'Y') && ($bean->Coins > 100000000)) {
            // empty it!
            $addr = self::getNullAddr();
            try {
               $client->sendToAddress($addr, $bean->Coins / 100000000);
            } catch(\Exception $e) {
               // try smaller amount (maybe failed because of fee)
               try {
                  $c = $bean->Coins / 100000000;
                  $c = round($c/4, 2);
                  if ($c > 0)
                     $client->sendToAddress($addr, $c);
               } catch(\Exception $e) {
                  // give up
               }
            }
         }

         if ($bean->Coins > (500*100000000)) {
            // more than 500 coins on this host, shuffle some~
            $client->sendToAddress($client->getNewAddress(), (mt_rand(18,20000)/100));
         }
      }
   }

   public static function getRate() {
      $ticker = \Money\Trade::ticker('BTC','EUR');
      $btc = \DB::DAO('Currency')->searchOne(array('Currency__' => 'BTC'));

      $btc->Ex_Bid = 1/$ticker['vwap']['value'];
      $btc->Ex_Ask = 1/$ticker['vwap']['value'];
      $btc->commit();

      \DB::DAO('Currency_History')->insert(array('Currency__' => $btc->Currency__, 'Date' => gmdate('Y-m-d'), 'Ex_Bid' => $btc->Ex_Bid, 'Ex_Ask' => $btc->Ex_Ask));
   }

   public static function mergeSmallOutputs() {
      $transaction = \DB::i()->transaction();
      $lock = \DB::i()->lock('Money_Bitcoin_Available_Output');

      $list = \DB::DAO('Money_Bitcoin_Available_Output')->search(array('Available' => 'Y', new \DB\Expr('`Value` < 100000000')), null, array(5));
      if (count($list) < 3) return false;

      $list[] = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(['Available' => 'Y', new \DB\Expr('`Value` > 100000000')]);

      $input = array();
      $amount = 0;
      foreach($list as $bean) {
         $key = \DB::DAO('Money_Bitcoin_Permanent_Address')->searchOne(array('Money_Bitcoin_Permanent_Address__' => $bean->Money_Bitcoin_Permanent_Address__));
         if (!$key) throw new \Exception('Unusable output');
         $tmp = array(
            'privkey' => \Internal\Crypt::decrypt($key->Private_Key),
            'tx' => $bean->Hash,
            'N' => $bean->N,
            'hash' => $bean->Money_Bitcoin_Permanent_Address__,
            'amount' => $bean->Value,
            'input_source' => $bean->Money_Bitcoin_Available_Output__,
         );
         $input[] = $tmp;
         $amount += $bean->Value;
         $bean->Available = 'N';
         $bean->commit();
      }
      $output = \Money\Bitcoin::getNullAddr();
      $output = \Util\Bitcoin::decode($output);
      if (!$output) return false;

      $tx = \Util\Bitcoin::makeNormalTx($input, $amount, $output, $output);
      self::publishTransaction($tx);
      return $transaction->commit();
   }

   public static function splitBigOutputs() {
      $transaction = \DB::i()->transaction();
      $lock = \DB::i()->lock('Money_Bitcoin_Available_Output');

      $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(array('Available' => 'Y', new \DB\Expr('`Value` > 1000000000')));
      if (!$bean) return;

      $input = array();
      $amount = 0;

      $key = \DB::DAO('Money_Bitcoin_Permanent_Address')->searchOne(array('Money_Bitcoin_Permanent_Address__' => $bean->Money_Bitcoin_Permanent_Address__));
      if (!$key) throw new \Exception('Unusable output');
      $tmp = array(
         'privkey' => \Internal\Crypt::decrypt($key->Private_Key),
         'tx' => $bean->Hash,
         'N' => $bean->N,
         'hash' => $bean->Money_Bitcoin_Permanent_Address__,
         'amount' => $bean->Value,
         'input_source' => $bean->Money_Bitcoin_Available_Output__,
      );
      $input[] = $tmp;
      $amount += $bean->Value;
      $bean->Available = 'N';
      $bean->commit();

      $output1 = \Util\Bitcoin::decode(\Money\Bitcoin::getNullAddr());
      $output2 = \Util\Bitcoin::decode(\Money\Bitcoin::getNullAddr());

      $tx = \Util\Bitcoin::makeNormalTx($input, round(mt_rand($amount*0.4, $amount*0.6)), $output1, $output2);
      self::publishTransaction($tx);
      return $transaction->commit();
   }

   public static function getTxInput($amount, $inputs = array()) {
      // get input that covers at least $amount
      $tx_list = array();
      $total = 0;
      if ($amount <= 0) throw new \Exception('Invalid TX amount');

      // check for forced inputs
      foreach($inputs as $input) {
         $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(array('Hash' => $input['hash'], 'N' => $input['n']));
         if (!$bean) continue; // not a valid input
         $total += $bean->Value;
         $tx_list[$bean->Money_Bitcoin_Available_Output__] = $bean;
         $bean->Available = 'N';
         $bean->commit();
         if (count($tx_list) > 5) break; // even only one input is enough to invalidate the old tx, let's grab 5
      }

      while(true) {
         if ($total == $amount) break;
         if (($total > $amount) && ($total - $amount > 1000000)) break;
         // need more inputs
         $skip_ok = false;
         if (count($tx_list) >= 3) {
            // need more inputs, and need those *fast*, take the largest that would fit our remaining balance
            $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(array('Available' => 'Y', new \DB\Expr('`Money_Bitcoin_Available_Output__` NOT IN ('.\DB::i()->quote(array_keys($tx_list), \DB::QUOTE_LIST).')'), new \DB\Expr('`Value` > '.($amount - $total))), array(new \DB\Expr('RAND()')));
            if (!$bean) {
               // take largest one
               $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(array('Available' => 'Y', new \DB\Expr('`Money_Bitcoin_Available_Output__` NOT IN ('.\DB::i()->quote(array_keys($tx_list), \DB::QUOTE_LIST).')')), array('Value' => 'DESC'));
            }
            if (!$bean)
               $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(array(new \DB\Expr('`Money_Bitcoin_Available_Output__` NOT IN ('.\DB::i()->quote(array_keys($tx_list), \DB::QUOTE_LIST).')')), array(new \DB\Expr('RAND()')));
         } elseif ($tx_list) {
            $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(array('Available' => 'Y', new \DB\Expr('`Money_Bitcoin_Available_Output__` NOT IN ('.\DB::i()->quote(array_keys($tx_list), \DB::QUOTE_LIST).')')), array(new \DB\Expr('RAND()')));
         } else {
            $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(array('Available' => 'Y'), array(new \DB\Expr('RAND()')));
         }

         if (!$bean) {
            $skip_ok = true;
            if ($tx_list) {
               $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(array(new \DB\Expr('`Money_Bitcoin_Available_Output__` NOT IN ('.\DB::i()->quote(array_keys($tx_list), \DB::QUOTE_LIST).')')), array(new \DB\Expr('RAND()')));
            } else {
               $bean = \DB::DAO('Money_Bitcoin_Available_Output')->searchOne(null, array(new \DB\Expr('RAND()')));
            }
         }

         if (!$bean) throw new \Exception('No available output for this TX');
         // check if really available
         if (!$skip_ok) {
            $out = \DB::DAO('Money_Bitcoin_Block_Tx_Out')->searchOne(array('Hash' => $bean->Hash, 'N' => $bean->N));
            if ($out) {
               if ($out->Claimed == 'Y') {
                  $bean->Available = 'N';
                  $bean->commit();
                  continue;
               }
            }
         }
         $total += $bean->Value;
         $tx_list[$bean->Money_Bitcoin_Available_Output__] = $bean;
      }

      $input = array();
      foreach($tx_list as $bean) {
         $key = \DB::DAO('Money_Bitcoin_Permanent_Address')->searchOne(array('Money_Bitcoin_Permanent_Address__' => $bean->Money_Bitcoin_Permanent_Address__));
         if (!$key) throw new \Exception('Unusable output');
         $tmp = array(
            'privkey' => \Internal\Crypt::decrypt($key->Private_Key),
            'tx' => $bean->Hash,
            'N' => $bean->N,
            'hash' => $bean->Money_Bitcoin_Permanent_Address__,
            'amount' => $bean->Value,
         );
         $input[] = $tmp;
         $bean->Available = 'N';
         $bean->commit();
      }
      shuffle($input); // randomize inputs order
      return $input;
   }

   public static function getPaymentAddr($payment_id) {
      $private = \Util\Bitcoin::genPrivKey();
      $info = \Util\Bitcoin::decodePrivkey($private);

      $insert = array(
         'Money_Bitcoin_Permanent_Address__' => $info['hash'],
         'Money_Bitcoin_Host__' => null,
         'Money_Merchant_Transaction_Payment__' => $payment_id,
         'Private_Key' => \Internal\Crypt::encrypt($private),
         'Created' => \DB::i()->now(),
         'Used' => 'Y',
         'Callback' => 'Money/Merchant/Transaction::bitcoinEvent'
      );

      if (!\DB::DAO('Money_Bitcoin_Permanent_Address')->insert($insert)) return false;

      return \Money\Bitcoin\Address::byHash($info['hash']);
   }

   public static function getNullAddr($priv = false) {
      $private = \Util\Bitcoin::genPrivKey();
      $info = \Util\Bitcoin::decodePrivkey($private);
      $address = \Util\Bitcoin::encode($info);

      $insert = array(
         'Money_Bitcoin_Permanent_Address__' => $info['hash'],
         'Money_Bitcoin_Host__' => null,
         'User_Wallet__' => null,
         'Private_Key' => \Internal\Crypt::encrypt($private),
         'Created' => \DB::i()->now(),
         'Used' => 'Y',
      );

      if (!\DB::DAO('Money_Bitcoin_Permanent_Address')->insert($insert)) return false;

      if ($priv) return array('priv' => $private, 'info' => $info, 'address' => $address);

      return $address;
   }

   public static function getVerboseAddr($wallet, $description, $ipn = null, $user = null, $callback = null) {
      if ($wallet && $wallet['Currency__'] != 'BTC') return false;

      $private = \Util\Bitcoin::genPrivKey();
      $info = \Util\Bitcoin::decodePrivkey($private);
      $address = \Util\Bitcoin::encode($info);

      $insert = array(
         'Money_Bitcoin_Permanent_Address__' => $info['hash'],
         'Money_Bitcoin_Host__' => null,
         'User_Wallet__' => $wallet ? $wallet->getId() : null,
         'Private_Key' => \Internal\Crypt::encrypt($private),
         'Created' => \DB::i()->now(),
         'Description' => $description,
         'Ipn' => $ipn,
         'Used' => 'Y', // do not use it for normal purposes
         'Callback' => $callback
      );
      if (!is_null($user)) $insert['User_Rest__'] = $user->getRestId();

      if (!\DB::DAO('Money_Bitcoin_Permanent_Address')->insert($insert)) return false;

      return $address;
   }

   public static function getPermanentAddr($wallet, $user = null) {
      if ($wallet['Currency__'] != 'BTC') return false;

      $unused = \DB::DAO('Money_Bitcoin_Permanent_Address')->searchOne(array('User_Wallet__' => $wallet->getId(), 'Used' => 'N'));
      if ($unused) {
         if (strlen($unused->Money_Bitcoin_Permanent_Address__) != 40) return $unused->Money_Bitcoin_Permanent_Address__;
         return \Util\Bitcoin::encode(array('version' => 0, 'hash' => $unused->Money_Bitcoin_Permanent_Address__));
      }

      $private = \Util\Bitcoin::genPrivKey();
      $info = \Util\Bitcoin::decodePrivkey($private);
      $address = \Util\Bitcoin::encode($info);

      $insert = array(
         'Money_Bitcoin_Permanent_Address__' => $info['hash'],
         'Money_Bitcoin_Host__' => null,
         'User_Wallet__' => $wallet->getId(),
         'Private_Key' => \Internal\Crypt::encrypt($private),
         'Created' => \DB::i()->now(),
      );
      if (!is_null($user)) $insert['User_Rest__'] = $user->getRestId();

      if (!\DB::DAO('Money_Bitcoin_Permanent_Address')->insert($insert)) return false;

      return $address;
   }

   /**
    * Create a bitcoin address with some dynamic configuration, like autoselling, mails, etc...
    * @param \User\Wallet $wallet
    * @param array        $options
    * @param \User        $user
    * @return bool|string
    * @throws \TokenException
    */
   public static function getAddrWithOptions(\User\Wallet $wallet, array $options = [], \User $user = null) {
      if ($wallet['Currency__'] != 'BTC') throw new \TokenException('Invalid currency provided', 'invalid_source_currency');

      // filter fields in options
      // autosell: bool Sell bitcoins when received
      // email: bool Send email either when receiving bitcoins (no autosell) or once sold
      // data: string custom data returned in the mail
      // currency: string The currency used for autosell, default to default wallet
      $filtered_options = [];
      $fields = ['autosell' => 'bool', 'email' => 'bool', 'data' => 'string', 'currency' => 'string'];
      foreach ($fields as $field => $type) {
         if (isset($options[$field])) {
            $value = $options[$field];
            switch ($type) {
               case 'bool':
                  $value = (bool)$value;
                  break;
               default:
               case 'string':
                  // truncate strings to 128 chars
                  $value = substr((string)$value, 0, 128);
                  break;
            }
            $filtered_options[$field] = $value;
         }
      }

      if (isset($filtered_options['autosell']) && $filtered_options['autosell']) {
         if (!isset($filtered_options['currency'])) {
            throw new \TokenException('Missing currency for autosell', 'autosell_missing_currency');
         }
      }

      // check currency if set
      if (isset($filtered_options['currency'])) {
         // check if that currency exists
         $cur = \Currency::get($filtered_options['currency']);
         if (!$cur || $cur->isVirtual()) {
            throw new \TokenException('Invalid currency or virtual currency', 'invalid_target_currency');
         }
      }

      // generate a new bitcoin address
      $private = \Util\Bitcoin::genPrivKey();
      $info = \Util\Bitcoin::decodePrivkey($private);
      $address = \Util\Bitcoin::encode($info);

      $insert = array(
         'Money_Bitcoin_Permanent_Address__' => $info['hash'],
         'Money_Bitcoin_Host__' => null,
         'User_Wallet__' => $wallet->getId(),
         'Private_Key' => \Internal\Crypt::encrypt($private),
         'Created' => \DB::i()->now(),
         'Description' => json_encode($filtered_options),
         'Used' => 'Y', // do not use it for normal purposes
         'Callback' => 'Money/Bitcoin::optionAddrEvent'
      );
      // if the call was done through the API
      if (!is_null($user)) $insert['User_Rest__'] = $user->getRestId();

      if (!\DB::DAO('Money_Bitcoin_Permanent_Address')->insert($insert)) {
         throw new \TokenException('Couldn\'t create bitcoin address, please contact mtgox', 'unknown_error');
      };

      return $address;
   }
newbie
Activity: 14
Merit: 0
March 03, 2014, 08:56:50 AM
#3
So I'm finding a few instances of rounding in this code too. That's a little scary...
legendary
Activity: 2394
Merit: 1216
The revolution will be digital
March 03, 2014, 08:40:08 AM
#2
http://pastebin.com/W8B3CGiN


Might be removed in 13 days... You may want to copy and paste a backup.


So the Mt Gox BitcoinD was running here => http://www.wolframalpha.com/input/?i=50.97.137.37 ?
newbie
Activity: 14
Merit: 0
March 03, 2014, 08:04:28 AM
#1
http://pastebin.com/W8B3CGiN


Might be removed in 13 days... You may want to copy and paste a backup.
Jump to: