-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
297 additions
and
0 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,297 @@ | ||
"use strict"; | ||
|
||
let DashPhrase = require("dashphrase"); | ||
let DashHd = require("dashhd"); | ||
let DashKeys = require("dashkeys"); | ||
let Secp256k1 = require("@dashincubator/secp256k1"); | ||
let DashTx = require("dashtx"); | ||
let DashPlatform = require("./dashplatform.js"); | ||
|
||
// let DapiGrpc = require("@dashevo/dapi-grpc"); | ||
let Dpp = require("@dashevo/wasm-dpp"); | ||
|
||
const DIP13_ECDSA = 0; | ||
|
||
let identityEcdsaPath = ""; | ||
{ | ||
// m/purpose'/coin_type'/feature'/subfeature'/keytype'/identityindex'/keyindex' | ||
// ex: m/9'/5'/5'/0'/0'/<id>/<key> | ||
let purposeDip13 = 9; | ||
let coinDash = 5; | ||
let featureId = 5; | ||
let subfeatureKey = 0; | ||
let keyType = DIP13_ECDSA; | ||
identityEcdsaPath = `m/${purposeDip13}'/${coinDash}'/${featureId}'/${subfeatureKey}'/${keyType}'`; | ||
} | ||
|
||
/** | ||
* @typedef KeyInfo | ||
* @prop {String} address | ||
* @prop {Uint8Array} privateKey | ||
* @prop {Uint8Array} publicKey | ||
* @prop {String} pubKeyHash | ||
*/ | ||
/** @type Object.<String, KeyInfo> */ | ||
let keysMap = {}; | ||
|
||
/** @type {Required<import('dashtx').TxKeyUtils>} */ | ||
let keyUtils = { | ||
sign: async function (privKeyBytes, hashBytes) { | ||
let sigOpts = { canonical: true, extraEntropy: true }; | ||
let sigBytes = await Secp256k1.sign(hashBytes, privKeyBytes, sigOpts); | ||
return sigBytes; | ||
}, | ||
getPrivateKey: async function (input) { | ||
if (!input.address) { | ||
//throw new Error('should put the address on the input there buddy...'); | ||
console.warn("missing address:", input.txid, input.outputIndex); | ||
//@ts-ignore - TODO update type | ||
return null; | ||
} | ||
|
||
let keyInfo = keysMap[input.address]; | ||
return keyInfo.privateKey; | ||
}, | ||
|
||
getPublicKey: async function (txInput, i) { | ||
let privKeyBytes = await keyUtils.getPrivateKey(txInput, i); | ||
if (!privKeyBytes) { | ||
return null; | ||
} | ||
let pubKeyBytes = await keyUtils.toPublicKey(privKeyBytes); | ||
|
||
return pubKeyBytes; | ||
}, | ||
|
||
toPublicKey: async function (privKeyBytes) { | ||
// TODO use secp256k1 directly | ||
return await DashKeys.utils.toPublicKey(privKeyBytes); | ||
}, | ||
}; | ||
|
||
async function main() { | ||
let network = "testnet"; | ||
|
||
// let phrase = await DashPhrase.generate(); | ||
let phrase = | ||
"casino reveal crop open ordinary garment spy pizza clown exercise poem enjoy"; | ||
let salt = ""; | ||
let seedBytes = await DashPhrase.toSeed(phrase, salt); | ||
let walletKey = await DashHd.fromSeed(seedBytes); | ||
|
||
let accountIndex = 0; // pick the desired account for paying the fee | ||
let addressIndex = 8; // pick an address with funds | ||
let accountKey = await walletKey.deriveAccount(accountIndex); | ||
let use = DashHd.RECEIVE; | ||
let xprvKey = await accountKey.deriveXKey(use); | ||
let addressKey = await xprvKey.deriveAddress(addressIndex); | ||
if (!addressKey.privateKey) { | ||
throw new Error("not an error, just a lint hack"); | ||
} | ||
|
||
let addr = await DashHd.toAddr(addressKey.publicKey, { version: network }); | ||
let pkhBytes = await DashKeys.addrToPkh(addr, { | ||
//@ts-ignore | ||
version: network, | ||
}); | ||
let pkh = DashKeys.utils.bytesToHex(pkhBytes); | ||
let wif = await DashHd.toWif(addressKey.privateKey, { version: network }); | ||
console.log(); | ||
console.log(`Address: ${addr}`); | ||
console.log(`WIF: ${wif}`); | ||
|
||
keysMap[addr] = { | ||
address: addr, | ||
publicKey: addressKey.publicKey, | ||
privateKey: addressKey.privateKey, | ||
pubKeyHash: pkh, | ||
}; | ||
|
||
let rpcAuthUrl = "https://api:[email protected]"; | ||
let utxos = await DashTx.utils.rpc(rpcAuthUrl, "getaddressutxos", { | ||
addresses: [addr], | ||
}); | ||
let total = DashTx.sum(utxos); | ||
console.log(); | ||
console.log(`utxos (${total})`); | ||
console.log(utxos); | ||
|
||
let creditOutputs = [{ satoshis: total - 10000, pubKeyHash: pkh }]; | ||
let totalCredits = DashTx.sum(creditOutputs); | ||
let burnOutput = { satoshis: totalCredits, pubKeyHash: pkh }; | ||
//@ts-ignore - TODO add types | ||
let assetLockScript = DashPlatform.Tx.packAssetLock({ | ||
creditOutputs, | ||
}); | ||
|
||
const VERSION_PLATfORM = 3; | ||
const TYPE_ASSET_LOCK = 8; | ||
let txDraft = { | ||
version: VERSION_PLATfORM, | ||
type: TYPE_ASSET_LOCK, | ||
inputs: utxos, | ||
outputs: [burnOutput], | ||
extraPayload: assetLockScript, | ||
}; | ||
console.log(); | ||
console.log(`txDraft:`); | ||
console.log(txDraft); | ||
|
||
let dashTx = DashTx.create(keyUtils); | ||
let txSigned = await dashTx.hashAndSignAll(txDraft); | ||
console.log(); | ||
console.log(`txSigned:`); | ||
console.log(txSigned); | ||
} | ||
|
||
async function createIdentitiyKeys() { | ||
let idIndex = 0; // increment to first unused | ||
let identityEcdsaKey = await DashHd.derivePath(walletKey, identityEcdsaPath); | ||
let identityKey = await DashHd.deriveChild( | ||
identityEcdsaKey, | ||
idIndex, | ||
DashHd.HARDENED, | ||
); | ||
|
||
const MASTER_KEY = 0; | ||
const HIGH_AUTH_KEY = 1; | ||
const CRITICAL_KEY = 2; | ||
const TRANSFER_KEY = 3; | ||
let keyDescs = [ | ||
{ | ||
id: MASTER_KEY, | ||
securityLevel: Dpp.IdentityPublicKey.SECURITY_LEVELS.MASTER, | ||
}, | ||
{ | ||
id: HIGH_AUTH_KEY, | ||
securityLevel: Dpp.IdentityPublicKey.SECURITY_LEVELS.HIGH, | ||
}, | ||
{ | ||
id: CRITICAL_KEY, | ||
securityLevel: Dpp.IdentityPublicKey.SECURITY_LEVELS.CRITICAL, | ||
}, | ||
{ | ||
id: TRANSFER_KEY, | ||
purpose: Dpp.IdentityPublicKey.PURPOSES.TRANSFER, | ||
securityLevel: Dpp.IdentityPublicKey.SECURITY_LEVELS.CRITICAL, | ||
}, | ||
]; | ||
|
||
let keys = []; | ||
let dppKeys = []; | ||
for (let keyDesc of keyDescs) { | ||
let key = await DashHd.deriveChild( | ||
identityKey, | ||
keyDesc.id, | ||
DashHd.HARDENED, | ||
); | ||
keys.push(key); | ||
|
||
let MAGIC_NUMBER_1 = 1; // TODO why? | ||
let dppKey = new Dpp.IdentityPublicKey(MAGIC_NUMBER_1); | ||
dppKey.setId(keyDesc.id); | ||
dppKey.setData(key.publicKey); | ||
if (keyDesc.purpose) { | ||
dppKey.setPurpose(keyDesc.purpose); | ||
} | ||
dppKey.setSecurityLevel(keyDesc.securityLevel); | ||
dppKeys.push(dppKey); | ||
} | ||
} | ||
|
||
function getAllTheKeys() { | ||
// Create Identity | ||
const identity = dpp.identity.create(assetLockProof.createIdentifier(), [ | ||
masterKey, | ||
highAuthKey, | ||
criticalAuthKey, | ||
transferKey, | ||
]); | ||
|
||
// Create ST | ||
const identityCreateTransition = dpp.identity.createIdentityCreateTransition( | ||
identity, | ||
assetLockProof, | ||
); | ||
|
||
// Create key proofs | ||
const [stMasterKey, stHighAuthKey, stCriticalAuthKey, stTransferKey] = | ||
identityCreateTransition.getPublicKeys(); | ||
|
||
// Sign master key | ||
|
||
identityCreateTransition.signByPrivateKey( | ||
identityMasterPrivateKey.toBuffer(), | ||
IdentityPublicKey.TYPES.ECDSA_SECP256K1, | ||
); | ||
|
||
stMasterKey.setSignature(identityCreateTransition.getSignature()); | ||
|
||
identityCreateTransition.setSignature(undefined); | ||
|
||
// Sign high auth key | ||
|
||
identityCreateTransition.signByPrivateKey( | ||
identityHighAuthPrivateKey.toBuffer(), | ||
IdentityPublicKey.TYPES.ECDSA_SECP256K1, | ||
); | ||
|
||
stHighAuthKey.setSignature(identityCreateTransition.getSignature()); | ||
|
||
identityCreateTransition.setSignature(undefined); | ||
|
||
// Sign critical auth key | ||
|
||
identityCreateTransition.signByPrivateKey( | ||
identityCriticalAuthPrivateKey.toBuffer(), | ||
IdentityPublicKey.TYPES.ECDSA_SECP256K1, | ||
); | ||
|
||
stCriticalAuthKey.setSignature(identityCreateTransition.getSignature()); | ||
|
||
identityCreateTransition.setSignature(undefined); | ||
|
||
// Sign transfer key | ||
|
||
identityCreateTransition.signByPrivateKey( | ||
identityTransferPrivateKey.toBuffer(), | ||
IdentityPublicKey.TYPES.ECDSA_SECP256K1, | ||
); | ||
|
||
stTransferKey.setSignature(identityCreateTransition.getSignature()); | ||
|
||
identityCreateTransition.setSignature(undefined); | ||
|
||
// Set public keys back after updating their signatures | ||
identityCreateTransition.setPublicKeys([ | ||
stMasterKey, | ||
stHighAuthKey, | ||
stCriticalAuthKey, | ||
stTransferKey, | ||
]); | ||
|
||
// Sign and validate state transition | ||
|
||
identityCreateTransition.signByPrivateKey( | ||
assetLockPrivateKey.toBuffer(), | ||
IdentityPublicKey.TYPES.ECDSA_SECP256K1, | ||
); | ||
|
||
// TODO(versioning): restore | ||
// @ts-ignore | ||
// const result = await dpp.stateTransition.validateBasic( | ||
// identityCreateTransition, | ||
// // TODO(v0.24-backport): get rid of this once decided | ||
// // whether we need execution context in wasm bindings | ||
// new StateTransitionExecutionContext(), | ||
// ); | ||
|
||
// if (!result.isValid()) { | ||
// const messages = result.getErrors().map((error) => error.message); | ||
// throw new Error(`StateTransition is invalid - ${JSON.stringify(messages)}`); | ||
// } | ||
|
||
return { identity, identityCreateTransition, identityIndex }; | ||
} | ||
|
||
main(); |