Skip to content

Commit a1b01fe

Browse files
author
Am.A
committed
compact tx added
response from broadcast handled full example for mint and transfer added
1 parent d2c2969 commit a1b01fe

File tree

9 files changed

+317
-142
lines changed

9 files changed

+317
-142
lines changed

README.md

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The following is a breakdown of sections and classes for this cbdc-module:
3030
- constructor(script_type, pubHex) - ```a new Address Object```
3131
- script_type - {Number} representing the script type of the address
3232
- pubHex - {String} represents valid 32 byte hexadecimal string
33-
- getAddress() - returns {String} ```represented the bech32 encoding version of a publickey e.g. ```
33+
- getAddress() - returns {String} ```represented the bech32 encoding version of a publickey ```
3434
- decodeAddress() - returns {Object} ```with fields script_type (string representing) and pubHex (hex string of public key)```
3535

3636
- static decodeFromAddressString(address) - returns ```{Object} with fields script_type {Number} and pubHex {String} (hexdecimal string of public key)```
@@ -40,7 +40,9 @@ The following is a breakdown of sections and classes for this cbdc-module:
4040
- secretKeyData - ```{String} a valid 32 byte hexadecimal string```
4141
- static fromPrivateKeyData(secretKeyData)
4242
- secretKeyData - ```{String} a valid 32 byte hexadecimal string```
43-
- toBuffer() - ```returns {Buffer} 32 byte buffer publickey```
43+
- getWitnessCommit(scriptType) - ```returns witness commitment```
44+
- scriptType - ```{string} hex representation of script type ("00" for P2PK)```
45+
- toBuffer() - ```returns {Buffer} 32 byte buffer publickey```
4446

4547
- Secretkey
4648
- contructor(randBytes) - ```returns new SecretKey with randBytes provided as random seed (if provided), otherwise it creates random key from random secrety bytes```
@@ -54,10 +56,14 @@ The following is a breakdown of sections and classes for this cbdc-module:
5456

5557
## Networking
5658
* Networking
57-
- broadcastTx(port, host, signedTxHex) - ```broadcast a signedTxBuf to a sentinel server at host {host} and port {port} number```
59+
- broadcast(port, host, payloadHex, reqType) - ```Broadcast a payload to a (sentinel/coordinator/shard) and returns a promise that resolves to response```
5860
- port - {number} port number of host
59-
- host - {string} hostname url to send signedTx
60-
- signedTxHex - {string} a valid hexadecimal encoded transaction that has been signed
61+
- host - {string} hostname or url to send payloadHex
62+
- payloadHex - {string} a valid hexadecimal encoded message, could be signed tx or compact tx or any other message
63+
- reqType - {number|null} indicates type of request. can be null for no request type and for others:
64+
- sentinel: 0=execute, 1=validate
65+
- shard (read-only endpoint): 0=UHS, 1=tx
66+
- coordinator doesn't have request type
6167

6268
## Transaction
6369
- Input
@@ -67,20 +73,9 @@ The following is a breakdown of sections and classes for this cbdc-module:
6773
- witnessProgramCommitment {String} - witness commitment for this input
6874
- value {number} - the number of dollar units this input is worth
6975
- writeInputToBuffer() - ```returns Buffer representation as buffer type```
70-
- getUHSHash() - ```returns {Buffer} Universal Hash Set hash of the input e.g. concatentation of [txid, index, witnessProgramCommitment, value] into bytes```
71-
e.g.
76+
- getUHSHash() - ```returns {Buffer} Universal Hash Set hash of the input i.e. concatenation of [txid, index, witnessProgramCommitment, value] into bytes```
7277
- toString() - ```returns {String} representing valid input```
7378

74-
## Utils
75-
- Utility Methods CBDC module
76-
- sign(secretKey, message) - ```returns signature that signed message {message} with privateKey {privateKey}```
77-
- secretKey - {String} - 32 byte hexadecimal string
78-
- message - {String} - message that is signed
79-
- verify(publicKey, message, signature) ```returns true or false whether the produced signature is validly signed publicKey```
80-
- publicKey - 32 byte hexadecimal string
81-
- message - message to verify was signed
82-
- signature - {String} signature to verify validly signed message against public key message pair
83-
8479
*N.B.:* A hash is used to identify a specfic UTXO within the monetary supply (the UHS ID); it is a concatenation of a txid, a 64-bit index of the UTXO's position in previous tx's outputs, a witnessProgramCommitment, and the 64-bit encoded value of that output
8580

