-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add api endpoint for signed message (close #105)
- Loading branch information
Showing
13 changed files
with
552 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import express from 'express' | ||
|
||
import register from './register.mjs' | ||
import message from './message.mjs' | ||
|
||
const router = express.Router() | ||
|
||
router.use('/register', register) | ||
router.use('/message', message) | ||
|
||
export default router |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import express from 'express' | ||
import { tools } from 'nanocurrency-web' | ||
|
||
import { rpc, verify_nano_community_message_signature } from '#common' | ||
import { | ||
ACCOUNT_TRACKING_MINIMUM_BALANCE, | ||
REPRESENTATIVE_TRACKING_MINIMUM_VOTING_WEIGHT | ||
} from '#constants' | ||
|
||
const router = express.Router() | ||
|
||
router.post('/?', async (req, res) => { | ||
const { logger, db } = req.app.locals | ||
try { | ||
const { message } = req.body | ||
|
||
const { | ||
version, | ||
|
||
entry_id, | ||
chain_id, | ||
entry_clock, | ||
chain_clock, | ||
|
||
public_key, | ||
operation, | ||
content, | ||
tags, | ||
|
||
references, | ||
|
||
created_at, | ||
|
||
signature | ||
} = message | ||
|
||
if (version !== 1) { | ||
res.status(400).send('Invalid message version') | ||
} | ||
|
||
// entry_id must be null or 32 byte hash | ||
if (entry_id && entry_id.length !== 64) { | ||
res.status(400).send('Invalid entry_id') | ||
} | ||
|
||
// chain_id must be null or 32 byte hash | ||
if (chain_id && chain_id.length !== 64) { | ||
res.status(400).send('Invalid chain_id') | ||
} | ||
|
||
// entry_clock must be null or positive integer | ||
if (entry_clock && entry_clock < 0) { | ||
res.status(400).send('Invalid entry_clock') | ||
} | ||
|
||
// chain_clock must be null or positive integer | ||
if (chain_clock && chain_clock < 0) { | ||
res.status(400).send('Invalid chain_clock') | ||
} | ||
|
||
// public_key must be 32 byte hash | ||
if (public_key.length !== 64) { | ||
res.status(400).send('Invalid public_key') | ||
} | ||
|
||
// operation must be SET or DELETE | ||
if (operation !== 'SET' && operation !== 'DELETE') { | ||
res.status(400).send('Invalid operation') | ||
} | ||
|
||
// content must be null or string | ||
if (content && typeof content !== 'string') { | ||
res.status(400).send('Invalid content') | ||
} | ||
|
||
// tags must be null or array of strings | ||
if (tags && !Array.isArray(tags)) { | ||
res.status(400).send('Invalid tags') | ||
} | ||
|
||
// references must be null or array of strings | ||
if (references && !Array.isArray(references)) { | ||
res.status(400).send('Invalid references') | ||
} | ||
|
||
// created_at must be null or positive integer | ||
if (created_at && created_at < 0) { | ||
res.status(400).send('Invalid created_at') | ||
} | ||
|
||
// signature must be 64 byte hash | ||
if (signature.length !== 128) { | ||
res.status(400).send('Invalid signature') | ||
} | ||
|
||
// validate signature | ||
const is_valid_signature = verify_nano_community_message_signature({ | ||
entry_id, | ||
chain_id, | ||
entry_clock, | ||
chain_clock, | ||
public_key, | ||
operation, | ||
content, | ||
tags, | ||
references, | ||
created_at, | ||
signature | ||
}) | ||
if (!is_valid_signature) { | ||
res.status(400).send('Invalid signature') | ||
} | ||
|
||
// public_key can be a linked keypair or an existing nano account | ||
|
||
const linked_accounts = await db('user_addresses') | ||
.select('user_addresses.address') | ||
.leftJoin('users', 'users.id', '=', 'user_addresses.user_id') | ||
.where({ public_key }) | ||
|
||
const nano_account = tools.publicKeyToAddress(public_key) | ||
|
||
const all_accounts = [ | ||
...linked_accounts.map((row) => row.address), | ||
nano_account | ||
] | ||
|
||
const accounts_info = [] | ||
for (const account of all_accounts) { | ||
const account_info = await rpc.accountInfo({ account }) | ||
if (account_info) { | ||
accounts_info.push(account_info) | ||
} | ||
} | ||
|
||
// check if any of the accounts have a balance beyond the tracking threshold | ||
const has_balance = accounts_info.some( | ||
(account_info) => account_info.balance > ACCOUNT_TRACKING_MINIMUM_BALANCE | ||
) | ||
|
||
// check if any of the accounts have weight beyond the tracking threshold | ||
const has_weight = accounts_info.some( | ||
(account_info) => | ||
account_info.weight > REPRESENTATIVE_TRACKING_MINIMUM_VOTING_WEIGHT | ||
) | ||
|
||
if (has_balance || has_weight) { | ||
// save the message to the database | ||
} | ||
|
||
console.log('here') | ||
|
||
res.status(200).send(message) | ||
Check failure Code scanning / CodeQL Reflected cross-site scripting High
Cross-site scripting vulnerability due to a
user-provided value Error loading related location Loading |
||
} catch (error) { | ||
logger.error(error) | ||
res.status(500).send('Internal server error') | ||
} | ||
}) | ||
Check failure Code scanning / CodeQL Missing rate limiting High
This route handler performs
authorization Error loading related location Loading |
||
|
||
export default router |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import ed25519 from '@trashman/ed25519-blake2b' | ||
|
||
export default function sign_nano_community_message(message, private_key) { | ||
const { | ||
entry_id, | ||
chain_id, | ||
entry_clock, | ||
chain_clock, | ||
public_key, | ||
operation, | ||
content, | ||
tags, | ||
references, | ||
created_at | ||
} = message | ||
|
||
const data = Buffer.from([ | ||
entry_id, | ||
chain_id, | ||
entry_clock, | ||
chain_clock, | ||
public_key, | ||
operation, | ||
content, | ||
tags, | ||
references, | ||
created_at | ||
]) | ||
|
||
const message_hash = ed25519.hash(data) | ||
|
||
return ed25519.sign(message_hash, private_key, public_key) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import ed25519 from '@trashman/ed25519-blake2b' | ||
|
||
export default function verify_nano_community_message_signature({ | ||
entry_id, | ||
chain_id, | ||
entry_clock, | ||
chain_clock, | ||
|
||
public_key, | ||
operation, | ||
content, | ||
tags, | ||
|
||
references, | ||
|
||
created_at, | ||
|
||
signature | ||
}) { | ||
const data = Buffer.from([ | ||
entry_id, | ||
chain_id, | ||
entry_clock, | ||
chain_clock, | ||
public_key, | ||
operation, | ||
content, | ||
tags, | ||
references, | ||
created_at | ||
]) | ||
|
||
const message_hash = ed25519.hash(data) | ||
|
||
return ed25519.verify(signature, message_hash, public_key) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.