Skip to content

Commit

Permalink
Merge pull request #330 from commerceblock/feat/testnet-option
Browse files Browse the repository at this point in the history
Feat/testnet option
  • Loading branch information
tomt1664 authored Mar 15, 2024
2 parents b315d93 + b26f6fd commit 69c2b15
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 161 deletions.
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

0 comments on commit 69c2b15

Please sign in to comment.