Skip to content

Commit 9906291

Browse files
committed
doc: demonstrate usage of Key Utils based on DashKeys and Secp256k1
1 parent 5c1335e commit 9906291

File tree

1 file changed

+172
-55
lines changed

1 file changed

+172
-55
lines changed

README.md

+172-55
Original file line numberDiff line numberDiff line change
@@ -26,60 +26,166 @@ Server and browser compatible. Vanilla JS. 0 Dependencies.
2626
npm install --save @dashincubator/secp256k1
2727
npm install --save dashkeys
2828
npm install --save dashtx
29+
npm install --save dashkeys
2930
```
3031

31-
Note: You may provide your own `sign()` function, as shown below.
32+
Note: You must provide your own _Key Util_ functions, as shown below.
3233

3334
```js
3435
"use strict";
3536

36-
let Tx = require("dashtx");
37-
let tx = Tx.create({ sign: sign });
38-
37+
let DashKeys = require("dashkeys");
38+
let DashTx = require("dashtx");
3939
let Secp256k1 = require("@dashincubator/secp256k1");
4040

41-
async function sign(privKeyBytes, hashBytes) {
42-
let sigOpts = { canonical: true, extraEntropy: true };
43-
let sigBytes = await Secp256k1.sign(hashBytes, privKeyBytes, sigOpts);
44-
return sigBytes;
45-
}
41+
let yourWalletKeyDataMapGoesHere = {
42+
/* SEE BELOW */
43+
};
4644

