Skip to content

Commit 5dd5c31

Browse files
committed
Initial Commit
0 parents  commit 5dd5c31

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+23606
-0
lines changed

.DS_Store

6 KB
Binary file not shown.

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

LICENSE

+674
Large diffs are not rendered by default.

README.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# productChain
2+
3+
productChain is a blockchain designed to keep track of valid/verified products. Each transaction in a block is a `product Name` - `Manufacturer ID Pair`.
4+
5+
By default, the blockchain has 3 transactions in each block and a set difficulty of 4
6+
These parameters can be changed in the `src/blockchain.ts` file. Run `tsc` in the `src` directory after any changes.
7+
8+
The blockchain uses a Discrete Log ZKP to verify products. This is done as follows,
9+
10+
`p` is a large prime and `g` is its generator
11+
12+
A product (with ID `x`) is sent to an trusted body for verification. After verifying the product, `y = g^x mod(p)` is calculated and published.
13+
14+
When an external verifier wants to verify if a product is legitimate, they should be able to do so without actually knowing the product's unique ID.
15+
16+
To accomplish this, we use the following Zero Knowledge Proof,
17+
18+
- A random number between `0` and `p - 1` is chosen and `h = g^x mod (p)` is calculated
19+
- `h` is sent to the verifier. The verifier then send a random bit `b` (0/1)
20+
- `s = (r + bx) mod(p - 1)` is calculated and sent to the verifier
21+
- The verifier checks if , `g^s mod (p)` equals `hy^b mod(p)`
22+
- This is done for multiple rounds (50 by default in our case). If the equality check fails once, then the productID is not valid
23+
24+
Thus, we are able to verify a productID without actually sending the productID to the verifier
25+
26+
Note : In this repo, a list of randomly generated UUIDs is present in `validproductIDs.txt` . For testing, only these productIDs will pass the ZKP. This is not a security hazard as the file is not used anywhere in the project.
27+
28+
29+
# Running the test network
30+
31+
Run `npm install` , after cloning the repo.
32+
33+
Run `npm run start`, to start the peerList.
34+
35+
`cd` into the `src` folder to run the nodes.
36+
37+
Each terminal instance can be used to run a node in a randomly selected port,
38+
39+
Run `node miner.js` in multiple terminals.
40+
41+
Run `node api.js` in another terminal. The API runs at port `3000` by default.
42+
43+
To view the blockchain: Make a `GET` request to `http://localhost:3000/getChain` to get a copy of the blockchain.
44+
45+
To open the frontend, `cd` into `frontend` and run `npm install` , the frontend will open on some open port by default.
46+
47+
Connect each node with its peers [Option `1` in the CLI] before interacting with the blockchain.
48+
49+

Test Miners/miner1.js

