Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/testnet option #330

Merged
merged 6 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 6 additions & 14 deletions doc/mainstay_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,27 +575,15 @@ const pvtKey =
const commitment =
'F01111111111111111111111111111111111111111111111111111111111110F';


let keyPair = ec.keyFromPrivate("97ddae0f3a25b92268175400149d65d6887b9cefaf28ea2c078e05cdc15a3c0a");
let privKey = keyPair.getPrivate("hex");
let pubKey = keyPair.getPublic();

let signature = ec.sign(commitment, privKey, "hex", {canonical: true}).toDER('base64');

var payload = {
commitment: commitment,
position: 0,
token: '4c8c006d-4cee-4fef-8e06-bb8112db6314',
};

payload = new Buffer(JSON.stringify(payload)).toString('base64');

const options = {
url: url + route,
headers: {
'X-MAINSTAY-PAYLOAD': payload,
'X-MAINSTAY-SIGNATURE': signature
}
body: payload
};

request.post(options, (error, response, body) => {
Expand All @@ -607,7 +595,11 @@ request.post(options, (error, response, body) => {

**Curl example**
```perl
curl --header "Content-Type: application/json" --request POST --data '{"X-MAINSTAY-PLAYLOAD":"eyJwb3NpdGlvbiI6MCwiY29tbWl0bWVudCI6IkYwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMEYifQ==","X-MAINSTAY-SIGNATURE":"IJbqe50XtfZbQ1b0jr+J1tswSPfZlWwZugXCpYbwYMPuRl+htqSb7wTLYY9RtQ6Bw9Ym5dw0vMNRaDwR8pked2Y="}' http://localhost:9000/api/v1/commitment/send
curl --header "Content-Type: application/json" --request POST --data '{
"position": "0",
"token": "4c8c006d-4cee-4fef-8e06-bb8112db6314",
"commitment": "f3d424bf830dbd59eebc3f0a23491a266b7158635188e47b0e2abf7dbcc8931",
}' http://localhost:9000/api/v1/commitment/send
```

*response*
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "mainstay-mvc",
"scripts": {
"start": "HOST_API='localhost' PORT_API='4000' PORT='80' webpack-dev-server",
"dev": "HOST_API='localhost' PORT_API='4000' PORT='8080' webpack-dev-server",
"dev": "HOST_API='localhost' PORT_API='4000' PORT='8080' TESTNET='true' webpack-dev-server",
"test": "jest"
},
"dependencies": {
Expand Down
157 changes: 53 additions & 104 deletions src/controllers/api_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -408,103 +408,63 @@ module.exports = {

commitment_send: async (req, res) => {
const startTime = start_time();
let rawRequestData = '';
req.on('data', chunk => {
rawRequestData += chunk.toString();
});

req.on('end', async () => {
// test payload in base64 format and defined
let data;
let payload;
try {
data = JSON.parse(rawRequestData);
payload = JSON.parse(base64decode(data[MAINSTAY_PAYLOAD]));
} catch (e) {
return reply_err(res, BAD_ARG_PAYLOAD, startTime);
}
const payload = req.body;

if (payload === undefined) {
return reply_err(res, MISSING_ARG_PAYLOAD, startTime);
}
if (payload === undefined) {
return reply_err(res, MISSING_ARG_PAYLOAD, startTime);
}

// check payload components are defined
if (payload.commitment === undefined) {
return reply_err(res, MISSING_PAYLOAD_COMMITMENT, startTime);
}
if (payload.position === undefined) {
return reply_err(res, MISSING_PAYLOAD_POSITION, startTime);
}
if (payload.token === undefined) {
return reply_err(res, MISSING_PAYLOAD_TOKEN, startTime);
}
// check payload components are defined
if (payload.commitment === undefined) {
return reply_err(res, MISSING_PAYLOAD_COMMITMENT, startTime);
}
if (payload.position === undefined) {
return reply_err(res, MISSING_PAYLOAD_POSITION, startTime);
}
if (payload.token === undefined) {
return reply_err(res, MISSING_PAYLOAD_TOKEN, startTime);
}

if (/[0-9A-Fa-f]{64}/g.test(payload.commitment) === false) {
return reply_err(res, BAD_COMMITMENT, startTime);
}

if (payload.commitment.length !== 64) {
return reply_err(res, BAD_COMMITMENT, startTime);
try {
// try get client details
const data = await models.clientDetails.find({client_position: payload.position});
if (data.length === 0) {
return reply_err(res, POSITION_UNKNOWN, startTime);
}
if (payload.commitment.split('').every(c => '0123456789ABCDEFabcdef'.indexOf(c) !== -1)) {
return reply_err(res, BAD_COMMITMENT, startTime);
if (data[0].auth_token !== payload.token) {
return reply_err(res, PAYLOAD_TOKEN_ERROR, startTime);
}
if (data[0].expiry_date && new Date(data[0].expiry_date) < new Date()) {
return reply_err(res, EXPIRY_DATE_ERROR, startTime);
}

if (data[0].service_level === 'free') {

const signatureCommitment = data[MAINSTAY_SIGNATURE];
try {
// try get client details
const data = await models.clientDetails.find({client_position: payload.position});
if (data.length === 0) {
return reply_err(res, POSITION_UNKNOWN, startTime);
}
if (data[0].auth_token !== payload.token) {
return reply_err(res, PAYLOAD_TOKEN_ERROR, startTime);
}
if (data[0].expiry_date && new Date(data[0].expiry_date) < new Date()) {
return reply_err(res, EXPIRY_DATE_ERROR, startTime);
}

if (data[0].pubkey && data[0].pubkey !== '') {
if (signatureCommitment === undefined) {
return reply_err(res, MISSING_ARG_SIGNATURE, startTime);
}

try {
// get pubkey hex
const pubkey = ec.keyFromPublic(data[0].pubkey, 'hex');

// get base64 signature
let sig = Buffer.from(signatureCommitment, 'base64');

if (!ec.verify(payload.commitment, sig, pubkey)) {
return reply_err(res, SIGNATURE_INVALID, startTime);
}
} catch (error) {
return reply_err(res, SIGNATURE_INVALID, startTime);
}
}

if (data[0].service_level === 'free') {

const latest_com = await models.clientCommitment.find({client_position: payload.position});
let today = new Date().toLocaleDateString();
const latest_com = await models.clientCommitment.find({client_position: payload.position});
let today = new Date().toLocaleDateString();

if (latest_com[0].date === today) {
return reply_err(res, FREE_TIER_LIMIT, startTime);
} else {
await models.clientCommitment.findOneAndUpdate({client_position: payload.position}, {
commitment: payload.commitment,
date: today
}, {upsert: true});
reply_msg(res, 'Commitment added', startTime);
}
if (latest_com[0].date === today) {
return reply_err(res, FREE_TIER_LIMIT, startTime);
} else {
await models.clientCommitment.findOneAndUpdate({client_position: payload.position}, {commitment: payload.commitment}, {upsert: true});
await models.clientCommitment.findOneAndUpdate({client_position: payload.position}, {
commitment: payload.commitment,
date: today
}, {upsert: true});
reply_msg(res, 'Commitment added', startTime);
}

} catch (error) {
return reply_err(res, INTERNAL_ERROR_API, startTime);
} else {
await models.clientCommitment.findOneAndUpdate({client_position: payload.position}, {commitment: payload.commitment}, {upsert: true});
reply_msg(res, 'Commitment added', startTime);
}
});

} catch (error) {
return reply_err(res, INTERNAL_ERROR_API, startTime);
}
},

commitment_add: async (req, res) => {
Expand Down Expand Up @@ -540,7 +500,6 @@ module.exports = {
return reply_err(res, MISSING_PAYLOAD_TOKEN, startTime);
}

const signatureCommitment = data[MAINSTAY_SIGNATURE];
try {
// try get client details
const data = await models.clientDetails.find({client_position: payload.position});
Expand All @@ -556,26 +515,6 @@ module.exports = {
return reply_err(res, NO_ADDITIONS, startTime);
}

if (data[0].pubkey && data[0].pubkey !== '') {
if (signatureCommitment === undefined) {
return reply_err(res, MISSING_ARG_SIGNATURE, startTime);
}

try {
// get pubkey hex
const pubkey = ec.keyFromPublic(data[0].pubkey, 'hex');

// get base64 signature
let sig = Buffer.from(signatureCommitment, 'base64');

if (!ec.verify(payload.commitment, sig, pubkey)) {
return reply_err(res, SIGNATURE_INVALID, startTime);
}
} catch (error) {
return reply_err(res, SIGNATURE_INVALID, startTime);
}
}

//get all unconfirmed additions
const addunconfirmed = await models.commitmentAdd.find({
client_position: payload.position,
Expand Down Expand Up @@ -1304,6 +1243,16 @@ module.exports = {
const data = JSON.parse(rawRequestData);
const token_id = data[ARG_TOKEN_ID];
const slot_id = data[ARG_SLOT_ID];

if (token_id === undefined || token_id === '') {
const months = 12; // an year
const clientDetailsData = await create_slot_with_token(months);
reply_msg(res, {
auth_token: clientDetailsData.auth_token,
slot_id: clientDetailsData.client_position,
expiry_date: clientDetailsData.expiry_date
}, startTime);
}
const tokenDetails = await models.tokenDetails.findOne({token_id: token_id});

if (tokenDetails.amount >= FEE_RATE_PER_MONTH_IN_EUR) {
Expand Down
24 changes: 3 additions & 21 deletions src/controllers/ctrl_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ module.exports = {
if (payload.commitment === undefined) {
return res.json({error: 'Incorrect commitment'});
}
if (/[0-9A-Fa-f]{64}/g.test(payload.commitment) === false) {
return res.json({error: 'Non hex or non 64 byte commitment'});
}

const data = await models.clientDetails.find({client_position: payload.position});
if (data.length === 0) {
Expand All @@ -161,27 +164,6 @@ module.exports = {
if (data[0].expiry_date && new Date(data[0].expiry_date) < new Date()) {
return res.json({error: 'Expired token, renew it by paying for slot'});
}
if (data[0].pubkey && data[0].pubkey !== '') {
if (payload.signature === undefined) {
return res.json({error: 'signature'});
}
try {
// get pubkey hex
const pubkey = ec.keyFromPublic(data[0].pubkey, 'hex');

// get base64 signature
const sig = Buffer.from(payload.signature, 'base64');
if (!ec.verify(payload.commitment, sig, pubkey)) {
return res.json({error: 'signature'});
}

} catch (error) {
return res.json({
error: SIGNATURE_INVALID,
message: error.message
});
}
}

if (data[0].service_level === 'free') {

Expand Down
14 changes: 8 additions & 6 deletions src/view/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ class Home extends React.Component {

toggleSlotDetailsModal = () => {
this.setState({modalSlotDetails: !this.state.modalSlotDetails});
this.setState({slot_details: {
auth_token: '',
slot_id: '',
expiry_date: '',
new_slot: true,
}});
if (this.state.modalSlotDetails) {
this.setState({slot_details: {
auth_token: '',
slot_id: '',
expiry_date: '',
new_slot: true,
}});
}
};

setSlotDetails = (key, value) => {
Expand Down
5 changes: 1 addition & 4 deletions src/view/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const options = [
{label: 'Position', name: 'position'},
{label: 'Token', name: 'token'},
{label: 'Commitment', name: 'commitment'},
{label: 'signature', name: 'signature'},
];

class Menu extends Component {
Expand All @@ -27,7 +26,6 @@ class Menu extends Component {
position: undefined,
token: undefined,
commitment: undefined,
signature: undefined,
isMenuOpened: false,
};
}
Expand All @@ -38,12 +36,11 @@ class Menu extends Component {

handleSubmit = (event) => {
event.preventDefault();
const {position, token, commitment, signature} = this.state;
const {position, token, commitment} = this.state;
apiService.axiosClient.post('/ctrl/sendcommitment', {
position,
token,
commitment,
signature,
}).then(res => {
if (res.data?.error) {
const errorMessage = res.data.error === 'undefined'
Expand Down
23 changes: 19 additions & 4 deletions src/view/modals/CreateSlotModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ class CreateSlotModal extends React.PureComponent {
});
}

handleTokenForTestnet = (event) => {
event.preventDefault();
const token_id = this.props.slotDetails.token_id;
this.handleTokenForSpend(token_id);
this.props.toggleSlotDetailsModal();
this.handleModalClose();
};

resetFormState = () => {
this.formRef.current.reset();
this.setState({
Expand Down Expand Up @@ -199,10 +207,17 @@ class CreateSlotModal extends React.PureComponent {
) : null}
</ModalBody>
<ModalFooter>
{this.state.invoice === '' ? (
<Button color="success" type="submit" onClick={this.handleGenerateInvoice}>Generate Invoice</Button>
) : (
<Button color="success" type="submit" onClick={this.handleVerifyInvoice}>Verify</Button>
{process.env.TESTNET === 'true' && (
<Button color="success" type="submit" onClick={this.handleTokenForTestnet}>
Create Slot
</Button>
)}
{process.env.TESTNET !== 'true' && (
this.state.invoice === '' ? (
<Button color="success" type="submit" onClick={this.handleGenerateInvoice}>Generate Invoice</Button>
) : (
<Button color="success" type="submit" onClick={this.handleVerifyInvoice}>Verify</Button>
)
)}
</ModalFooter>
</Form>
Expand Down
Loading
Loading