47-
// ...
45+
let keyUtils = {
46+
/* SEE BELOW */
47+
};
48+
let dashTx = DashTx.create(keyUtils);
49+
50+
let inputs = [{ outputIndex, publicKey, txid /*, optional addr/pkh/hdpath */ }];
51+
let outputs = [{ satoshis, pubKeyHash /*, optional addr/hdpath/etc */ }];
52+
let txInfo = { inputs, outputs };
53+
54+
// Sorted as per "Lexicographical Indexing of Transaction Inputs and Outputs"
55+
txInfo.inputs.sort(DashTx.sortInputs);
56+
txInfo.outputs.sort(DashTx.sortOutputs);
57+
58+
let txInfoSigned = await dashTx.hashAndSignAll(txInfo);
59+
60+
console.info(JSON.stringify(txInfo, null, 2));
61+
console.info(txInfo.transaction);
4862
```
4963

5064
## Browsers
5165

5266
```html
5367
<script src="https://unpkg.com/@dashincubator/secp256k1/secp256k1.js"></script>
68+
<script src="https://unpkg.com/dashkeys/dashkeys.js"></script>
5469
<script src="https://unpkg.com/dashtx/dashtx.js"></script>
5570
```
5671

57-
Note: You must provide your own `sign()` function, as shown below.
72+
Note: You must provide your own _Key Util_ functions, as shown below.
5873

5974
```js
60-
(function () {
75+
(async function () {
6176
"use strict";
6277

63-
let Tx = window.DashTx;
64-
let tx = Tx.create({ sign: sign });
65-
78+
let DashKeys = window.DashTx;
79+
let DashTx = window.DashKeys;
6680
let Secp256k1 = window.nobleSecp256k1;
6781

68-
async function sign(privKeyBytes, hashBytes) {
69-
let sigOpts = { canonical: true, extraEntropy: true };
70-
let sigBytes = await Secp256k1.sign(hashBytes, privKeyBytes, sigOpts);
71-
return sigBytes;
72-
}
82+
let yourWalletKeyDataMapGoesHere = {
83+
/* SEE BELOW */
84+
};
85+
86+
let keyUtils = {
87+
/* SEE BELOW */
88+
};
89+
let dashTx = DashTx.create(keyUtils);
90+
91+
let inputs = [
92+
{ outputIndex, publicKey, txid /*, optional addr/pkh/hdpath */ },
93+
];
94+
let outputs = [{ satoshis, pubKeyHash /*, optional addr/hdpath/etc */ }];
95+
let txInfo = { inputs, outputs };
96+
97+
// Sorted as per "Lexicographical Indexing of Transaction Inputs and Outputs"
98+
txInfo.inputs.sort(DashTx.sortInputs);
99+
txInfo.outputs.sort(DashTx.sortOutputs);
100+
101+
let txInfoSigned = await dashTx.hashAndSignAll(txInfo);
102+
103+
console.info(JSON.stringify(txInfo, null, 2));
104+
console.info(txInfo.transaction);
73105

74106
// ...
75107
})();
76108
```
77109

78-
## Example Usage
110+
## Example Wallet Key Data
79111

80-
See also: [example.js](/example.js).
112+
DashTx does not depend on any specific implementation of a wallet key storage
113+
engine, but it works great with plain-old JSON:
114+
115+
```js
116+
let yourWalletKeyMapGoesHere = {
117+
yTw3SFk9PbQ1kikMgJBRA7CFyLfNt2G6QD: {
118+
hdpath: "0bGYi3S7n2Q|m/44'/1'/0'/0/0",
119+
address: "yTw3SFk9PbQ1kikMgJBRA7CFyLfNt2G6QD",
120+
wif: "cUeUEgRQWfKiYPBeRZsYsrvvSZiKHbUNqiQE2AdKA4s7ymycdVxc",
121+
},
122+
yb4zn8MSW4hHsvmP6PxX2tUPDb9bvmxSrS: {
123+
hdpath: "0bGYi3S7n2Q|m/44'/1'/0'/0/1",
124+
wif: "cN28SZpmmuVFmmBQBHCNdwa6a14kWZM8VVpZETjzk47aGNvVGXK7",
125+
address: "yb4zn8MSW4hHsvmP6PxX2tUPDb9bvmxSrS",
126+
},
127+
yfrB4v4cih7os6t1tg4YuWkrTYmHyMHkZb: {
128+
hdpath: "0bGYi3S7n2Q|m/44'/1'/0'/0/2",
129+
address: "yfrB4v4cih7os6t1tg4YuWkrTYmHyMHkZb",
130+
wif: "cQBHjCxspabNZKGMK3gbuvSLgssqSxAuDsaMMDuzgXioYyR723Bg",
131+
},
132+
// ...
133+
};
134+
```
135+
136+
You can use any indexing, query, or storage strategy you like, with values in
137+
Base58Check, Hex, Byte, or whatever else you fancy - just as long as your
138+
provided _Key Util_ functions can convert them.
139+
140+
## Example Key Utils
141+
142+
DashTx does not depend on any specific implementation of signing or key
143+
transformation, but it works greeat **NobleSecp256k1** and **DashKeys**:
144+
145+
```text
146+
getPrivateKey(txInput, i)
147+
getPublicKey(txInput, i)
148+
sign(privKeyBytes, txHashBytes)
149+
toPublicKey(privKeyBytes)
150+
```
151+
152+
```js
153+
let keyUtils = {
154+
getPrivateKey: async function (txInput, i) {
155+
let pkhBytes = DashKeys.utils.hexToBytes(txInput.pubKeyHash);
156+
let address = await DashKeys.pkhToAddr(txInput.pubKeyHash);
157+
158+
let yourKeyData = yourWalletKeyMapGoesHere[address];
159+
160+
let privKeyBytes = DashKeys.wifToPrivKey(yourKeyData.wif);
161+
return privKeyBytes;
162+
},
163+
164+
getPublicKey: async function (txInput, i) {
165+
let privKeyBytes = getPrivateKey(txInput, i);
166+
let publicKey = await keyUtils.toPublicKey(privKeyBytes);
167+
168+
return publicKey;
169+
},
170+
171+
sign: async function (privKeyBytes, txHashBytes) {
172+
let sigOpts = { canonical: true, extraEntropy: true };
173+
let sigBytes = await Secp256k1.sign(txHashBytes, privKeyBytes, sigOpts);
174+
175+
return sigBytes;
176+
},
177+
178+
toPublicKey: async function (privKeyBytes) {
179+
let isCompressed = true;
180+
let pubKeyBytes = Secp256k1.getPublicKey(privateKey, isCompressed);
181+
return pubKeyBytes;
182+
},
183+
};
184+
```
81185

82-
Note: You must provide your own `sign()` function, as shown above.
186+
## Example Tx Info
187+
188+
See also: [example.js](/example.js).
83189

84190
```js
85191
let memo = Tx.utils.strToHex("Hello, Dash!");
@@ -114,7 +220,6 @@ let txInfo = {
114220
txInfo.inputs.sort(Tx.sortInputs);
115221
txInfo.outputs.sort(Tx.sortOutputs);
116222

117-
let keys = txInfo.inputs.map(getPrivateKey);
118223
let txInfoSigned = await tx.hashAndSignAll(txInfo);
119224

120225
console.info(JSON.stringify(txInfo, null, 2));
@@ -160,7 +265,6 @@ let txInfo = {
160265
txInfo.inputs.sort(Tx.sortInputs);
161266
txInfo.outputs.sort(Tx.sortOutputs);
162267

163-
let keys = txInfo.inputs.map(getPrivateKey);
164268
let txInfoSigned = await tx.hashAndSignAll(txInfo);
165269

166270
console.info(JSON.stringify(txInfo, null, 2));
@@ -278,7 +382,7 @@ Tx.OUTPUT_SIZE // 34
278382
```
279383

280384
```text
281-
Tx.create({ sign, getPrivateKey });
385+
Tx.create({ getPrivateKey, getPublicKey, sign, toPublicKey });
282386
tx.hashAndSignAll(txInfo);
283387
tx.legacy.draftSingleOutput({ utxos, inputs, output });
284388
tx.legacy.finalizePresorted(txDraft, keys);
@@ -304,18 +408,13 @@ Tx.hashPartial(txHex, Tx.SIGHASH_ALL);
304408
305409
// Deprecated
306410
Tx.createLegacyTx(coins, outputs, changeOutput);
307-
308-
// Not API-locked, May change
309-
Tx.utils.sign(privKeyBytes, txHashBytes);
310-
Tx.utils.toPublicKey(privKeyBytes);
311-
Tx.utils.addrToPubKeyHash(addr);
312411
```
313412

314413
```js
315414
/**
316415
* Creates a tx signer instance.
317416
*/
318-
Tx.create({ sign, getPrivateKey });
417+
Tx.create({ getPrivateKey, getPublicKey, sign, toPublicKey });
319418

320419
/**
321420
* Estimates the min, mid, and max sizes of (fees for) a transaction (including memos).
@@ -469,26 +568,7 @@ Tx.utils.strToHex(str);
469568
### You-do-It Functions
470569

471570
```js
472-
Tx.create({ sign, getPrivateKey });
473-
474-
/**
475-
* Sign a 256-bit hash. 'canonical' form is required for
476-
* blockchains. Must return the signature as an ASN.1 DER.
477-
* These may or may not be the default options, depending
478-
* on the library used.
479-
*
480-
* We recommend @dashincubator/secp256k1 and @noble/secp256k1.
481-
*
482-
* @param {Uint8Array} privateKey - an input's corresponding key
483-
* @param {Uint8Array} txHashBytes - the (not reversed) 2x-sha256-hash
484-
* @returns {String} - hex representation of an ASN.1 signature
485-
*/
486-
async function sign(privateKey, txHashBytes) {
487-
let sigOpts = { canonical: true };
488-
let sigBytes = await Secp256k1.sign(txHashBytes, privateKey, sigOpts);
489-
490-
return Tx.utils.bytesToHex(sigBytes);
491-
}
571+
Tx.create({ getPrivateKey, getPublicKey, sign, toPublicKey });
492572

493573
/**
494574
* Given information that you provided about an input
@@ -504,10 +584,47 @@ async function sign(privateKey, txHashBytes) {
504584
* @returns {Uint8Array} - the private key bytes
505585
*/
506586
async function getPrivateKey(txInput, i) {
507-
let address = await DashKeys.pubkeyToAddr(txInput.publicKey);
508-
let privateKey = privateKeys[address];
587+
let pkhBytes = DashKeys.utils.hexToBytes(txInput.pubKeyHash);
588+
let address = await DashKeys.pkhToAddr(txInput.pubKeyHash);
589+
let privKeyBytes = privateKeys[address];
590+
591+
return privKeyBytes;
592+
}
593+
594+
async function getPublicKey(txInput, i) {
595+
let privKeyBytes = getPrivateKey(txInput, i);
596+
let publicKey = await toPublicKey(privKeyBytes);
597+
598+
return publicKey;
599+
}
600+
601+
let Secp256k1 =
602+
//@ts-ignore
603+
window.nobleSecp256k1 || require("@dashincubator/secp256k1");
604+
605+
/**
606+
* Sign a 256-bit hash. 'canonical' form is required for
607+
* blockchains. Must return the signature as an ASN.1 DER.
608+
* These may or may not be the default options, depending
609+
* on the library used.
610+
*
611+
* We recommend @dashincubator/secp256k1 and @noble/secp256k1.
612+
*
613+
* @param {Uint8Array} privateKey - an input's corresponding key
614+
* @param {Uint8Array} txHashBytes - the (not reversed) 2x-sha256-hash
615+
* @returns {String} - hex representation of an ASN.1 signature
616+
*/
617+
async function sign(privKeyBytes, txHashBytes) {
618+
let sigOpts = { canonical: true, extraEntropy: true };
619+
let sigBytes = await Secp256k1.sign(txHashBytes, privKeyBytes, sigOpts);
620+
621+
return sigBytes;
622+
}
509623

510-
return privateKey;
624+
async function toPublicKey(privKeyBytes) {
625+
let isCompressed = true;
626+
let pubKeyBytes = Secp256k1.getPublicKey(privateKey, isCompressed);
627+
return pubKeyBytes;
511628
}
512629
```
513630

0 commit comments

Comments
 (0)