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 2548 additions and 3694 deletions
<?php
namespace App\Repository;
use GuzzleHttp\Client;
class MergeRequest implements Proposal
{
private $merge_request;
public function __construct($merge_request)
{
$this->merge_request = $merge_request;
}
public function iid() : int
{
return $this->merge_request->iid;
}
public function id() : int
{
return $this->merge_request->id;
}
public function url() : string
{
return $this->merge_request->web_url;
}
public function title() : string
{
return $this->merge_request->title;
}
public function author() : string
{
return $this->merge_request->author->username;
}
public function created_at() : int
{
return strtotime($this->merge_request->created_at);
}
}
class Gitlab implements Repository
{
private $client;
private $base_url;
private const stateToString = [ State::Opened => 'opened',
State::Merged => 'merged'];
public function __construct(Client $client, string $repository_url)
{
$this->client = $client;
$this->base_url = $repository_url;
}
public function mergeRequests($state)
{
$url = $this->base_url . '/merge_requests?scope=all&per_page=50&state=' . Self::stateToString[$state];
$response = $this->client->request('GET', $url);
return collect(json_decode($response->getBody()))->map(function ($merge_request) {
return new MergeRequest($merge_request);
});
}
public function getNewFiles($merge_request)
{
$url = $this->base_url . '/merge_requests/' . $merge_request->iid() . '/changes';
$response = $this->client->request('GET', $url);
return collect(json_decode($response->getBody())->changes)->filter(function ($change) {
return $change->new_file;
})->map(function ($change) {
return $change->new_path;
});
}
}
<?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);
}
......@@ -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,11 +3,10 @@
use Faker\Generator as Faker;
$factory->define(\App\Project::class, function (Faker $faker) {
$state = $faker->randomElement(['OPENED', 'IDEA', 'FUNDING-REQUIRED', 'WORK-IN-PROGRESS', 'COMPLETED']);
$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(),
......
......@@ -17,7 +17,7 @@ class CreateProjectsTable extends Migration
$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('address_uri')->nullable();
$table->string('qr_code')->nullable();
......
......@@ -15,8 +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()));
}
public function getNewFiles($merge_request_iid) {
$url = env('GITLAB_URL') . '/merge_requests/' . $merge_request_iid . '/changes';
$response = $this->client->request('GET', $url, ['headers' => ['Private-Token' => env('GITLAB_ACCESS_TOKEN')]]);
$deserialized = collect(json_decode($response->getBody()));
$result = [];
foreach ($deserialized['changes'] as $change) {
if ($change->new_file) {
$result[] = $change->new_path;
}
}
return $result;
}
}
\ No newline at end of file
......@@ -37,11 +37,11 @@ interface WalletManager
public function blockHeight();
/**
* Creates a new integrated address
* Creates a new subaddress
*
* @return array ['integrated_address', 'payment_id']
* @return array ['address', 'address_index']
*/
public function createIntegratedAddress();
public function createSubaddress();
/**
* Gets any incoming transactions
......@@ -64,12 +64,12 @@ interface WalletManager
* 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);
public function createUri($address, $amount = null, $paymentId = null);
/**
* creates a random 64 char payment id
......
......@@ -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;
}
......@@ -6,8 +6,13 @@ use App\Project;
use Carbon\Carbon;
use Illuminate\Support\Collection;
class WalletOld
class WalletOld implements WalletCommon
{
public static function digitsAfterTheRadixPoint() : int
{
return 12;
}
/**
* WalletOld constructor.
*
......@@ -15,7 +20,9 @@ class WalletOld
*/
public function __construct($client = null)
{
$this->client = $client ?: new jsonRPCClient(env('RPC_URL'));
$this->client = $client ?: new jsonRPCClient([ 'username' => env('RPC_USER'),
'password' => env('RPC_PASSWORD'),
'url' => env('RPC_URL')]);
}
/**
......@@ -28,12 +35,12 @@ class WalletOld
public function getPaymentAddress()
{
$integratedAddress = $this->createIntegratedAddress();
if (!$integratedAddress) {
$subaddress = $this->createSubaddress();
if (!$subaddress) {
return ['address' => 'not valid'];
}
return ['address' => $integratedAddress['integrated_address'], 'paymentId' => $integratedAddress['payment_id']];
return ['address' => $subaddress['address'], 'subaddr_index' => $subaddress['address_index']];
}
/**
......@@ -46,71 +53,40 @@ class WalletOld
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 int $blockheight
* @param Collection $paymentIDs
*
* @return array|Transaction
*/
public function scanBlocks($blockheight, $paymentIDs)
{
$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;
}
}
return collect($transactions);
}
/**
* @param $blockheight
* @param $account_index
*
* @return \Illuminate\Support\Collection
*/
public function scanMempool($blockheight)
public function scanIncomingTransfers($min_height = 0, $account_index = 0)
{
$address = $this->getAddress();
$response = $this->client->incomingTransfers($min_height);
if (!$response) {
return collect([]);
}
$transactions = [];
$response = $this->mempoolTransfers();
if ($response && isset($response['pool'])) {
foreach ($response['pool'] as $payment) {
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'],
$address,
0,
$payment['address'],
$payment['confirmations'],
0,
Carbon::now(),
$payment['payment_id'],
$blockheight
$payment['subaddr_index']['minor'],
$payment['height']
);
$transactions[] = $transaction;
}
......@@ -124,7 +100,7 @@ class WalletOld
*
* @return int
*/
public function blockHeight()
public function blockHeight() : int
{
return $this->client->blockHeight();
}
......@@ -140,35 +116,34 @@ class WalletOld
}
/**
* 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($address, $amount = null, $paymentId = null): string
public function createQrCodeString($address, $amount = null): string
{
return $this->client->createUri($address, $amount, $paymentId);
return $this->client->createUri($address, $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;
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'];
}
}
......@@ -12,18 +12,7 @@ use Illuminate\Support\Facades\Log;
*/
class jsonRPCClient implements Contracts\WalletManager
{
/** @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 $rpc;
/**
* JsonRPCClient constructor.
......@@ -32,20 +21,7 @@ class jsonRPCClient implements Contracts\WalletManager
*/
public function __construct($options, $client = null)
{
$this->username = $options['username'] ?? $this->username;
$this->password = $options['password'] ?? $this->password;
$this->url = $options['url'] ?? $this->url;
if (empty($client)) {
$client = new Client([
'base_uri' => $this->url,
'headers' => [
'Content-Type' => 'application/json',
]
]);
}
$this->client = $client;
$this->rpc = new jsonRpcBase($options, $client);
}
/**
......@@ -55,7 +31,7 @@ class jsonRPCClient implements Contracts\WalletManager
*/
public function balance() : int
{
$response = $this->request('get_balance');
$response = $this->rpc->request('get_balance');
return $response['balance'];
}
......@@ -66,7 +42,7 @@ class jsonRPCClient implements Contracts\WalletManager
*/
public function unlockedBalance() : int
{
$response = $this->request('get_balance');
$response = $this->rpc->request('get_balance');
return $response['unlocked_balance'];
}
......@@ -77,7 +53,7 @@ class jsonRPCClient implements Contracts\WalletManager
*/
public function address() : string
{
$response = $this->request('get_address');
$response = $this->rpc->request('get_address');
return $response['address'];
}
......@@ -88,17 +64,21 @@ class jsonRPCClient implements Contracts\WalletManager
*/
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;
}
......@@ -109,7 +89,7 @@ class jsonRPCClient implements Contracts\WalletManager
*/
public function incomingTransfers($min_height = 0) : array
{
$response = $this->request('get_transfers', ['pool' => true, 'in' => true, 'min_height' => $min_height, 'filter_by_height' => $min_height > 0 ? true : false]);
$response = $this->rpc->request('get_transfers', ['pool' => true, 'in' => true, 'min_height' => $min_height, 'filter_by_height' => $min_height > 0 ? true : false]);
return $response;
}
......@@ -124,7 +104,7 @@ class jsonRPCClient implements Contracts\WalletManager
*/
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;
}
......@@ -133,14 +113,14 @@ class jsonRPCClient implements Contracts\WalletManager
* 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'];
}
......@@ -154,60 +134,4 @@ class jsonRPCClient implements Contracts\WalletManager
{
return bin2hex(openssl_random_pseudo_bytes(32));
}
/**
* 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
*/
protected function request(string $method, array $params = [])
{
$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'];
}
}
<?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'];
}
}
This diff is collapsed.
## About Monero FFS
# About Monero CCS
Monero FFS is a simple web system for capturing donations made to fund community projects
Monero CCS is a simple web system for capturing donations made to fund community projects
# CCS Deployment Quickstart
## Requirements
```
mysql >= 5.7.7
php >= 7.1
```
## Deployment
```
apt update
apt install -y cron git jekyll mysql-server nginx php php-curl php-fpm php-gd php-mbstring php-mysql php-xml unzip
```
Install `Composer` following the instructions at https://getcomposer.org/download/
Checkout and configure CCS backend, frontend and proposals repositories (replace `<REPOSITORY_CCS_BACKEND>`, `<REPOSITORY_CCS_FRONTEND>`, `<REPOSITORY_CCS_PROPOSALS>` with the actual URLs)
```
cd /var/www/html
git clone <REPOSITORY_CCS_BACKEND>
git clone <REPOSITORY_CCS_FRONTEND>
git clone <REPOSITORY_CCS_PROPOSALS> ccs-back/storage/app/proposals
rm -rf ccs-front/proposals
ln -s /var/www/html/ccs-back/storage/app/proposals ccs-front/proposals
ln -fs /var/www/html/ccs-back/storage/app/proposals.json ccs-front/_data/proposals.json
ln -fs /var/www/html/ccs-back/storage/app/complete.json ccs-front/_data/completed-proposals.json
cd ccs-back
composer update
cp .env.example .env
```
Spin up MYSQL server, create new database, user and grant user access to it
Open `.env` in editor of choice and edit the following lines:
> `COIN` - choose one of supported coins: `monero` or `zcoin`
> `REPOSITORY_URL` - CCS proposals Github URL or GitLab API endpoint (e.g. https://\<GITLAB_DOMAIN>/api/v4/projects/\<PROJECT_ID>)>
> `GITHUB_ACCESS_TOKEN` - leave empty if you are not using Github or visit https://github.com/settings/tokens to generate new `public_repo` token
```
APP_URL=http://<HOSTNAME>
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<DB_NAME>
DB_USERNAME=<DB_USER_NAME>
DB_PASSWORD=<DB_USER_PASSWORD>
RPC_URL=http://127.0.0.1:28080/json_rpc
RPC_USER=
RPC_PASSWORD=
COIN=<COIN>
REPOSITORY_URL=<REPOSITORY_URL>
GITHUB_ACCESS_TOKEN=
```
Initialize the system
```
php artisan migrate:fresh
php artisan up
php artisan key:generate
php artisan proposal:process
php artisan proposal:update
```
Grant `www-data` user access to the files
```
cd ..
chown -R www-data ccs-back/
chown -R www-data ccs-front/
```
Remove Nginx example config
```
rm /etc/nginx/sites-enabled/default
```
Create new file `/etc/nginx/sites-enabled/ccs` in editor of choice and paste the following lines replacing `<HOSTNAME>` and `<PHP_VERSION>` with appropriate values
```
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html/ccs-front/_site/;
index index.php index.html;
server_name <HOSTNAME>;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# pass the PHP scripts to FastCGI server
#
location ~ \.php$ {
root /var/www/html/ccs-back/public/;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php<PHP_VERSION>-fpm.sock;
}
}
```
```
service nginx reload
```
Set up a cron job that will run periodic updates (every minute) and generate static HTML files
```
* * * * * git -C /var/www/html/ccs-back/storage/app/proposals/ pull; php /var/www/html/ccs-back/artisan schedule:run; jekyll build --source /var/www/html/ccs-front --destination /var/www/html/ccs-front/_site
```
## Optional
Instead of scheduling a cron job you can run the following commands in no particular order
1. Update CCS system proposals intenal state
```
php /var/www/html/ccs-back/artisan proposal:process
php /var/www/html/ccs-back/artisan generate:addresses
php /var/www/html/ccs-back/artisan wallet:notify
php /var/www/html/ccs-back/artisan proposal:update
```
2. Process incoming donations
*Run it either on new block/tx notification or schedule it to run every minute or so*
```
php /var/www/html/ccs-back/artisan monero:notify
```
1. Generate static HTML files
```
jekyll build --source /var/www/html/ccs-front --destination /var/www/html/ccs-front/_site
```
2. Get the full list of processed transactions in JSON format
```
php /var/www/html/ccs-back/artisan deposit:list
```
......@@ -6,7 +6,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FFS</title>
<title>CCS - Donate {{$project->title}}</title>
<link rel="apple-touch-icon" sizes="180x180" href="/meta/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/meta/favicon-32x32.png">
......@@ -27,72 +27,84 @@
<div class="page-wrapper">
<div class="mob-nav">
<input class="burger-check" id="mobile-burger" type="checkbox"><label for="mobile-burger" class="burger"></label>
<input class="burger-check" id="mobile-burger" type="checkbox"><label for="mobile-burger" class="burger"></label>
<div class="slide-in-nav">
<div class="container slide-in">
<div class="row">
<div class="col-xs-12">
<div class="text-center nav-item mob">
<a href="/forum-funding-system/ideas/" class="top-link">Ideas</a>
</div>
<div class="text-center nav-item mob">
<a href="/forum-funding-system/funding-required/">Funding Required</a>
</div>
<div class="text-center nav-item mob">
<a href="/forum-funding-system/work-in-progress/">Work in Progress</a>
</div>
<div class="text-center nav-item mob">
<a href="/forum-funding-system/completed-proposals/">Completed Tasks</a>
</div>
<div class="text-center nav-item mob">
<a href="/forum-funding-system/completed-proposals/">Back to Getmonero.org</a>
</div>
</div>
</div>
</div>
<div class="container slide-in">
<div class="row">
<div class="col-xs-12">
<div class="text-center nav-item mob">
<a href="/ideas/" class="top-link">Ideas</a>
</div>
<div class="text-center nav-item mob">
<a href="/funding-required/">Funding Required</a>
</div>
<div class="text-center nav-item mob">
<a href="/work-in-progress/">Work in Progress</a>
</div>
<div class="text-center nav-item mob">
<a href="/completed-proposals/">Completed Tasks</a>
</div>
<div class="text-center nav-item mob">
<a href="/donate/index.html">Donate</a>
</div>
<div class="text-center nav-item mob">
<a href="/completed-proposals/">Back to Getmonero.org</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="desktop-nav">
<nav class="container">
</div>
<div class="desktop-nav">
<nav class="container">
<div class="row middle-xs">
<div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">
<p class="site-name"><a href="/">Community Crowdfunding System</a></p>
</div>
<div class="col-lg-8 col-md-8 col-sm-8 items end-xs">
<div class="row end-xs middle-xs">
<div class="col-md-12">
<div class="dropdown">
<label for="desktopdrop">CCS Stages<div class="arrow-down"></div></label>
<input class="burger-checkdropdown" id="desktopdrop" type="checkbox">
<div class="dropdown-content">
<a href="/ideas/">Ideas</a>
<a href="/funding-required/">Funding Required</a>
<a href="/work-in-progress/">Work in Progress</a>
<a href="/completed-proposals/">Completed Tasks</a>
</div>
</div>
<a href="https://getmonero.org">Getmonero.org</a>
<a href="/donate/" class="donate-btn">Donate</a>
</div>
</div>
</div>
</div>
</nav>
</div>
<div class="mob bot-nav white-nav">
<div class="row middle-xs">
<div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">
<a href="/forum-funding-system/"><img src="/img/monero-logo.png" alt="Monero Logo" class="monero-logo"></a>
</div>
<div class="col-lg-8 col-md-8 col-sm-8 items end-xs">
<div class="row end-xs middle-xs">
<div class="col-md-12">
<a href="/forum-funding-system/ideas/" class="top-link">Ideas</a>
<a href="/forum-funding-system/funding-required/">Funding Required</a>
<a href="/forum-funding-system/work-in-progress/">Work in Progress</a>
<a href="/forum-funding-system/completed-proposals/">Completed Tasks</a>
<a href="/forum-funding-system/completed-proposals/">Getmonero.org</a>
</div>
</div>
<div class="col-xs-12">
<p class="site-name"><a href="
/
">Community Crowdfunding System</a></p>
</div>
</div>
</nav>
</div>
<div class="mob bot-nav white-nav">
<div class="row middle-xs">
<div class="col-xs-6">
<a href="/
"><img src="/img/monero-logo.png" alt="Monero Logo" class="monero-logo"></a>
</div>
</div>
</div>
</div>
<div class="site-wrap ffs-proposal ffs-con">
<div class="container ffs-breadcrumbs">
<div class="row">
<div class="col-xs-12">
<p><a href="/forum-funding-system/">Forum Funding System</a></p>
<p><a href="/forum-funding-system/funding-required/">Funding Required</a></p>
<p><a href="/forum-funding-system/funding-required/">{{$project->title}}</a></p>
<p><a href="/">Community Crowdfunding System</a></p>
<p><a href="/funding-required/">Funding Required</a></p>
<p><a href="/proposals/{{pathinfo($project->filename, PATHINFO_FILENAME)}}.html">{{$project->title}}</a></p>
<p class="bread-active">Contribute</p>
</div>
</div>
......@@ -101,15 +113,14 @@
<section class="container full">
<div class="info-block">
<div class="row">
<div class="col-xs-12">
<div class="col-xs-12">
<h2>{{$project->title}}</h2>
<div class="row middle-xs between-xs">
<p class="author-list"><span><img src="/img/author-filled.png"></span>{{$project->author}}</p>
<p class="date-list"><span><img src="/img/calendar.png"></span>{{$project->github_created_at}}</p>
<p class="author-list">{{$project->author}}</p>
<p class="date-list">{{date('F j, Y', strtotime($project->created_at))}}</p>
<p class="date-list contributor">{{$project->contributions}}</p>
<p class="bar-fund-status">Raised <span class="progress-number-funded">{{$project->raised_amount}}</span> of <span class="progress-number-goal">{{$project->target_amount}}</span> XMR</p>
</div>
<div class="progress-bar">
<span class="fund-progress" style="width: [PERCENTAGE HERE]%"></span>
<span class="fund-progress" style="width: {{min(100, intval($project->raised_amount * 100 / $project->target_amount))}}%"></span>
</div>
<p>Your contribution should be visible within 5 minutes of you sending your contribution. If for some reason it is not there, please contact a member of the Core Team!</p>
</div>
......@@ -129,32 +140,21 @@
<p>1. Choose the amount of XMR you wish to contribute to this proposal</p>
<p>2. Scan this QR code or tap to open in your Monero wallet app:</p>
<p>
<a href="{{$project->address_uri}}" class="qr"><img src="{{ $project->qrCodeSrc}}"/></a>
<a href="{{$project->address_uri}}" class="qr"><img src="{{$project->qrCodeSrc}}"/></a>
</p>
<p>3. Send! Thank you! You are amazing!</p>
</div>
</div>
<input class="input" name="tabs" type="radio" id="tab-2"/>
<label class="label" for="tab-2">Integrated Address</label>
<label class="label" for="tab-2">Address</label>
<div class="panel col-xs-12">
<div class="panel-segment">
<h3>Contribute using an integrated address</h3>
<h3>Contribute using an address</h3>
<p>1. Choose the amount of XMR you wish to contribute to this proposal</p>
<p>2. Enter the following XMR address:</p> <p class="string">{{$project->address}}</p>
<p>3. Send! Thank you! You are amazing!</p>
</div>
</div>
<input class="input" name="tabs" type="radio" id="tab-3"/>
<label class="label" for="tab-3">Payment ID</label>
<div class="panel col-xs-12">
<div class="panel-segment">
<h3>Contribute using a payment ID</h3>
<p>1. Choose the amount of XMR you wish to contribute to this proposal</p>
<p>2. Enter the following XMR address:</p> <p class="string">{{ env('WALLET_ADDRESS') }}</p>
<p>3. Enter the following payment ID that is unique to this proposal:</p> <p class="string">{{$project->payment_id}}</p>
<p>4. Send! Thank you! You're amazing!</p>
</div>
</div>
</div>
</div>
</div>
......@@ -162,21 +162,26 @@
</div>
<footer class="container-fluid">
<div class="container">
<div class="row around-xs footer-wrapper">
<div class="row center-xs">
<div class="social-icons">
</div>
<div class="container">
<div class="row center-xs footer-wrapper">
<div class="col-md-8 col-sm-10 col-xs-12">
<h3>Donate to the Monero Project</h3>
<p>By donating to the following Monero address (General Fund), you are supporting the Monero Project. If you wish to donate to a specific proposal, please see <a href="/funding-required/index.html" class="white gf">Funding Required</a>.</p>
<p><a href="monero:44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A" class="qr"><img src="/img/donate-monero.png" /></a></p>
<p class="gf-address">44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A</p>
</div>
</div>
</div>
<div class="row center-xs">
<div class="footer-links">
<ul class="list-unstyled list-inline">
<li><a href="https://repo.getmonero.org/monero-project/ccs-front" class="white footer-link">CCS Front End Repo</a></li>
<li><a href="https://repo.getmonero.org/monero-project/ccs-back" class="white footer-link">CCS Backend Repo</a></li>
<li><a href="https://repo.getmonero.org/monero-project/ccs-proposals" class="white footer-link">CCS Proposals Repo</a></li>
</ul>
</div>
</div>
</div>
</div>
</footer>
</footer>
</div>
</body>
</html>
\ No newline at end of file
......@@ -10,11 +10,11 @@
<tbody>
@foreach ($projects as $project)
<tr>
<td><a href='{!! url('/projects/'.$project->payment_id); !!}'>{{ $project->payment_id }}</a></td>
<td><a href='{!! url('/projects/'.$project->subaddr_index); !!}'>{{ $project->subaddr_index }}</a></td>
<td>{{$project->status}}</td>
<td>{{$project->amount_received}} XMR</td>
<td>{{$project->raised_amount}} XMR</td>
<td>{{$project->target_amount}} XMR</td>
</tr>
@endforeach
</tbody>
</table>
\ No newline at end of file
</table>