From 4c4ec4013f22e2318f36d9f64295fd2aad6db8c7 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 22 Aug 2024 18:39:39 -0600 Subject: [PATCH] WIP: creates assetLockTx --- demo.js | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 demo.js diff --git a/demo.js b/demo.js new file mode 100644 index 0000000..35abccf --- /dev/null +++ b/demo.js @@ -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'// + 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. */ +let keysMap = {}; + +/** @type {Required} */ +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:null@trpc.digitalcash.dev"; + 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();