Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • monero-project/ccs-back
  • xiphon/ccs-back
  • Fudin/ccs-back
  • john_r365/ccs-back
  • plowsofff/ccs-back
5 results
Show changes
Showing
with 14407 additions and 2080 deletions
<?php
namespace App\Repository;
use GuzzleHttp\Client;
interface State
{
const Merged = 0;
const Opened = 1;
const All = 2;
}
interface Proposal
{
public function id() : int;
public function url() : string;
public function title() : string;
public function author() : string;
public function created_at() : int;
}
interface Repository
{
public function __construct(Client $client, string $repository_url);
public function mergeRequests($state);
public function getNewFiles(Proposal $proposal);
}
......@@ -8,15 +8,17 @@
"php": "^7.1.3",
"ext-curl": "*",
"ext-json": "*",
"barryvdh/laravel-ide-helper": "^2.5",
"doctrine/dbal": "^2.8",
"ext-openssl": "*",
"fideloper/proxy": "^4.0",
"guzzlehttp/guzzle": "^6.3",
"laravel/framework": "5.7.*",
"laravel/tinker": "^1.0",
"simplesoftwareio/simple-qrcode": "2.0.*"
"simplesoftwareio/simple-qrcode": "2.0.*",
"symfony/yaml": "^4.2"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^2.5",
"doctrine/dbal": "^2.8",
"beyondcode/laravel-dump-server": "^1.0",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
......
This diff is collapsed.
......@@ -208,6 +208,7 @@ return [
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'Yaml' => 'Symfony\Component\Yaml\Yaml',
],
......
......@@ -4,7 +4,7 @@ use Faker\Generator as Faker;
$factory->define(\App\Deposit::class, function (Faker $faker) {
return [
'payment_id' => $faker->sha256,
'subaddr_index' => $faker->randomNumber(),
'amount' => $faker->randomNumber(2),
'time_received' => $faker->dateTime,
'tx_id' => $faker->sha256,
......
......@@ -3,13 +3,18 @@
use Faker\Generator as Faker;
$factory->define(\App\Project::class, function (Faker $faker) {
$status = $faker->randomElement(['opened', 'closed', 'locked', 'merged']);
$state = $faker->randomElement(['FUNDING-REQUIRED', 'WORK-IN-PROGRESS', 'COMPLETED']);
return [
'title' => $faker->sentence(),
'payment_id' => $faker->sha256,
'subaddr_index' => $faker->randomNumber(),
'address' => $faker->sha256,
'address_uri' => "monero:{$faker->sha256}",
'qr_code' => $faker->file(),
'target_amount' => $faker->randomFloat(2, 0, 2000),
'state' => $status,
'merge_request_id' => $faker->randomNumber(6),
'raised_amount' => $faker->randomFloat(2, 0, 2000),
'state' => $state,
'author' => $faker->userName,
'gitlab_url' => $faker->url,
'created_at' => $faker->dateTimeThisYear,
'updated_at' => $faker->dateTimeThisYear,
];
......
......@@ -15,12 +15,19 @@ class CreateProjectsTable extends Migration
{
Schema::create('projects', function (Blueprint $table) {
$table->increments('id');
$table->string('author');
$table->string('title');
$table->string('payment_id')->nullable();
$table->unsignedInteger('subaddr_index')->nullable();
$table->string('address')->nullable();
$table->string('target_amount')->nullable();
$table->string('state')->default('opened');
$table->unsignedInteger('merge_request_id')->unique();
$table->string('address_uri')->nullable();
$table->string('qr_code')->nullable();
$table->float('target_amount');
$table->float('raised_amount')->default(0);
$table->string('state');
$table->string('filename')->unique();
$table->unsignedInteger('milestones');
$table->unsignedInteger('milestones_completed')->default(0);
$table->string('gitlab_url')->nullable();
$table->timestamps();
});
}
......
......@@ -15,7 +15,8 @@ class CreateDepositsTable extends Migration
{
Schema::create('deposits', function (Blueprint $table) {
$table->increments('id');
$table->string('payment_id');
$table->unsignedInteger('confirmations')->default(0);
$table->unsignedInteger('subaddr_index');
$table->string('amount');
$table->dateTime('time_received');
$table->string('tx_id');
......
......@@ -12,7 +12,7 @@ class ProjectsTableSeeder extends Seeder
public function run()
{
factory(\App\Project::class, 20)->create()->each(function ($p) {
$p->deposits()->saveMany(factory(\App\Deposit::class, rand(0,12))->make(['payment_id' => $p->payment_id]));
$p->deposits()->saveMany(factory(\App\Deposit::class, rand(0,12))->make(['subaddr_index' => $p->subaddr_index]));
});
}
}
<?php
namespace GitLab;
use GuzzleHttp\Client;
class Connection
{
/** @var Client */
private $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function mergeRequests($state = 'all') {
$url = env('GITLAB_URL') . '/merge_requests?scope=all&per_page=50&state='. $state;
$response = $this->client->request('GET', $url, ['headers' => ['Private-Token' => env('GITLAB_ACCESS_TOKEN')]]);
return collect(json_decode($response->getBody()));
}
}
\ No newline at end of file
<?php
namespace Monero\Contracts;
/**
* Interface WalletManager
* @package Monero
*/
interface WalletManager
{
/**
* Gets the balance
*
* @return int the overall value after inputs unlock
*/
public function balance();
/**
* Gets the unlocked balance
*
* @return int the spendable balance
*/
public function unlockedBalance();
/**
* Gets the primary address
*
* @return string wallets primary address
*/
public function address();
/**
* Gets the current block height
*
* @return int block height
*/
public function blockHeight();
/**
* Creates a new subaddress
*
* @return array ['address', 'address_index']
*/
public function createSubaddress();
/**
* Gets any incoming transactions
*
* @return array
*/
public function incomingTransfers();
/**
* Checks for any payments made to the paymentIds
*
* @param array $paymentIds list of payment ids to be searched for
* @param int $minHeight the lowest block the search should start with
*
* @return array payments received since min block height with a payment id provided
*/
public function payments($paymentIds, $minHeight);
/**
* creates a uri for easier wallet parsing
*
* @param string $address address comprising of primary, sub or integrated address
* @param int $amount atomic amount requested
* @param string $paymentId payment id when not using integrated addresses
*
* @return string the uri string which can be used to generate a QR code
*/
public function createUri($address, $amount = null, $paymentId = null);
/**
* creates a random 64 char payment id
*
* @return string
*/
public function generatePaymentId();
}
......@@ -16,7 +16,7 @@ class Transaction
public $confirmations;
public $payment_id;
public $subaddr_index;
public $block_height;
......@@ -31,7 +31,7 @@ class Transaction
* @param $timeReceived
* @param $paymentId
*/
public function __construct($id, $amount, $address, $confirmations, $time, $timeReceived, $paymentId = null, $blockheight = null)
public function __construct($id, $amount, $address, $confirmations, $time, $timeReceived, $subaddr_index = null, $blockheight = null)
{
$this->amount = $amount;
$this->time_received = $timeReceived;
......@@ -39,7 +39,7 @@ class Transaction
$this->address = $address;
$this->id = $id;
$this->confirmations = $confirmations;
$this->payment_id = $paymentId;
$this->subaddr_index = $subaddr_index;
$this->block_height = $blockheight;
$this->correctTimeRecieved();
}
......
......@@ -28,15 +28,15 @@ class Wallet
public function getPaymentAddress()
{
$integratedAddress = $this->createIntegratedAddress();
if (!$integratedAddress) {
$subaddress = $this->createSubaddress();
if (!$subaddress) {
return ['address' => 'not valid', 'expiration_time' => 900];
}
$project = new Project();
$project->payment_id = $integratedAddress['payment_id'];
$project->subaddr_index = $subaddress['address_index'];
$project->save();
return ['address' => $integratedAddress['integrated_address'], 'paymentId' => $integratedAddress['payment_id']];
return ['address' => $subaddress['address'], 'subaddr_index' => $subaddress['address_index']];
}
/**
......@@ -49,71 +49,41 @@ class Wallet
return $this->client->balance();
}
public function mempoolTransfers()
{
return $this->client->incomingTransfers();
}
public function bulkPayments($paymentIds)
{
$blockBuffer = 10;
return $this->client->payments($paymentIds, intval($this->wallet->last_scanned_block_height) - $blockBuffer);
}
/**
* Scans the monero blockchain for transactions for the payment ids
*
* @param $blockheight
* @param $paymentIDs
* @param $min_height
* @param $account_index
*
* @return array|Transaction
* @return \Illuminate\Support\Collection
*/
public function scanBlocks($blockheight, $paymentIDs)
public function scanIncomingTransfers($min_height = 0, $account_index = 0)
{
$response = $this->bulkPayments($paymentIDs);
$address = $this->getAddress();
$transactions = [];
if ($response && isset($response['payments'])) {
foreach ($response['payments'] as $payment) {
$transaction = new Transaction(
$payment['tx_hash'],
$payment['amount'],
$address,
$blockheight - $payment['block_height'],
0,
Carbon::now(),
$payment['payment_id'],
$payment['block_height']
);
$transactions[] = $transaction;
}
$response = $this->client->incomingTransfers($min_height);
if (!$response) {
return collect([]);
}
return collect($transactions);
}
/**
* @param $blockheight
*
* @return \Illuminate\Support\Collection
*/
public function scanMempool($blockheight)
{
$address = $this->getAddress();
$transactions = [];
$response = $this->mempoolTransfers();
if ($response && isset($response['pool'])) {
foreach ($response['pool'] as $payment) {
const toScan = ['pool', 'in'];
foreach (toScan as $entry) {
if (!isset($response[$entry])) {
continue;
}
foreach ($response[$entry] as $payment) {
if $payment['subaddr_index']['major'] != $account_index {
continue;
}
if ($payment['locked']) {
continue;
}
$transaction = new Transaction(
$payment['txid'],
$payment['amount'],
$address,
0,
$payment['address'],
$payment['confirmations'],
0,
Carbon::now(),
$payment['payment_id'],
$blockheight
$payment['subaddr_index']['minor'],
$payment['height']
);
$transactions[] = $transaction;
}
......@@ -143,37 +113,34 @@ class Wallet
}
/**
* Returns XMR integrated address
* Returns XMR subaddress
*
* @return mixed
*/
public function createIntegratedAddress()
public function createSubaddress()
{
return $this->client->createIntegratedAddress();
return $this->client->createSubaddress();
}
/**
* @param $amount
* @param $address
* @param $paymentId
* @param $amount
*
* @return string
*/
public function createQrCodeString($amount, $address, $paymentId = ''): string
public function createQrCodeString($address, $amount): string
{
// @todo add tx_payment_id support
// monero payment_id is passed through the address
return 'monero:'.$address.'?tx_amount='.$amount;
}
/**
* gets all the payment_ids outstanding from the address_pool, we use these to check against the latest mined blocks
* gets all the subaddr_indexes outstanding from the address_pool, we use these to check against the latest mined blocks
*
* @return Collection
*/
public function getPaymentIds()
public function getSubaddressIndexes()
{
return Project::pluck('payment_id'); //stop scanning for payment_ids after 24h
return Project::pluck('subaddr_index'); //stop scanning for subaddr_index after 24h
}
}
<?php
namespace Monero;
interface WalletCommon
{
public static function digitsAfterTheRadixPoint() : int;
public function getPaymentAddress();
public function scanIncomingTransfers($min_height = 0);
public function blockHeight() : int;
public function createQrCodeString($address, $amount = null) : string;
}
<?php
namespace Monero;
use App\Project;
use Carbon\Carbon;
use Illuminate\Support\Collection;
class WalletOld implements WalletCommon
{
public static function digitsAfterTheRadixPoint() : int
{
return 12;
}
/**
* WalletOld constructor.
*
* @param null $client
*/
public function __construct($client = null)
{
$this->client = $client ?: new jsonRPCClient([ 'username' => env('RPC_USER'),
'password' => env('RPC_PASSWORD'),
'url' => env('RPC_URL')]);
}
/**
* Gets a Payment address for receiving payments
*
* @return array
*
* @internal param \WalletOld $wallet
*/
public function getPaymentAddress()
{
$subaddress = $this->createSubaddress();
if (!$subaddress) {
return ['address' => 'not valid'];
}
return ['address' => $subaddress['address'], 'subaddr_index' => $subaddress['address_index']];
}
/**
* Returns the actual available and useable balance (unlocked balance)
*
* @return float|int|mixed
*/
public function balance()
{
return $this->client->balance();
}
/**
* @param $blockheight
* @param $account_index
*
* @return \Illuminate\Support\Collection
*/
public function scanIncomingTransfers($min_height = 0, $account_index = 0)
{
$response = $this->client->incomingTransfers($min_height);
if (!$response) {
return collect([]);
}
$transactions = [];
foreach (['pool', 'in'] as $entry) {
if (!isset($response[$entry])) {
continue;
}
foreach ($response[$entry] as $payment) {
if ($payment['subaddr_index']['major'] != $account_index) {
continue;
}
if ($payment['locked']) {
continue;
}
$transaction = new Transaction(
$payment['txid'],
$payment['amount'],
$payment['address'],
$payment['confirmations'],
0,
Carbon::now(),
$payment['subaddr_index']['minor'],
$payment['height']
);
$transactions[] = $transaction;
}
}
return collect($transactions);
}
/**
* Gets the current blockheight of xmr
*
* @return int
*/
public function blockHeight() : int
{
return $this->client->blockHeight();
}
/**
* Returns monero wallet address
*
* @return string
*/
public function getAddress()
{
return $this->client->address();
}
/**
* Returns XMR subaddress
*
* @return mixed
*/
public function createSubaddress()
{
return $this->client->createSubaddress();
}
/**
* @param $address
* @param $amount
*
* @return string
*/
public function createQrCodeString($address, $amount = null): string
{
return $this->client->createUri($address, $amount);
}
/**
* gets all the subaddr_indexes outstanding from the address_pool, we use these to check against the latest mined blocks
*
* @return Collection
*/
public function getSubaddressIndexes()
{
return Project::pluck('subaddr_index'); //stop scanning for subaddr_index after 24h
}
}
<?php
namespace Monero;
use Carbon\Carbon;
class WalletZcoin implements WalletCommon
{
private $rpc;
public static function digitsAfterTheRadixPoint() : int
{
return 8;
}
public function __construct()
{
$this->rpc = new jsonRpcBase([ 'auth_type' => 'basic',
'username' => env('RPC_USER'),
'password' => env('RPC_PASSWORD'),
'url' => env('RPC_URL')]);
}
public function getPaymentAddress()
{
return ['address' => $this->rpc->request('getnewaddress')];
}
private function decodeTxAmount(string $tx_amount) : int
{
$tx_amount = str_replace(',', '.', $tx_amount);
$amount = explode('.', $tx_amount);
if (sizeof($amount) < 1 || sizeof($amount) > 2) {
throw new \Exception('Failed to decode tx amount ' . $tx_amount);
}
$fraction = $amount[1] ?? "";
if (strlen($fraction) > $this->digitsAfterTheRadixPoint()) {
throw new \Exception('Failed to decode tx amount, too many digits after the redix point ' . $tx_amount);
}
$amount = $amount[0] . str_pad($fraction, $this->digitsAfterTheRadixPoint(), '0');
$amount = intval($amount);
if ($amount == 0) {
throw new \Exception('Failed to convert tx amount to int ' . $tx_amount);
}
return $amount;
}
public function scanIncomingTransfers($skip_txes = 0)
{
return collect($this->rpc->request('listtransactions', ['', 100, $skip_txes]))->filter(function ($tx) {
return $tx['category'] == 'receive';
})->map(function ($tx) {
return new Transaction(
$tx['txid'],
$this->decodeTxAmount($tx['amount']),
$tx['address'],
$tx['confirmations'],
0,
Carbon::now(),
0,
isset($tx['blockhash']) ? $this->blockHeightByHash($tx['blockhash']) : 0
);
});
}
public function blockHeight() : int
{
return $this->rpc->request('getblockcount');
}
public function createQrCodeString($address, $amount = null) : string
{
return 'zcoin:' . $address . ($amount ? '?amount=' . $amount : '');
}
private function blockHeightByHash($block_hash) : int
{
return $this->rpc->request('getblockheader', [$block_hash])['height'];
}
}
......@@ -10,32 +10,18 @@ use Illuminate\Support\Facades\Log;
* Class jsonRPCClient
* JSON 2.0 RPC Client for cryptocurrency wallet
*/
class jsonRPCClient
class jsonRPCClient implements Contracts\WalletManager
{
/** @var string */
private $username;
/** @var string */
private $password;
/** @var Client|null */
private $client;
private $rpc;
/**
* JsonRPCClient constructor.
* @param array $options
* @param null $client
*/
public function __construct($client = null)
public function __construct($options, $client = null)
{
if (empty($client)) {
$client = new Client([
'base_uri' => env('RPC_URL'),
]);
}
$this->username = env('MONERO_USERNAME');
$this->password = env('MONERO_PASSWORD');
$this->client = $client;
$this->rpc = new jsonRpcBase($options, $client);
}
/**
......@@ -45,7 +31,7 @@ class jsonRPCClient
*/
public function balance() : int
{
$response = $this->request('get_balance');
$response = $this->rpc->request('get_balance');
return $response['balance'];
}
......@@ -56,7 +42,7 @@ class jsonRPCClient
*/
public function unlockedBalance() : int
{
$response = $this->request('get_balance');
$response = $this->rpc->request('get_balance');
return $response['unlocked_balance'];
}
......@@ -67,7 +53,7 @@ class jsonRPCClient
*/
public function address() : string
{
$response = $this->request('get_address');
$response = $this->rpc->request('get_address');
return $response['address'];
}
......@@ -78,17 +64,21 @@ class jsonRPCClient
*/
public function blockHeight() : int
{
$response = $this->request('get_height');
$response = $this->rpc->request('get_height');
return $response['height'];
}
/**
* Creates a new integrated address
* Creates a new subaddress
*
* @return array ['integrated_address', 'payment_id']
* @param int $account_index account index to create subaddress (maajor index)
* @param string $label label to assign to new subaddress
*
* @return array ['address', 'address_index']
*/
public function createIntegratedAddress() : array
public function createSubaddress($account_index = 0, $label = '') : array
{
$response = $this->request('make_integrated_address');
$response = $this->rpc->request('create_address', ['account_index' => $account_index, 'label' => $label]);
return $response;
}
......@@ -97,9 +87,9 @@ class jsonRPCClient
*
* @return array
*/
public function incomingTransfers() : array
public function incomingTransfers($min_height = 0) : array
{
$response = $this->request('get_transfers', ['pool' => true, 'in' => true]);
$response = $this->rpc->request('get_transfers', ['pool' => true, 'in' => true, 'min_height' => $min_height, 'filter_by_height' => $min_height > 0 ? true : false]);
return $response;
}
......@@ -114,7 +104,7 @@ class jsonRPCClient
*/
public function payments($paymentIds, $minHeight) : array
{
$response = $this->request('get_bulk_payments', ['payment_ids' => $paymentIds, 'min_block_height' => $minHeight]);
$response = $this->rpc->request('get_bulk_payments', ['payment_ids' => $paymentIds, 'min_block_height' => $minHeight]);
return $response;
}
......@@ -123,70 +113,25 @@ class jsonRPCClient
* creates a uri for easier wallet parsing
*
* @param string $address address comprising of primary, sub or integrated address
* @param string $paymentId payment id when not using integrated addresses
* @param int $amount atomic amount requested
* @param string $paymentId payment id when not using integrated addresses
*
* @return string the uri string which can be used to generate a QR code
*/
public function createUri($address, $paymentId = null, $amount = null) : string
public function createUri($address, $amount = null, $paymentId = null) : string
{
$response = $this->request('make_uri', ['address' => $address, 'amount' => $amount, 'payment_id' => $paymentId]);
$response = $this->rpc->request('make_uri', ['address' => $address, 'amount' => $amount, 'payment_id' => $paymentId]);
return $response['uri'];
}
/**
* Sets up the request data body
*
* @param string $method name of the rpc command
* @param array $params associative array of variables being passed to the method
*
* @return false|string will return a json string or false
*/
private function preparePayload($method, $params)
{
$payload = [
'jsonrpc' => '2.0',
'id' => '0',
'method' => $method,
'params' => $params,
];
return json_encode($payload);
}
/**
*
* @param string $method name of the rpc command
* @param array $params associative array of variables being passed to the method
* creates a random 64 char payment id
*
* @return mixed the rpc query result
*
* @throws \RuntimeException
* @return string
*/
protected function request(string $method, array $params = [])
public function generatePaymentId(): string
{
$payload = $this->preparePayload($method, $params);
try {
$response = $this->client->request('POST', '',[
'auth' => [$this->username, $this->password, 'digest'],
'body' => $payload,
'headers' => [
'Content-Type' => 'application/json',
]
]);
$body = $response->getBody();
} catch (GuzzleException $exception) {
Log::error($exception);
throw new \RuntimeException('Connection to node unsuccessful');
}
$result = json_decode((string) $body, true);
if (isset($result['error'])) {
throw new \RuntimeException($result['error']['message']);
}
return $result['result'];
return bin2hex(openssl_random_pseudo_bytes(32));
}
}
<?php
namespace Monero;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
class jsonRpcBase
{
/** @var string */
private $username = 'test2';
/** @var string */
private $password = 'test2';
/** @var string */
private $url = 'http://127.0.0.1:28080/json_rpc';
/** @var Client|null */
private $client;
private $auth_type;
/**
* JsonRPCClient constructor.
* @param array $options
* @param null $client
*/
public function __construct($options, $client = null)
{
$this->username = $options['username'] ?? $this->username;
$this->password = $options['password'] ?? $this->password;
$this->url = $options['url'] ?? $this->url;
$this->auth_type = $options['auth_type'] ?? 'digest';
if (empty($client)) {
$client = new Client([
'base_uri' => $this->url,
'headers' => [
'Content-Type' => 'application/json',
]
]);
}
$this->client = $client;
}
/**
* Sets up the request data body
*
* @param string $method name of the rpc command
* @param array $params associative array of variables being passed to the method
*
* @return false|string will return a json string or false
*/
private function preparePayload($method, $params)
{
$payload = [
'jsonrpc' => '2.0',
'id' => '0',
'method' => $method,
'params' => $params,
];
return json_encode($payload);
}
/**
* Send off request to rpc server
*
* @param string $method name of the rpc command
* @param array $params associative array of variables being passed to the method
*
* @return mixed the rpc query result
*
* @throws \RuntimeException
*/
public function request(string $method, array $params = [])
{
$payload = $this->preparePayload($method, $params);
try {
$response = $this->client->request('POST', '',[
'auth' => [$this->username, $this->password, $this->auth_type],
'body' => $payload,
'headers' => [
'Content-Type' => 'application/json',
]
]);
$body = $response->getBody();
} catch (GuzzleException $exception) {
Log::error($exception);
error_log($exception);
throw new \RuntimeException('Connection to node ' . $this->url . ' unsuccessful');
}
$result = json_decode((string) $body, true);
if (isset($result['error'])) {
throw new \RuntimeException($result['error']['message']);
}
return $result['result'];
}
}
server {
listen 80;
listen [::]:80;
root /var/www/moneroffs/public;
index index.php index.html index.htm;
server_name moneroffs.test;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
\ No newline at end of file
This diff is collapsed.