8681
- Output
@@ -99,11 +94,23 @@ The following is a breakdown of sections and classes for this cbdc-module:
9994
- outputs - Array{Output}
10095
- witnesses - Array of witness object
10196
- toHex() - ```returns {String} hexadecimal string of the unsigned raw transaction```
97+
- getCompactHex(sentinelAttestation) - ```returns {String} compact-tx in hexadecimal format with given sentinel attestations```
10298
- getTxid() - ```returns {String} returns hexadecimal string of transaction id (txid)```
10399
- sign(secretKey) - ```returns {Buffer} signed tx in bytes```
104100
- static txFromHex(rawHex) - ```returns {Transaction} object from the provided rawTx```
105101
- rawHex - valid hexadecimal string represents a valid tx
106102

103+
## Utils
104+
- Utility Methods CBDC module
105+
- sign(secretKey, message) - ```returns signature that signed message {message} with privateKey {privateKey}```
106+
- secretKey - {String} - 32 byte hexadecimal string
107+
- message - {String} - message that is signed
108+
- verify(publicKey, message, signature) ```returns true or false whether the produced signature is validly signed publicKey```
109+
- publicKey - 32 byte hexadecimal string
110+
- message - message to verify was signed
111+
- signature - {String} signature to verify validly signed message against public key message pair
112+
113+
107114
## Special Notes
108115
BigInt is used everywhere throughout this module, that may pose problems in using toJson() methods or serializing for output
109116

@@ -137,7 +144,7 @@ The following are example code snippets.
137144
```js
138145
let network = require('./networking/broadcast');
139146
const txBuf = new Transaction();
140-
Networking.broadcastTx(5555, '127.0.0.1', Buffer.from(tx.toHex(), 'hex'))
147+
Networking.broadcast(5555, '127.0.0.1', Buffer.from(tx.toHex(), 'hex'))
141148
```
142149
- Transaction
143150
```js
@@ -153,7 +160,7 @@ The following are example code snippets.
153160
01000000000000009f981e64afc0fc56a0d7b355cd9eba36f3d19507088713b1f73afc5bf301a44e000000000000000070cd87ebaaa0d2d059dccaceeb7f9f823a5791d60b00aef9d9573f1fbf91ca29c800000000000000010000000000000081b095a242974d9f4e98ca18b468b8e644e4168380a035b3d66bc279b36c6510c80000000000000001000000000000006100000000000000003ad8f015f9212f8262248af4cf4cc39907d0215fdde14507f8bc09ad5836bbe901986cc97272bdb7624a824afcef76936b8f945e55d6e8479b95c81298e77b42d5a255e8529fc2f0d90743e7f9997a7159b6121105c7ec9b9252da992f34611f
154161
```
155162

156-
- Constructing, signing and broacasting tx to sentinel
163+
- Constructing, signing and broadcasting tx to sentinel for execution
157164
```js
158165
const secretKey = 'e00b5c3d80899217a22fea87e7337907203df8a1efebd4d2a8773c8f629fff36';
159166

@@ -163,7 +170,7 @@ The following are example code snippets.
163170

164171
tx.sign(secretKey);
165172

166-
Networking.broadcastTx(5555, '127.0.0.1', Buffer.from(tx.toHex(), 'hex'))
173+
Networking.broadcast(5555, '127.0.0.1', Buffer.from(tx.toHex(), 'hex'))
167174
```
168175

