moneroexamples 2017-07-24 11:16:51 +08:00
parent 2cef6fb998
commit 9647945261
4 changed files with 401 additions and 1 deletions

View File

@ -108,7 +108,6 @@ set(LIBRARIES
mnemonics
epee
easylogging
readline
${Boost_LIBRARIES}
pthread
unbound

View File

@ -642,6 +642,61 @@ curl -w "\n" -X GET "http://139.162.32.245:8081/api/networkinfo"
}
```
#### api/outputsblocks
Search for our outputs in last few blocks (up to 5 blocks), using provided address and viewkey.
```bash
# testnet address
curl -w "\n" -X GET http://127.0.0.1:8081/api/outputsblocks?address=9sDyNU82ih1gdhDgrqHbEcfSDFASjFgxL9B9v5f1AytFUrYsVEj7bD9Pyx5Sw2qLk8HgGdFM8qj5DNecqGhm24Ce6QwEGDi&viewkey=807079280293998634d66e745562edaaca45c0a75c8290603578b54e9397e90a&limit=5&mempool=1
```
Example result:
```json
{
{
"data": {
"address": "0182d5be0f708cecf2b6f9889738bde5c930fad846d5b530e021afd1ae7e24a687ad50af3a5d38896655669079ad0163b4a369f6c852cc816dace5fc7792b72f",
"height": 960526,
"limit": "5",
"mempool": true,
"outputs": [
{
"amount": 33000000000000,
"block_no": 0,
"in_mempool": true,
"output_idx": 1,
"output_pubkey": "2417b24fc99b2cbd9459278b532b37f15eab6b09bbfc44f9d17e15cd25d5b44f",
"payment_id": "",
"tx_hash": "9233708004c51d15f44e86ac1a3b99582ed2bede4aaac6e2dd71424a9147b06f"
},
{
"amount": 2000000000000,
"block_no": 960525,
"in_mempool": false,
"output_idx": 0,
"output_pubkey": "9984101f5471dda461f091962f1f970b122d4469077aed6b978a910dc3ed4576",
"payment_id": "0000000000000055",
"tx_hash": "37825d0feb2e96cd10fa9ec0b990ac2e97d2648c0f23e4f7d68d2298996acefd"
},
{
"amount": 96947454120000,
"block_no": 960525,
"in_mempool": false,
"output_idx": 1,
"output_pubkey": "e4bded8e2a9ec4d41682a34d0a37596ec62742b28e74b897fcc00a47fcaa8629",
"payment_id": "0000000000000000000000000000000000000000000000000000000000001234",
"tx_hash": "4fad5f2bdb6dbd7efc2ce7efa3dd20edbd2a91640ce35e54c6887f0ee5a1a679"
}
],
"viewkey": "807079280293998634d66e745562edaaca45c0a75c8290603578b54e9397e90a"
},
"status": "success"
}
```
#### api/emission
```bash

View File

@ -561,6 +561,37 @@ main(int ac, const char* av[])
return r;
});
CROW_ROUTE(app, "/api/outputsblocks").methods("GET"_method)
([&](const crow::request &req) {
string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ?
req.url_params.get("limit") : "3";
string address = regex_search(req.raw_url, regex {"address=\\w+"}) ?
req.url_params.get("address") : "";
string viewkey = regex_search(req.raw_url, regex {"viewkey=\\w+"}) ?
req.url_params.get("viewkey") : "";
bool in_mempool_aswell {false};
try
{
in_mempool_aswell = regex_search(req.raw_url, regex {"mempool=[01]"}) ?
boost::lexical_cast<bool>(req.url_params.get("mempool")) :
false;
}
catch (const boost::bad_lexical_cast &e)
{
cerr << "Cant parse tx_prove as bool. Using default value" << endl;
}
myxmr::jsonresponse r{xmrblocks.json_outputsblocks(limit, address, viewkey, in_mempool_aswell)};
return r;
});
}
if (enable_autorefresh_option)

View File

