Skip to content

Commit 2f657ca

Browse files
committed
feat: get signature generation and validation working
1 parent 8c17cd9 commit 2f657ca

11 files changed

Lines changed: 2189 additions & 160 deletions

File tree

backend/package-lock.json

Lines changed: 1279 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,24 @@
55
"main": "src/server.js",
66
"scripts": {
77
"start": "node src/server.js",
8-
"dev": "nodemon src/server.js"
8+
"dev": "nodemon src/server.js",
9+
"test": "mocha"
910
},
1011
"author": "Brice Dobry",
1112
"license": "MIT",
1213
"dependencies": {
14+
"@noble/secp256k1": "^2.2.3",
1315
"@stacks/blockchain-api-client": "^8.5.0",
1416
"@stacks/network": "^7.0.2",
17+
"@stacks/transactions": "^7.0.2",
1518
"body-parser": "^1.20.3",
1619
"dotenv": "^16.4.7",
1720
"express": "^4.21.2",
1821
"pg": "^8.13.1"
1922
},
2023
"devDependencies": {
24+
"chai": "^5.1.2",
25+
"mocha": "^11.1.0",
2126
"nodemon": "^3.1.9"
2227
}
2328
}
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
const {
2+
CHANNEL_STATE,
3+
ACTION,
4+
OWNER,
5+
PRIVATE_KEY,
6+
} = require("../utils/constants");
7+
const pool = require("../utils/db");
8+
const { verifySignature, generateSignature } = require("../utils/signature");
9+
const { getChannel, insertSignatures } = require("../services/channelService");
10+
11+
async function handleTransfer(req, res) {
12+
const {
13+
amount,
14+
token,
15+
"principal-1": principal1,
16+
"principal-2": principal2,
17+
"balance-1": balance1,
18+
"balance-2": balance2,
19+
nonce,
20+
"hashed-secret": hashedSecret,
21+
signature,
22+
"next-hops": nextHops,
23+
"next-hop": nextHop,
24+
} = req.body;
25+
26+
try {
27+
const client = await pool.connect();
28+
await client.query("BEGIN");
29+
30+
const channel = await getChannel(client, principal1, principal2, token);
31+
32+
if (!channel) {
33+
return res.status(403).json({ error: "Channel does not exist." });
34+
}
35+
36+
const sender = principal1 === OWNER ? principal2 : principal1;
37+
38+
// Check if the nonce is valid
39+
if (BigInt(nonce) <= BigInt(channel.nonce)) {
40+
return res.status(409).json({ error: "Nonce conflict.", channel });
41+
}
42+
43+
// We will only automatically sign off on an incoming transfer and the
44+
// balances must be correct.
45+
const myBalance =
46+
principal1 === OWNER ? BigInt(balance1) : BigInt(balance2);
47+
const theirBalance =
48+
principal1 === OWNER ? BigInt(balance2) : BigInt(balance1);
49+
const myPrevBalance =
50+
principal1 === OWNER
51+
? BigInt(channel.balance1)
52+
: BigInt(channel.balance2);
53+
const theirPrevBalance =
54+
principal1 === OWNER
55+
? BigInt(channel.balance2)
56+
: BigInt(channel.balance1);
57+
if (
58+
myPrevBalance + BigInt(amount) !== myBalance ||
59+
theirPrevBalance - BigInt(amount) !== theirBalance
60+
) {
61+
return res.status(403).json({ error: "Invalid transfer balance.", channel });
62+
}
63+
64+
// Validate the signature
65+
const isValid = verifySignature(
66+
signature,
67+
68+
);
69+
70+
if (!isValid || BigInt(nonce) <= BigInt(channel.nonce)) {
71+
return res.status(403).json({ error: "Invalid transfer signature." });
72+
}
73+
74+
// Add the signature to the database
75+
const ownerSignature = generateSignature(PRIVATE_KEY, token);
76+
await insertSignatures(
77+
client,
78+
channel.id,
79+
balance1,
80+
balance2,
81+
nonce,
82+
"transfer",
83+
principal2,
84+
hashedSecret || null,
85+
ownerSignature,
86+
signature
87+
);
88+
89+
if (nextHops && nextHop !== null) {
90+
// Logic to initiate the next transfer hop
91+
console.log("Initiating next hop for the transfer:", nextHop);
92+
// TODO: Trigger next-hop transfer logic here
93+
}
94+
95+
await client.query("COMMIT");
96+
res.status(200).json({ signature: ownerSignature });
97+
} catch (error) {
98+
console.error("Error handling transfer:", error.message);
99+
res.status(500).json({ error: "Internal Server Error" });
100+
}
101+
}
102+
103+
async function handleDeposit(req, res) {
104+
const {
105+
amount,
106+
token,
107+
"principal-1": principal1,
108+
"principal-2": principal2,
109+
"balance-1": balance1,
110+
"balance-2": balance2,
111+
nonce,
112+
signature,
113+
} = req.body;
114+
115+
try {
116+
const client = await pool.connect();
117+
await client.query("BEGIN");
118+
119+
const channel = await getChannel(client, principal1, principal2, token);
120+
121+
if (!channel) {
122+
return res.status(403).json({ error: "Channel does not exist." });
123+
}
124+
125+
if (BigInt(nonce) <= BigInt(channel.nonce)) {
126+
return res.status(409).json({ error: "Nonce conflict." });
127+
}
128+
129+
// Validate the deposit action
130+
const isValid = verifySignature(
131+
signature,
132+
{ balance1, balance2, nonce, amount },
133+
principal2
134+
);
135+
136+
if (!isValid) {
137+
return res.status(403).json({ error: "Invalid deposit signature." });
138+
}
139+
140+
// Add the signature to the database
141+
const ownerSignature = generateSignature(
142+
{ balance1, balance2, nonce, amount },
143+
principal1
144+
);
145+
await insertSignatures(
146+
client,
147+
channel.id,
148+
balance1,
149+
balance2,
150+
nonce,
151+
"deposit",
152+
principal2,
153+
null,
154+
ownerSignature,
155+
signature
156+
);
157+
158+
await client.query("COMMIT");
159+
res.status(200).json({ signature: ownerSignature });
160+
} catch (error) {
161+
console.error("Error handling deposit:", error.message);
162+
res.status(500).json({ error: "Internal Server Error" });
163+
}
164+
}
165+
166+
async function handleWithdraw(req, res) {
167+
const {
168+
amount,
169+
token,
170+
"principal-1": principal1,
171+
"principal-2": principal2,
172+
"balance-1": balance1,
173+
"balance-2": balance2,
174+
nonce,
175+
signature,
176+
} = req.body;
177+
178+
try {
179+
const client = await pool.connect();
180+
await client.query("BEGIN");
181+
182+
const channel = await getChannel(client, principal1, principal2, token);
183+
184+
if (!channel) {
185+
return res.status(403).json({ error: "Channel does not exist." });
186+
}
187+
188+
if (BigInt(nonce) <= BigInt(channel.nonce)) {
189+
return res.status(409).json({ error: "Nonce conflict." });
190+
}
191+
192+
// Validate the withdrawal action
193+
const isValid = verifySignature(
194+
signature,
195+
{ balance1, balance2, nonce, amount },
196+
principal2
197+
);
198+
199+
if (!isValid) {
200+
return res.status(403).json({ error: "Invalid withdraw signature." });
201+
}
202+
203+
// Add the signature to the database
204+
const ownerSignature = generateSignature(
205+
{ balance1, balance2, nonce, amount },
206+
principal1
207+
);
208+
await insertSignatures(
209+
client,
210+
channel.id,
211+
balance1,
212+
balance2,
213+
nonce,
214+
"withdraw",
215+
principal2,
216+
null,
217+
ownerSignature,
218+
signature
219+
);
220+
221+
await client.query("COMMIT");
222+
res.status(200).json({ signature: ownerSignature });
223+
} catch (error) {
224+
console.error("Error handling withdraw:", error.message);
225+
res.status(500).json({ error: "Internal Server Error" });
226+
}
227+
}
228+
229+
async function handleClose(req, res) {
230+
const {
231+
token,
232+
"principal-1": principal1,
233+
"principal-2": principal2,
234+
"balance-1": balance1,
235+
"balance-2": balance2,
236+
nonce,
237+
signature,
238+
} = req.body;
239+
240+
try {
241+
const client = await pool.connect();
242+
await client.query("BEGIN");
243+
244+
const channel = await getChannel(client, principal1, principal2, token);
245+
246+
if (!channel) {
247+
return res.status(403).json({ error: "Channel does not exist." });
248+
}
249+
250+
if (BigInt(nonce) <= BigInt(channel.nonce)) {
251+
return res.status(409).json({ error: "Nonce conflict." });
252+
}
253+
254+
// Validate the close action
255+
const isValid = verifySignature(
256+
signature,
257+
{ balance1, balance2, nonce },
258+
principal2
259+
);
260+
261+
if (!isValid) {
262+
return res.status(403).json({ error: "Invalid close signature." });
263+
}
264+
265+
// Add the signature to the database
266+
const ownerSignature = generateSignature(
267+
{ balance1, balance2, nonce },
268+
principal1
269+
);
270+
await insertSignatures(
271+
client,
272+
channel.id,
273+
balance1,
274+
balance2,
275+
nonce,
276+
"close",
277+
principal2,
278+
null,
279+
ownerSignature,
280+
signature
281+
);
282+
283+
await client.query("COMMIT");
284+
res.status(200).json({ signature: ownerSignature });
285+
} catch (error) {
286+
console.error("Error handling close:", error.message);
287+
res.status(500).json({ error: "Internal Server Error" });
288+
}
289+
}
290+
291+
module.exports = {
292+
handleTransfer,
293+
handleDeposit,
294+
handleWithdraw,
295+
handleClose,
296+
};

0 commit comments

Comments
 (0)