169176
- Signing and Verifying
@@ -176,6 +183,40 @@ The following are example code snippets.
176183
const sig = utils.sign(secretKey, message);
177184
utils.verify(publicKey, Buffer.from(sha256(message), 'hex'), sig);
178185
```
186+
187+
- Full example for minting and transferring in **2PC** architecture
188+
```js
189+
const user1_sk = new Secretkey();
190+
const user2_sk = new Secretkey();
191+
const user1_pk = new Publickey(user1_sk.secretKeyBuf);
192+
const user2_pk = new Publickey(user2_sk.secretKeyBuf);
193+
const witness1 = user1_pk.getWitnessCommit();
194+
const witness2 = user2_pk.getWitnessCommit();
195+
196+
const sentinel_sk = "SENTINEL_SECRET_KEY_HEX";
197+
const sentinel_pk = Publickey.fromPrivateKeyData(sentinel_sk);
198+
199+
const output1 = new Output(witness1, 50);
200+
const mint_tx = new Transaction([], [output1], []);
201+
const input = new Input(mint_tx.getTxid(), 0, witness1, 50);
202+
const compactMintTx = buffer.from(mint_tx.getCompactHex([]), 'hex');
203+
const sentinelAttestation = buffer.concat([
204+
sentinel_pk,
205+
schnorr.sign(sentinel_sk, buffer.from(Utils.sha256(compactMintTx), 'hex')),
206+
]);
207+
console.log(await Comms.broadcast(SHARD_PORT, SHARD_IP, input.getUHSHash(), 0)); // input UHS doesn't exists, so output will be 0100
208+
console.log(await Comms.broadcast(COORD_PORT, COORD_IP, mint_tx.getCompactHex([sentinel_attest]), null)); // okay mint result will be 0101
209+
console.log(await Comms.broadcast(SHARD_PORT, SHARD_IP, input.getUHSHash(), 0)); // input UHS will exist and output will be 0101
210+
211+
const output22 = new Output(witness2, 20); // pay 20 to user2
212+
const output21 = new Output(witness1, 30); // take 30 back as change
213+
const transferTx = new Transaction([input], [output22, output21], []);
214+
const input21 = new Input(transferTx.getTxid(), 1, witness1, 30);
215+
transferTx.sign(user1_sk.toHex());
216+
console.log(await Comms.broadcast(SHARD_PORT, SHARD_IP, input21.getUHSHash(), 0)); // input21 UHS doesn't exists, so output will be 0100
217+
console.log(await Comms.broadcast(SENTINEL_PORT, SENTINEL_IP, transferTx.toHex(), 0)); // output for successful transfer = 01 00 03 00 00 00 00
218+
console.log(await Comms.broadcast(SHARD_PORT, SHARD_IP, input21.getUHSHash(), 0)); // input21 UHS exists after successful transfer, so output will be 0101
219+
```
179220

180221
## Sample Data
181222

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@mit-dci/opencbdc",
33
"version": "0.0.2",
4-
"description": "An javascript module for interacting with the opencbdc-tx atomizer and 2pc environments",
4+
"description": "A javascript module for interacting with the opencbdc-tx atomizer and 2pc environments",
55
"main": "index.js",
66
"scripts": {
77
"test": "mocha test/**/*.js",

src/crypto/publickey.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,35 @@
11
const Secp256k1 = require('@enumatech/secp256k1-js');
22
const buffer = require('buffer/').Buffer;
3+
const utils = require('./utils');
34

45
class PublicKey {
5-
66
/**
77
* @param secretKeyData privateKey hex string
88
*/
99
constructor(secretKeyData) {
1010
this.secretKeyBuf = buffer.from(secretKeyData, 'hex');
11-
this.publicKey = Secp256k1.generatePublicKeyFromPrivateKeyData(Secp256k1.uint256(secretKeyData, 16)).x;
11+
this.publicKey = Secp256k1.generatePublicKeyFromPrivateKeyData(
12+
Secp256k1.uint256(secretKeyData, 16),
13+
).x;
1214
}
1315

1416
/**
1517
* @param {string} secretKeyBuf
16-
* @returns {Buffer} PublicKey Buffer
18+
* @returns {Buffer} PublicKey Buffer
1719
*/
1820
static fromPrivateKeyData(secretKeyData) {
1921
const pubHex = Secp256k1.generatePublicKeyFromPrivateKeyData(Secp256k1.uint256(secretKeyData, 16)).x;
2022
return buffer.from(pubHex, 'hex');
2123
}
22-
24+
25+
/**
26+
* @param {string} scriptType hex representation of script type (0 for P2PK)
27+
* @returns {Buffer} witness commitment
28+
*/
29+
getWitnessCommit(scriptType = '00') {
30+
return buffer.from(utils.sha256(buffer.from(scriptType + this.publicKey, 'hex')), 'hex');
31+
}
32+
2333
/**
2434
* @returns {Buffer} representing a public key in size x bytes
2535
*/
@@ -29,4 +39,3 @@ class PublicKey {
2939
}
3040

3141
module.exports = PublicKey;
32-

src/crypto/secretkey.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class SecretKey {
4040
* @returns {string} hexadecimal version of private key
4141
*/
4242
toHex() {
43-
return Buffer.toString(this.secretKeyBuf, 'hex');
43+
return this.secretKeyBuf.toString('hex');
4444
}
4545
}
4646

src/crypto/utils.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
const schnorr = require('bip-schnorr');
2+
const crypto = require('crypto');
23

34
/**
45
* Return sha256 of input b
5-
* @param {*} b
6-
* @returns
6+
* @param {*} b
7+
* @returns hex SHA256 digest
78
*/
8-
9-
const sha256 = function a(b){function c(a,b){return a>>>b|a<<32-b;}for(var d,e,f=Math.pow,g=f(2,32),h='length',i='',j=[],k=8*b[h],l=a.h=a.h||[],m=a.k=a.k||[],n=m[h],o={},p=2;64>n;p++)if(!o[p]){for(d=0;313>d;d+=p)o[d]=p;l[n]=f(p,.5)*g|0,m[n++]=f(p,1/3)*g|0;}for(b+='\x80';b[h]%64-56;)b+='\x00';for(d=0;d<b[h];d++){if(e=b.charCodeAt(d),e>>8)return;j[d>>2]|=e<<(3-d)%4*8;}for(j[j[h]]=k/g|0,j[j[h]]=k,e=0;e<j[h];){var q=j.slice(e,e+=16),r=l;for(l=l.slice(0,8),d=0;64>d;d++){var s=q[d-15],t=q[d-2],u=l[0],v=l[4],w=l[7]+(c(v,6)^c(v,11)^c(v,25))+(v&l[5]^~v&l[6])+m[d]+(q[d]=16>d?q[d]:q[d-16]+(c(s,7)^c(s,18)^s>>>3)+q[d-7]+(c(t,17)^c(t,19)^t>>>10)|0),x=(c(u,2)^c(u,13)^c(u,22))+(u&l[1]^u&l[2]^l[1]&l[2]);l=[w+x|0].concat(l),l[4]=l[4]+w|0;}for(d=0;8>d;d++)l[d]=l[d]+r[d]|0;}for(d=0;8>d;d++)for(e=3;e+1;e--){var y=l[d]>>8*e&255;i+=(16>y?0:'')+y.toString(16);}
10-
return i;};
9+
10+
const sha256 = (msg) => {
11+
return crypto.createHash('sha256').update(Buffer.from(msg)).digest('hex');
12+
};
1113

1214
/**
1315
* Returns signature that signed message {message} with privateKey {privateKey}
@@ -34,7 +36,7 @@ const verify = (publicKey, message, signature) => {
3436
try {
3537
schnorr.verify(pubkeyBuf, messageBuf, sigBuf);
3638
} catch (error) {
37-
console.log('Error msg: ', error);
39+
console.log('Error msg: ', error);
3840
return false;
3941
}
4042
return true;
@@ -43,5 +45,5 @@ const verify = (publicKey, message, signature) => {
4345
module.exports = {
4446
sign,
4547
sha256,
46-
verify
47-
};
48+
verify,
49+
};

src/networking/broadcast.js

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,57 @@ const net = require('net');
22
const buffer = require('buffer/').Buffer;
33

44
const Networking = {
5-
// Broadcast a rawTxBuffer to a sentinel at specified host {hostURL} and port {port} number
6-
// make sure to include the size of the rawTxBuffer before sending to the sentinel
7-
// reqType indicates execute transaction (0) or validate transaction (1)
8-
broadcastTx: (port, host, signedTxHex, reqType = 0) => {
9-
10-
const client = new net.Socket();
11-
12-
client.connect(port, host, () => {
13-
console.log('Connected');
14-
const requestId = Math.round(Math.random() * 10000000);
15-
const reqIdBuf = buffer.alloc(8);
16-
reqIdBuf.writeBigUInt64LE(BigInt(requestId));
17-
const reqTypeBuf = buffer.alloc(1);
5+
/**
6+
* Broadcast a payload to a (sentinel/coordinator/shard)
7+
* @param {number} port tcp port of destination
8+
* @param {string} host ip address or hostname of destination
9+
* @param {string} payloadHex hex representation of payload, could be tx or compact tx or any other message
10+
* @param {(number|null)} reqType indicates type of request. can be null for no request type.
11+
* - for sentinel 0=execute, 1=validate
12+
* - for shard 0=UHS, 1=tx
13+
* - coordinator doesn't have request type
14+
* @return {Promise<Buffer>} response from destination
15+
*/
16+
broadcast: (port, host, payloadHex, reqType = 0) => {
17+
const requestId = Math.round(Math.random() * Math.pow(2, 64));
18+
const reqIdBuf = buffer.alloc(8);
19+
reqIdBuf.writeBigUInt64LE(BigInt(requestId));
20+
let reqTypeBuf;
21+
if (reqType === null) {
22+
// no request type in packet (when sending to coordinator)
23+
reqTypeBuf = buffer.alloc(0);
24+
} else {
25+
reqTypeBuf = buffer.alloc(1);
1826
reqTypeBuf.writeUInt8(reqType);
19-
const sizePacket = buffer.alloc(8);
20-
const signedTxBuffer = buffer.from(signedTxHex, 'hex');
21-
sizePacket.writeBigUInt64LE(BigInt(signedTxBuffer.length + reqIdBuf.length + reqTypeBuf.length));
22-
const finalPacket = buffer.concat([sizePacket, reqIdBuf, reqTypeBuf, signedTxBuffer]);
23-
client.write(finalPacket);
24-
});
25-
26-
client.on('data', (data) => {
27-
console.log('Received: ' + data.toString('hex'));
28-
// TODO: read response completely (based on packet length) to prevent killing prematurely
29-
client.destroy(); // kill client after server's response
30-
});
31-
32-
client.on('close', () => {
33-
console.log('Connection closed');
34-
});
27+
}
28+
const payloadBuffer = buffer.from(payloadHex, 'hex');
29+
const sizePacket = buffer.alloc(8);
30+
sizePacket.writeBigUInt64LE(BigInt(payloadBuffer.length + reqIdBuf.length + reqTypeBuf.length));
31+
const finalPacket = buffer.concat([sizePacket, reqIdBuf, reqTypeBuf, payloadBuffer]);
3532

36-
client.on('error', (err) => {
37-
console.error(err);
33+
return new Promise((resolve, reject) => {
34+
const client = new net.Socket(); // TODO: use single socket per class instance
35+
client.setTimeout(5000, client.kill); // 5s timeout
36+
client.on('error', reject);
37+
client.on('timeout', reject);
38+
client.on('close', reject);
39+
client.connect(port, host, () => {
40+
let packetLength;
41+
let receivedData = buffer.alloc(0);
42+
client.on('data', (chunk) => {
43+
receivedData = Buffer.concat([receivedData, chunk]);
44+
if (packetLength === undefined && receivedData.length >= 8) {
45+
packetLength = receivedData.readBigUInt64LE();
46+
receivedData = receivedData.subarray(8);
47+
}
48+
if (packetLength !== undefined && receivedData.length >= packetLength) {
49+
resolve(receivedData.subarray(8)); // ignore request id
50+
client.destroy(); // kill client after server's complete response
51+
}
52+
client.read(); // poll for any buffered data
53+
});
54+
client.write(finalPacket);
55+
});
3856
});
3957
},
4058
};

src/transaction/input.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const buffer = require('buffer/').Buffer;
2+
const crypto = require('crypto');
23

34
/*
45
* Input class represents the 'input' abstraction in digital currency transaction

0 commit comments

Comments
 (0)