+225
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
const ws = require("ws");
2+
const {Block ,Blockchain , blockData , productChain} = require('../dist/blockchain.js');
3+
const sha256 = require('crypto-js/sha256')
4+
const EC = require('elliptic').ec;
5+
const ec = new EC('secp256k1');
6+
7+
const readline = require('readline');
8+
const rl = readline.createInterface({
9+
input: process.stdin,
10+
output: process.stdout
11+
});
12+
13+
const manu_sign = ec.keyFromPrivate('ea29bd9c1a35ef95b4afa163902a27d1ed2d1fe304a5035e1c6ce5df9d5ec09f')
14+
const manu_id = manu_sign.getPublic('hex')
15+
16+
let tempChain = new Blockchain()
17+
tempChain.chain.pop();
18+
19+
const PORT = 3000;
20+
const peers = ['ws://localhost:3001' , 'ws://localhost:3002' , 'ws://localhost:3003'];
21+
const my_address = `ws://localhost:${PORT}`
22+
23+
24+
const server = new ws.Server({ port : PORT})
25+
26+
let opened = [] , connected = [];
27+
28+
console.log("Listening on PORT" , PORT)
29+
30+
async function connect(address) {
31+
if(!connected.find(peerAddress => peerAddress === address) && address != my_address){
32+
const socket = new ws(address);
33+
34+
socket.on("open" , () => {
35+
socket.send(JSON.stringify(produceMessage("HANDSHAKE" , [my_address , ...connected])))
36+
37+
opened.forEach(node => node.socket.send(JSON.stringify(produceMessage("HANDSHAKE" , [address]))))
38+
39+
if (!opened.find(peer => peer.address === address) && address !== my_address) {
40+
opened.push({ socket, address });
41+
}
42+
43+
if (!connected.find(peerAddress => peerAddress === address) && address !== my_address) {
44+
connected.push(address);
45+
}
46+
});
47+
48+
socket.on("close", () => {
49+
opened.splice(connected.indexOf(address), 1);
50+
connected.splice(connected.indexOf(address), 1);
51+
});
52+
}
53+
}
54+
55+
function produceMessage(type, data) {
56+
return { type, data };
57+
}
58+
59+
function sendMessage(message) {
60+
opened.forEach(node => {
61+
node.socket.send(JSON.stringify(message));
62+
})
63+
}
64+
65+
66+
server.on("connection" , async (socket , req) => {
67+
68+
socket.on("message" , message => {
69+
const _message = JSON.parse(message)
70+
71+
switch(_message.type){
72+
case "CREATE_product":
73+
74+
const productData = _message.data[0];
75+
console.log("Received Data from " ,_message.data[1] ," Pending Length : " , (productChain.pendingData.length + 1))
76+
77+
productChain.addData(productData)
78+
if(productChain.pendingData.length == productChain.blockSize){
79+
setTimeout(() => {
80+
interactWithChain(99)
81+
},0) //To simulate some slow nodes, if necessary
82+
}
83+
break;
84+
85+
86+
case "ADD_BLOCK":
87+
88+
//console.log(_message.data[0])
89+
90+
const newBlock = _message.data[0];
91+
const prevHash = newBlock.prevHash
92+
93+
console.log("New Block Received from : ",_message.data[1])
94+
95+
if(
96+
(sha256(productChain.getLatestBlock().hash + JSON.stringify(newBlock.data) + newBlock.nonce).toString() === newBlock.hash) &&
97+
newBlock.hash.startsWith(Array(productChain.difficulty + 1)) &&
98+
newBlock.hasValidData(newBlock) || true &&
99+
productChain.getLatestBlock().hash === prevHash //add timestamp check (gen chk also)
100+
){
101+
const newBlock = _message.data;
102+
productChain.chain.push(newBlock);
103+
productChain.pendingData = [];
104+
console.log("Block Added")
105+
}
106+
else if(productChain.getLatestBlock().hash = newBlock.hash)
107+
console.log("Block Not Added. The block is already present here")
108+
else if(productChain.getLatestBlock().data === newBlock.data)
109+
console.log("Block Not Added. The duplicate block data detected")
110+
else
111+
console.log("Checks failed. Block was not added")
112+
break;
113+
114+
case "SEND_CHAIN":
115+
116+
console.log()
117+
console.log("Blocks received")
118+
console.log()
119+
const { block, finished } = _message.data;
120+
121+
if (!finished)
122+
tempChain.chain.push(block);
123+
else {
124+
tempChain.chain.push(block);
125+
if (Blockchain.isValid(tempChain))
126+
productChain.chain = tempChain.chain;
127+
tempChain = new Blockchain();
128+
}
129+
130+
break;
131+
132+
case "REQUEST_CHAIN":
133+
134+
console.log('A copy of the blockchain was requested')
135+
const socketToSend = opened.filter(node => node.address === _message.data)[0].socket;
136+
for (let i = 0; i < productChain.chain.length; i++) {
137+
socketToSend.send(JSON.stringify(produceMessage(
138+
"SEND_CHAIN",
139+
{
140+
block: productChain.chain[i],
141+
finished: i === productChain.chain.length - 1
142+
}
143+
)));
144+
}
145+
146+
break;
147+
148+
case "HANDSHAKE":
149+
150+
const nodes = _message.data;
151+
nodes.forEach(node => connect(node))
152+
}
153+
})
154+
})
155+
156+
console.log("Manufacturer ID (Public Add) : ",manu_id)
157+
console.log()
158+
console.log("Connect to Peers -> 1");
159+
console.log("Request copy of blockchain -> 2");
160+
console.log('Show chain -> 3');
161+
console.log('Add product -> 4');
162+
console.log()
163+
function interactWithChain(choice){
164+
switch(choice){
165+
case 1:
166+
peers.forEach(peer => connect(peer));
167+
console.log()
168+
console.log("Connected to Peers")
169+
console.log()
170+
break;
171+
case 2:
172+
sendMessage(produceMessage("REQUEST_CHAIN" , my_address))
173+
break;
174+
case 99:
175+
if (productChain.pendingData.length == productChain.blockSize) {
176+
productChain.minePending();
177+
console.log("Broadcasting block to other nodes.")
178+
sendMessage(produceMessage("ADD_BLOCK", [productChain.getLatestBlock() , my_address]))
179+
break;
180+
}
181+
else{
182+
console.log("Listening.... Pending Data Length:",productChain.pendingData.length)
183+
}
184+
break;
185+
case 3:
186+
console.log()
187+
console.log(JSON.stringify(productChain , null , 1))
188+
console.log()
189+
break;
190+
case 4:
191+
const rand = Math.floor(Math.random() * 100)
192+
const data1 = new blockData(manu_id , `product ID ${rand}` , `product Name ${rand}`);
193+
blockData.signData(manu_sign , data1)
194+
console.log(`Broadcasting --> product ID ${rand} product Name ${rand}`)
195+
sendMessage(produceMessage("CREATE_product", [data1 , my_address]));
196+
productChain.addData(data1)
197+
if(productChain.pendingData.length == productChain.blockSize){
198+
productChain.minePending()
199+
sendMessage(produceMessage("ADD_BLOCK", [productChain.getLatestBlock() , my_address]))
200+
}
201+
else
202+
console.log("Listening.... Pending Data Length:",productChain.pendingData.length)
203+
break;
204+
default:
205+
flag = false
206+
}
207+
}
208+
209+
210+
rl.on('line', (input) => {
211+
if (input === '1') {
212+
interactWithChain(1);
213+
} else if (input === '2') {
214+
interactWithChain(2);
215+
} else if (input === '3') {
216+
interactWithChain(3);
217+
} else if (input === '4') {
218+
interactWithChain(4);
219+
} else if (input === '5') {
220+
interactWithChain(5);
221+
} else {
222+
console.log('Invalid input');
223+
}
224+
});
225+

0 commit comments

Comments
 (0)