public function gather_inputs($wallet_id, $amount, $privkeys = array()) { // Initialize global $config; $bip32 = new bip32(); $enc = new encrypt(); // Get wallet if (!($wallet = DB::queryFirstRow("SELECT * FROM coin_wallets WHERE id = %d", $wallet_id))) { trigger_error("Wallet does not exist, ID# {$wallet_id}", E_USER_ERROR); } // Go through inputs $inputs = array(); $input_amount = 0; $rows = DB::query("SELECT * FROM coin_inputs WHERE is_spent = 0 AND is_confirmed = 1 ORDER BY id"); foreach ($rows as $row) { if ($input_amount >= $amount) { break; } // Get address row if (!($addr_row = DB::queryFirstRow("SELECT * FROM coin_addresses WHERE address = %s", $row['address']))) { continue; } // Multisig address if ($wallet['address_type'] == 'multisig') { // Go through addresses $keys = array(); $public_keys = array(); $arows = DB::query("SELECT * FROM coin_addresses_multisig WHERE address = %s ORDER BY id", $row['address']); foreach ($arows as $arow) { // Get public key $keyindex = $addr_row['is_change_address'] . '/' . $arow['address_num']; $ext_pubkey = trim($enc->decrypt(DB::queryFirstField("SELECT public_key FROM coin_wallets_keys WHERE id = %d", $arow['key_id']))); $child_pubkey = $bip32->build_key($ext_pubkey, $keyindex)[0]; $import = $bip32->import($child_pubkey); $public_keys[] = $import['key']; // Go through private keys foreach ($privkeys as $privkey) { // Get child key $child_privkey = $bip32->build_key($privkey, $keyindex)[0]; $chk_pubkey = $bip32->extended_private_to_public($child_privkey); if ($chk_pubkey != $child_pubkey) { continue; } // Validate privkey if (!in_array($child_privkey, $keys)) { $keys[] = $child_privkey; } } } if (count($keys) < $wallet['sigs_required']) { continue; } // Add to inputs $vars = array('input_id' => $row['id'], 'txid' => $row['txid'], 'vout' => $row['vout'], 'amount' => $row['amount'], 'scriptsig' => $bip32->create_redeem_script($wallet['sigs_required'], $public_keys), 'public_keys' => $public_keys, 'privkeys' => $keys); array_push($inputs, $vars); // Standard address } else { // Get private key $keyindex = $addr_row['is_change_address'] . '/' . $addr_row['address_num']; $privkey = $bip32->build_key($privkeys[0], $keyindex)[0]; // Get script sig $decode_address = $bip32->base58_decode($row['address']); $scriptsig = '76a914' . substr($decode_address, 2, 40) . '88ac'; // Get public key $public_key = DB::queryFirstField("SELECT public_key FROM coin_wallets_keys WHERE wallet_id = %d ORDER BY id LIMIT 0,1", $wallet_id); $public_key = trim($enc->decrypt($public_key)); $child_pubkey = $bip32->build_key($public_key, $keyindex)[0]; // Validate key $chk_pubkey = $bip32->extended_private_to_public($privkey); if ($chk_pubkey != $child_pubkey) { continue; } // Add to inputs $vars = array('input_id' => $row['id'], 'txid' => $row['txid'], 'vout' => $row['vout'], 'amount' => $row['amount'], 'scriptsig' => $scriptsig, 'public_keys' => array($public_key), 'privkeys' => array($privkey)); array_push($inputs, $vars); } // Add to amounts $input_amount += $row['amount']; $amount += $config['btc_txfee']; } // Check amount if ($input_amount < $amount) { return false; } // Return return $inputs; }
continue; } // Get keyindexes if ($wrow['address_type'] == 'multisig') { $keyindexes = array(); $public_keys = array(); $addr_rows = DB::query("SELECT * FROM coin_addresses_multisig WHERE address = %s ORDER BY id", $row['address']); foreach ($addr_rows as $arow) { $keyindexes[] = $addr_row['is_change_address'] . '/' . $arow['address_num']; // Get public key $ext_public_key = trim($encrypt->decrypt(DB::queryFirstField("SELECT public_key FROM coin_wallets_keys WHERE id = %d", $arow['key_id']))); $child_key = $bip32->build_key($ext_public_key, $addr_row['is_change_address'] . '/' . $arow['address_num'])[0]; $import = $bip32->import($child_key); $public_keys[] = $import['key']; } $sigscript = $bip32->create_redeem_script($wrow['sigs_required'], $public_keys); } else { $keyindexes = $addr_row['is_change_address'] . '/' . $addr_row['address_num']; $decode_address = $bip32->base58_decode($row['address']); $sigscript = '76a914' . substr($decode_address, 2, 40) . '88ac'; } // Set vars $vars = array('input_id' => $row['id'], 'amount' => $row['amount'], 'txid' => $row['txid'], 'vout' => $row['vout'], 'sigscript' => $sigscript, 'keyindex' => $keyindexes); array_push($json['inputs'], $vars); } // Gather outputs $rows = DB::query("SELECT * FROM coin_sends WHERE status = 'pending' ORDER BY id"); foreach ($rows as $row) { // Gather recipients $recipients = array(); $arows = DB::query("SELECT * FROM coin_sends_addresses WHERE send_id = %d", $row['id']);
// Set variables $is_change = DB::queryFirstField("SELECT is_change_address FROM coin_addresses WHERE address = %s", $row['address']); // Get addresses $public_keys = array(); $keyindexes = array(); $addr_rows = DB::query("SELECT * FROM coin_addresses_multisig WHERE address = %s ORDER BY id", $row['address']); foreach ($addr_rows as $addr_row) { // Get public key & index $public_key = trim($enc->decrypt(DB::queryFirstField("SELECT public_key FROM coin_wallets_keys WHERE id = %d", $addr_row['key_id']))); $keyindex = $is_change . '/' . $addr_row['address_num']; $keyindexes[] = $keyindex; // Generate child key $child_ext_key = $bip32->build_key($public_key, $keyindex)[0]; $public_keys[] = $bip32->import($child_ext_key)['key']; } $scriptsig = $bip32->create_redeem_script($wallet['sigs_required'], $public_keys); $keyindex = implode(", ", $keyindexes); } else { $addr_row = DB::queryFirstRow("SELECT * FROM coin_addresses WHERE address = %s", $row['address']); $keyindex = $addr_row['is_change_address'] . '/' . $addr_row['address_num']; $decode_address = $bip32->base58_decode($row['address']); $scriptsig = '76a914' . substr($decode_address, 2, 40) . '88ac'; } // Add input $vars = array('input_id' => $row['id'], 'txid' => $row['txid'], 'vout' => $row['vout'], 'amount' => $row['amount'], 'keyindex' => $keyindex, 'scriptsig' => $scriptsig); array_push($inputs, $vars); $input_ids[] = $row['id']; } // Create transaction $client = new rawtx(); $trans = $client->create_transaction($send['wallet_id'], $inputs, $outputs);