@ -4662,6 +4662,190 @@ namespace xmreg
return j_response;
}
json
json_outputsblocks(string _limit,
string address_str,
string viewkey_str,
bool in_mempool_aswell = false)
{
boost::trim(_limit);
boost::trim(address_str);
boost::trim(viewkey_str);
json j_response {
{"status", "fail"},
{"data", json {}}
};
json& j_data = j_response["data"];
uint64_t no_of_last_blocks {3};
try
{
no_of_last_blocks = boost::lexical_cast<uint64_t>(_limit);
}
catch (const boost::bad_lexical_cast& e)
{
j_data["title"] = fmt::format(
"Cant parse page and/or limit numbers: {:s}", _limit);
return j_response;
}
// maxium five last blocks
no_of_last_blocks = std::min(no_of_last_blocks, 5ul);
if (address_str.empty())
{
j_response["status"] = "error";
j_response["message"] = "Monero address not provided";
return j_response;
}
if (viewkey_str.empty())
{
j_response["status"] = "error";
j_response["message"] = "Viewkey not provided";
return j_response;
}
// parse string representing given monero address
cryptonote::account_public_address address;
if (!xmreg::parse_str_address(address_str, address, testnet))
{
j_response["status"] = "error";
j_response["message"] = "Cant parse monero address: " + address_str;
return j_response;
}
// parse string representing given private key
crypto::secret_key prv_view_key;
if (!xmreg::parse_str_secret_key(viewkey_str, prv_view_key))
{
j_response["status"] = "error";
j_response["message"] = "Cant parse view key: "
+ viewkey_str;
return j_response;
}
string error_msg;
j_data["outputs"] = json::array();
json& j_outptus = j_data["outputs"];
if (in_mempool_aswell)
{
// first check if there is something for us in the mempool
// get mempool tx from mempoolstatus thread
vector<MempoolStatus::mempool_tx> mempool_txs
= MempoolStatus::get_mempool_txs();
uint64_t no_mempool_txs = mempool_txs.size();
// need to use vector<transactions>,
// not vector<MempoolStatus::mempool_tx>
vector<transaction> tmp_vector;
tmp_vector.reserve(no_mempool_txs);
for (size_t i = 0; i < no_mempool_txs; ++i)
{
// get transaction info of the tx in the mempool
tmp_vector.push_back(std::move(mempool_txs.at(i).tx));
}
if (!find_our_outputs(
address, prv_view_key,
0 /* block_no */, true /*is mempool*/,
tmp_vector.cbegin(), tmp_vector.cend(),
j_outptus /* found outputs are pushed to this*/,
error_msg))
{
j_response["status"] = "error";
j_response["message"] = error_msg;
return j_response;
}
} // if (in_mempool_aswell)
// and now serach for outputs in last few blocks in the blockchain
uint64_t height = core_storage->get_current_blockchain_height();
// calculate starting and ending block numbers to show
int64_t start_height = height - no_of_last_blocks;
// check if start height is not below range
start_height = start_height < 0 ? 0 : start_height;
int64_t end_height = start_height + no_of_last_blocks - 1;
// loop index
int64_t block_no = end_height;
// iterate over last no_of_last_blocks of blocks
while (block_no >= start_height)
{
// get block at the given height block_no
block blk;
if (!mcore->get_block_by_height(block_no, blk))
{
j_response["status"] = "error";
j_response["message"] = fmt::format("Cant get block: {:d}", block_no);
return j_response;
}
// get transactions in the given block
list <cryptonote::transaction> blk_txs{blk.miner_tx};
list <crypto::hash> missed_txs;
if (!core_storage->get_transactions(blk.tx_hashes, blk_txs, missed_txs))
{
j_response["status"] = "error";
j_response["message"] = fmt::format("Cant get transactions in block: {:d}", block_no);
return j_response;
}
(void) missed_txs;
if (!find_our_outputs(
address, prv_view_key,
block_no, false /*is mempool*/,
blk_txs.cbegin(), blk_txs.cend(),
j_outptus /* found outputs are pushed to this*/,
error_msg))
{
j_response["status"] = "error";
j_response["message"] = error_msg;
return j_response;
}
--block_no;
} // while (block_no >= start_height)
// return parsed values. can be use to double
// check if submited data in the request
// matches to what was used to produce response.
j_data["address"] = pod_to_hex(address);
j_data["viewkey"] = pod_to_hex(prv_view_key);
j_data["limit"] = _limit;
j_data["height"] = height;
j_data["mempool"] = in_mempool_aswell;
j_response["status"] = "success";
return j_response;
}
/*
* Lets use this json api convention for success and error
* https://labs.omniti.com/labs/jsend
@ -4755,6 +4939,137 @@ namespace xmreg
private:
template <typename Iterator>
bool
find_our_outputs(
account_public_address const& address,
secret_key const& prv_view_key,
uint64_t const& block_no,
bool const& is_mempool,
Iterator const& txs_begin,
Iterator const& txs_end,
json& j_outptus,
string& error_msg)
{
// for each tx, perform output search using provided
// address and viewkey
for (auto it = txs_begin; it != txs_end; ++it)
{
cryptonote::transaction const& tx = *it;
tx_details txd = get_tx_details(tx);
// public transaction key is combined with our viewkey
// to create, so called, derived key.
key_derivation derivation;
if (!generate_key_derivation(txd.pk, prv_view_key, derivation))
{
error_msg = "Cant calculate key_derivation";
return false;
}
uint64_t output_idx{0};
std::vector<uint64_t> money_transfered(tx.vout.size(), 0);
//j_data["outputs"] = json::array();
//json& j_outptus = j_data["outputs"];
for (pair<txout_to_key, uint64_t> &outp: txd.output_pub_keys)
{
// get the tx output public key
// that normally would be generated for us,
// if someone had sent us some xmr.
public_key tx_pubkey;
derive_public_key(derivation,
output_idx,
address.m_spend_public_key,
tx_pubkey);
// check if generated public key matches the current output's key
bool mine_output = (outp.first.key == tx_pubkey);
// if mine output has RingCT, i.e., tx version is 2
if (mine_output && tx.version == 2)
{
// cointbase txs have amounts in plain sight.
// so use amount from ringct, only for non-coinbase txs
if (!is_coinbase(tx))
{
// initialize with regular amount
uint64_t rct_amount = money_transfered[output_idx];
bool r {false};
rct::key mask = tx.rct_signatures.ecdhInfo[output_idx].mask;
r = decode_ringct(tx.rct_signatures,
txd.pk,
prv_view_key,
output_idx,
mask,
rct_amount);
if (!r)
{
error_msg = "Cant decode ringct for tx: "
+ pod_to_hex(txd.hash);
return false;
}
outp.second = rct_amount;
money_transfered[output_idx] = rct_amount;
} // if (!is_coinbase(tx))
} // if (mine_output && tx.version == 2)
if (mine_output)
{
string payment_id;
// decrypt encrypted payment id, as used in integreated addresses
crypto::hash8 decrypted_payment_id8 = txd.payment_id8;
if (decrypted_payment_id8 != null_hash8)
{
if (decrypt_payment_id(decrypted_payment_id8, txd.pk, prv_view_key))
{
payment_id = pod_to_hex(decrypted_payment_id8);
}
}
else if(txd.payment_id != null_hash)
{
payment_id = pod_to_hex(txd.payment_id);
}
j_outptus.push_back(json {
{"output_pubkey" , pod_to_hex(outp.first.key)},
{"amount" , outp.second},
{"block_no" , block_no},
{"in_mempool" , is_mempool},
{"output_idx" , output_idx},
{"tx_hash" , pod_to_hex(txd.hash)},
{"payment_id" , payment_id}
});
}
++output_idx;
} // for (pair<txout_to_key, uint64_t>& outp: txd.output_pub_keys)
} // for (auto it = blk_txs.begin(); it != blk_txs.end(); ++it)
return true;
}
json
get_tx_json(const transaction& tx, const tx_details& txd)
{