-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathindex.js
1 lines (1 loc) · 18.6 KB
/
index.js
1
function t(t){return t&&"object"==typeof t&&"default"in t?t.default:t}Object.defineProperty(exports,"__esModule",{value:!0});var e=t(require("bitcoinsource"));var n=t(require("axios"));var s={BITCOIN_NETWORK:"testnet",BLOCK_EXPLORER_URL:"https://tbch.blockdozer.com/api",MIN_SATOSHI_AMOUNT:546,MIN_NON_DUST_AMOUNT:2750,UN_P2SH_URL:"http://localhost:3000",DEFAULT_FEE:2e3};e.versionGuard=(()=>!0),e.Networks.defaultNetwork=e.Networks[s.BITCOIN_NETWORK];class r{constructor(t){this.kind=t}toJSON(){return{}}}class a extends Error{constructor(t,e,...n){super(...n),this.name="Error",this.message=t+(e?`: ${e}`:""),Error.captureStackTrace&&Error.captureStackTrace(this,a)}}function o(t){return{writable:!0,value:t}}function i(t){return Buffer.from(t)}function c(t){return t.toString()}function u(t,e,{[t]:n,...s}){return{[e]:n,...s}}const d=async t=>{try{return(await t).data}catch(t){if(t.response){const{status:e,statusText:n,data:s}=t.response;const{method:r,url:o}=t.response.config||{method:"unknown",url:"unknown"};const i=t.response.config.data;const c=s.error||(-1!==s.indexOf("Code:")?s:n);throw new a(`\nCommunication Error\n\nstatus\t${e} ${n}\nmessage\t${c}\nrequest\t${r} ${o}${i?`\ndata\t${i}`:""}`)}throw new a("Communication error","Service unavailable.")}};const h=async(t,e=s.BLOCK_EXPLORER_URL)=>d(n.get(`${e}${t}`));const p=async(t,e,r=s.BLOCK_EXPLORER_URL)=>d(n.post(`${r}${t}`,e));const l=async t=>{const{balanceSat:e,unconfirmedBalanceSat:n}=await(async t=>h(`/addr/${t.toString()}`))(t);return e+n};const m=async t=>u("txid","txId",await p("/tx/send",{rawtx:t.toString()}));const w=async t=>(await h(`/rawtx/${t}`)).rawtx;const g=async t=>{const e=t.toString();return(t=>t.filter((t,e,n)=>n.findIndex(e=>e.txId===t.txId&&e.vout===t.vout)===e))((await h(`/addr/${e}/utxo`)).map(t=>u("txid","txId",t))).map(t=>(t.spent=!1,t))};const f=async t=>{const e=await(async t=>h(`/tx/${t}`))(t.txId);const n=e.vout[t.outputNumber];const s=n.scriptPubKey.addresses[0];const{txId:r}=t;const a=t.outputNumber;const o=parseFloat(n.value);const i=1e8*o;const c=e.blockheight;const{confirmations:u}=e;const d=!!n.spentTxId;return{address:s,txId:r,vout:a,scriptPubKey:n.scriptPubKey.hex,amount:o,satoshis:i,height:c,confirmations:u,spent:d}};const y=async t=>p("/",t,s.UN_P2SH_URL);const S=async t=>h(`/un-p2sh/${t}`,s.UN_P2SH_URL);const b=async t=>h(`/txos/${t.toString()}`,s.UN_P2SH_URL);const O=async(t,e)=>p("/txos/set-spent/",{txId:t,vOut:e},s.UN_P2SH_URL);const{PublicKey:_,Transaction:P}=e;class x extends r{constructor(t={}){super("script"),Object.keys(t).forEach(e=>{this[e]=t[e]}),this.__amount=t.__amount||s.MIN_NON_DUST_AMOUNT}getData(t){return this[t]}getSerializeData(){const t={...this};return delete t.__publicKeys,delete t.__amount,delete t.kind,Object.entries(t).reduce((t,e)=>t.concat(e),[]).map(i)}setSerializedData(t){const e=t.map(c);for(let t=0;t<e.length;t+=2)this[e[t]]=e[t+1];delete this.kind}static fromMultiSigScriptHashInput(t){const e=t.redeemScript||A.redeemScriptFromP2shScript(A.fromBuffer(t._scriptBuffer));return this.fromRedeemScript(e)}static fromRedeemScript(t){const{chunks:e}=t;const n=e.slice(4,e.length).filter((t,e)=>e%2==0).map(t=>t.buf);const r=new this;return r.setSerializedData(n),r.__publicKeys=t.getPublicKeys(),r.__amount=s.MIN_SATOSHI_AMOUNT,r.kind="script",r}toJSON(){const t={...this};return t.kind="script",t.__publicKeys=this.__publicKeys.map(t=>t.toString()),t}static isJSON(t){return"script"===t.kind}static fromJSON(t){const e={...t};return e.__publicKeys=e.__publicKeys.map(t=>new _(t)),new this(e)}}const{PublicKey:I,Address:K,Transaction:k}=e;class N extends r{constructor(t,e){super("pkh"),this.address=t,this.amount=e}static fromPublicKeyHashInput(t){const{output:e}=t;return new this(e.script.toAddress(),e.satoshis)}toJSON(){return{kind:"pkh",address:this.address.toString(),amount:this.amount}}static isJSON(t){return t.address&&t.amount}static fromJSON(t){return new this(new K(t.address),t.amount)}}class D extends r{constructor(t){super("return"),this.data=t||""}getData(){return this.data}toJSON(){return{kind:"return",data:this.data}}static isJSON(t){return!!t.data}static fromJSON(t){return new this(t.data)}}const{Address:M,PublicKey:T,Signature:v,Script:E,Opcode:U}=e;class A extends E{static outputScriptFromScriptOutputData(t){const e=t.__publicKeys||[];const n=t.getSerializeData();const s=new A;return s.add("OP_1"),e.forEach(t=>s.add(t.toBuffer())),s.add(`OP_${e.length}`),s.add("OP_CHECKMULTISIG"),n.forEach(t=>s.add(t).add("OP_DROP")),s}getPublicKeys(){let t=1;const e=[];for(;this.chunks[t].buf;)e.push(new T(this.chunks[t].buf)),t+=1;return e}static inputScriptFromScriptOutputData(t,e,n,s){const r=new A;return n.forEach(t=>{r.add(t)}),r.add(s),r}isDbDataScript(){return!(!(this.chunks.length>=5&&this.chunks[0].opcodenum===U.OP_1&&this.chunks[1].buf)||20!==this.chunks[1].buf.length&&33!==this.chunks[1].buf.length||this.chunks[2].opcodenum!==U.OP_1||this.chunks[3].opcodenum!==U.OP_CHECKMULTISIG||!this.chunks[4].buf||this.chunks[5].opcodenum!==U.OP_DROP)}static isP2shScript(t){return!(3!==t.chunks.length||t.chunks[0].opcodenum!==U.OP_0||!t.chunks[1].buf||!t.chunks[2].buf)}static redeemScriptFromP2shScript(t){if(!this.isP2shScript(t))throw new Error("not a p2sh script");const e=new E(t.chunks[2].buf);const n=new A;return n.chunks=e.chunks,n}toOutputData(t=s.MIN_SATOSHI_AMOUNT){if(this.isDbDataScript()){const t=new x({__publicKeys:[new T(this.chunks[1].buf)],__amount:s.MIN_SATOSHI_AMOUNT});const e=this.chunks.slice(4,this.chunks.length).filter((t,e)=>e%2==0).map(t=>t.buf);return t.setSerializedData(e),t}if(this.isPublicKeyHashOut()){const e=new M(this.getData());return new N(e,t)}if(this.isDataOut())return new D(this.getData().toString());throw new Error("unknown script type")}}const{PublicKey:R,Address:H}=e;class J extends r{constructor(t){super("change"),this.address=t}toJSON(){return{kind:"change",address:this.address.toString()}}static isJSON(t){return t.address}static fromJSON(t){return new this(new H(t.address))}}class ${static fromJSON(t){if(x.isJSON(t))return x.fromJSON(t);if(N.isJSON(t))return N.fromJSON(t);if(J.isJSON(t))return J.fromJSON(t);if(D.isJSON(t))return D.fromJSON(t);throw new Error(`unrecognized json ${JSON.stringify(t)}`)}}const{Transaction:W,PublicKey:F,Address:B,BN:L,Script:C,encoding:j}=e;const{Output:z,Input:V}=W;const{MultiSigScriptHash:q,PublicKeyHashInput:G}=V;const{BufferReader:X}=j;class Q extends W{constructor(t){super(t),this._outputData=[],Object.defineProperty(this,"to",o(this._to)),Object.defineProperty(this,"from",o(this._from))}get dataInputs(){return this.inputs.map(t=>{if("MultiSigScriptHashInput"===t.constructor.name)return x.fromMultiSigScriptHashInput(t);if("PublicKeyHashInput"===t.constructor.name)return N.fromPublicKeyHashInput(t);if("Input"===t.constructor.name){const e=new C(t._scriptBuffer);const n=new A;if(n.chunks=e.chunks,n.isPublicKeyHashIn())return new N(n.toAddress(),0);if(A.isP2shScript(n)){const t=A.redeemScriptFromP2shScript(n);return x.fromRedeemScript(t)}}throw new Error(`unknown script class ${t.constructor.name}`)})}set dataInputs(t){throw Error("dataTransaction.dataInputs cannot be set directly, use dataTransaction.from or dataTransaction.fromScriptOutput")}get inputsWithData(){return this.dataInputs.filter(t=>"script"===t.kind)}fromMultiSig(t,e,n){const s=t.map(t=>u("txId","txid",t));return super.from(s,e,n)}_from(t){const e=u("txId","txid",t);return super.from(e)}fromScriptOutput(t,e){const n=A.outputScriptFromScriptOutputData(e);const s=new q({output:new z({script:new A(t.scriptPubKey),satoshis:Math.round(t.satoshis)}),prevTxId:t.txId,outputIndex:t.vout,script:new A},e.__publicKeys,1,null,n);return this.addInput(s),this}get outputData(){if(!this._outputData.length)throw new Error("dataTransaction.outputData is not initialized. Call dataTransaction.fetchDataOuptuts() first.");return this._outputData}set outputData(t){throw Error("dataTransaction.dataInputs cannot be set directly, use dataTransaction.toOutputData")}get outputsWithData(){return this.outputData.filter(t=>"script"===t.kind)}change(t){const e=this.outputs.length;return super.change(t),this.outputs.length>e&&this._outputData.push(new J(t)),this}toChangeOutput(t){const e=this.outputs.length;return super.change(t.address),this.outputs.length>e&&this._outputData.push(t),this}toPkhOutput(t){return super.to(t.address,t.amount),this._outputData.push(t),this}toScriptOutput(t){const e=A.outputScriptFromScriptOutputData(t);const n=A.buildScriptHashOut(e);const s=new z({script:n,satoshis:t.__amount});return this.addOutput(s),this._outputData.push(t),this}toReturnOutput(t){return this.addData(t.data),this._outputData.push(t),this}_to(t){switch(t.kind){case"change":return this.toChangeOutput(t);case"return":return this.toReturnOutput(t);case"script":return this.toScriptOutput(t);case"pkh":return this.toPkhOutput(t);default:throw new Error("Unsupported output kind")}}async fetchOutputData(){if(this._outputData.length)return this._outputData;const t=this.getTxId();const e=await S(t);return this._outputData=e.map($.fromJSON),this._outputData}getTxId(){return new X(this._getHash()).readReverse().toString("hex")}static async fromTxId(t){const e=await w(t);const n=new Q;return await n.fromString(e),n}}const{Mnemonic:Y,HDPrivateKey:Z,PrivateKey:tt,PublicKey:et,Address:nt}=e;class st{constructor(t){this.mnemonic=t||new Y,this.path="",this.hdPrivateKey=this.mnemonic.toHDPrivateKey(this.path,s.BITCOIN_NETWORK)}static getRandomMnemonic(){return(new Y).toString()}static fromMnemonic(t){return new st(t)}getMnemonic(){return this.mnemonic}getPath(){return this.path}derive(t=0,e=!1){const n=new st(this.mnemonic);return n.path=`${this.path}${this.path.length?"/":""}${t}${e?"'":""}`,n.hdPrivateKey=this.hdPrivateKey.derive(t,e),n}static getHdPrivateKey(){return new Z}getPrivateKey(){return this.hdPrivateKey.privateKey}getPublicKey(){return this.hdPrivateKey.publicKey}getAddress(){return this.address=this.address||this.getPublicKey().toAddress(),this.address}async getBalance(){const t=this.getAddress();return l(t.toString())}async getUtxosFromAddress(t,e){const n=await g(t.toString());for(let t=n.length-1;t>0;t-=1){const e=Math.floor(Math.random()*(t+1));const s=n[t];n[t]=n[e],n[e]=s}let s=0;const r=[];let a=0;for(;s<e&&a<n.length;)r.push(n[a]),s+=n[a].satoshis,a+=1;if(s<e)throw new Error(`Insufficient balance in address ${t.toString()}`);return r}async getUtxos(t){const e=this.getAddress();return this.getUtxosFromAddress(e,t)}async getTokenUtxos(){const t=this.getPublicKey();const e=await b(t);return Promise.all(e.map(async t=>{const e=await Q.fromTxId(t.txId);const n=(await e.fetchOutputData())[t.vOut];if(n){const s=A.outputScriptFromScriptOutputData(n);const r=A.buildScriptHashOut(s);const a=e.outputs[t.vOut].satoshis;return{txId:t.txId,vout:t.vOut,scriptPubKey:r,amount:Math.round(a/1e8),satoshis:a,amountSat:a,outputData:n}}return null}))}async sendTransaction(t,e=!1){const{txId:n}=await m(t);if(e){const e=JSON.stringify(t.outputData.map(t=>t.toJSON()));await y({txId:n,outputData:e})}return{txId:n}}async send(t,e){const n=new Q;const r=e||this.getAddress();const a=s.DEFAULT_FEE;const o=this.getPrivateKey();const i=t.reduce((t,e)=>t+parseInt(e.amount||0,10),0);return(await this.getUtxos(i+a)).forEach(n.from.bind(n)),t.forEach(n.to.bind(n)),n.change(r),n.sign(o),this.sendTransaction(n)}async sendAll(t){const e=await this.getBalance();const n=s.DEFAULT_FEE;if(e>n){const s=new N(t,e-n);return this.send([s])}throw new Error("Insufficient funds to send payment.")}}class rt{constructor(t){this.wallet=t||new st}static fromMnemonic(t){return new this(new st(t))}async put(t){return this.update([],t)}async get(t){return Promise.all(t.map(async({txId:t,outputNumber:e})=>{const n=await Q.fromTxId(t);return await n.fetchOutputData(),n.outputData[e]}))}async update(t,e){const n=new Q;await Promise.all(t.map(async t=>{const e=await f(t);const s=await S(e.txId);const r=x.fromJSON(s[e.vout]);n.fromScriptOutput(e,r),await O(e.txId,e.vout)}));const r=s.DEFAULT_FEE+e.length*s.MIN_NON_DUST_AMOUNT;(await this.wallet.getUtxos(r)).forEach(n.from.bind(n)),e.forEach(n.to.bind(n)),n.change(this.wallet.getAddress()),n.sign(this.wallet.getPrivateKey());const{txId:a}=await this.wallet.sendTransaction(n,!0);return[...Array(e.length).keys()].map(t=>({txId:a,outputNumber:t}))}}class at{constructor(t){this.db=t||new rt}static fromMnemonic(t){return new this(rt.fromMnemonic(t))}async init(t){const e=Object.getPrototypeOf(async()=>{}).constructor;Object.entries(t).forEach(([t,n])=>{this[t]=new e(`"use strict"; return ${n}`).bind(this)()})}async create(t){const e=new x({...t,__publicKeys:[this.db.wallet.getPublicKey()]});return this.id=(await this.db.put([e]))[0],this.id}join(t){this.id=t}async getTokenUtxos(){const t=this.db.wallet.getPublicKey();const e=await b(t);const n=(await Promise.all(e.map(async t=>{const e=await Q.fromTxId(t.txId);return await e.fetchOutputData(),Object.assign({transaction:e},t)}))).filter(t=>t.transaction.outputData[t.vOut]);const s=await((t,e)=>Promise.all(t.map(t=>e(t))).then(e=>t.filter(t=>e.shift())))(n,(async t=>this.isValid(t.transaction.hash)).bind(this));return Promise.all(s.map(async t=>{const e=t.transaction.outputData[t.vOut];const n=A.outputScriptFromScriptOutputData(e);const s=A.buildScriptHashOut(n);const r=t.transaction.outputs[t.vOut].satoshis;return{txId:t.txId,vout:t.vOut,scriptPubKey:s,amount:Math.round(r/1e8),satoshis:r,amountSat:r,outputData:e}}))}async send(t,e){const n=await this.getTokenUtxos();let s=0;const r=n.filter(async e=>{const n=s<t;const{txId:r,vout:a,outputData:o}=e;const{balance:i}=o;return s+=i?parseInt(i,10):0,await O(r,a),n}).map(t=>({txId:t.txId,outputNumber:t.vout}));if(s<t)throw new Error("Insufficient token funds");const a=new x({balance:t.toString(10),__publicKeys:[e]});const o=s-t;const i=this.db.wallet.getPublicKey();const c=new x({balance:o.toString(10),__publicKeys:[i]});return this.db.update(r,[a,c])}async getBalance(){const t=await this.getTokenUtxos();return(await Promise.all(t.map(async t=>{const e=await S(t.txId);const n=x.fromJSON(e[t.vout]).getData("balance");return n?parseInt(n,10):0}))).reduce((t,e)=>t+e,0)}async isValidIssuance(t){return this.id.txId===t.getTxId()}async isValidTransfer(t,e){return Promise.all(t.inputsWithData.map(async(n,s)=>this.isValid(t.inputs[s].prevTxId.toString("hex"),e+2))).then(t=>t.every(t=>t))}async isValid(t,e=0){const n=await Q.fromTxId(t);return await n.fetchOutputData(),this.isIssuance(n)?this.isValidIssuance(n):!!this.isTransfer(n)&&this.isValidTransfer(n,e)}isIssuance(t){return 0===t.inputsWithData.length&&1===t.outputsWithData.length}isTransfer(t){return t.inputsWithData.length>=1}}const{Mnemonic:ot}=e;class it{constructor(){this.wallet=new st}static getRandomMnemonic(){return st.getRandomMnemonic().toString()}static fromMnemonic(t){const e=new this;const n=new ot(t);return e.wallet=new st(n),e}getMnemonic(){return this.wallet.getMnemonic().toString()}getPath(){return this.wallet.path}getPrivateKey(){return this.wallet.getPrivateKey().toString()}getPublicKey(){return this.wallet.getPublicKey().toString()}getAddress(t){const e=t||"legacy";if(!["legacy","bitpay","cashaddr"].includes(e))throw new Error("second parameter in wallet.getAddress must be 'legacy', 'bitpay', or 'cashaddr'");return this.wallet.getAddress().toString(e)}async getBalance(){return this.wallet.getBalance()}derive(t=0,e=!1){const n=new it;const s=this.wallet.derive(t,e);return n.wallet=s,n}async send(t,e,n){const s=$.fromJSON({amount:t,address:e});return this.wallet.send([s],n)}async transaction(t,e){const n=t.map($.fromJSON);return this.wallet.send(n,e)}static fromHdPrivateKey(){throw new Error("\nwallet.fromHdPrivateKey is not supported anymore. Use wallet.fromMnemonic instead.\n\nFor example:\nconst mnemonic = Wallet.getRandomMnemonic()\nconst wallet = wallet.fromMnemonic(mnemonic)\n ")}}const{Mnemonic:ct}=e;class ut{constructor(t){this.db=new rt(t?t.wallet:null)}static fromMnemonic(t){const e=new ct(t);const n=new this;return n.db=rt.fromMnemonic(e),n}getWallet(){const t=this.db.wallet.getMnemonic();return it.fromMnemonic(t.toString())}toScriptOutputData(t){return x.fromJSON({...t.data,kind:"script",__publicKeys:t.owners||[this.getWallet().getPublicKey().toString()],__amount:t.amount||s.MIN_NON_DUST_AMOUNT})}async putReturn(t){const e=new D(t);const{txId:n}=await this.db.wallet.send([e]);return{txId:n,outputNumber:0}}async put(t,e,n){const s={data:t,owners:e,amount:n};const r=this.toScriptOutputData(s);return(await this.db.put([r]))[0]}async get(t){const[e]=await this.db.get([t]);const n=e.toJSON();const s={data:n,amount:n.__amount,owners:n.__publicKeys};return delete s.data.kind,delete s.data.__amount,delete s.data.__publicKeys,s}async update(t,e,n,r=s.MIN_NON_DUST_AMOUNT){const a={data:e,owners:n,amount:r};const o=this.toScriptOutputData(a);return(await this.db.update([t],[o]))[0]}async transaction(t){const e=t.map(t=>t.outputId);const n=t.map(this.toScriptOutputData.bind(this));return this.db.update(e,n)}getRandomMnemonic(){throw new Error("db.getRandomMnemonic does not exist. Use db.getWallet().getRandomMnemonic() instead.")}getPrivateKey(){throw new Error("db.getPrivateKey does not exist. Use db.getWallet().getPrivateKey() instead.")}getPublicKey(){throw new Error("db.getPublicKey does not exist. Use db.getWallet().getPublicKey() instead.")}getAddress(){throw new Error("db.getAddress does not exist. Use db.getWallet().getAddress() instead.")}static fromHdPrivateKey(){throw new Error("\ndb.fromHdPrivateKey is not supported anymore. Use db.fromMnemonic instead.\n\nFor example:\nconst mnemonic = Wallet.getRandomMnemonic()\nconst db = Db.fromMnemonic(mnemonic)\n ")}}const{PublicKey:dt,Mnemonic:ht}=e;exports.Token=class{constructor(t){this.token=new at(t?t.db:null)}static fromMnemonic(t){const e=new ht(t);const n=new this;return n.token=at.fromMnemonic(e),n}getWallet(){const t=this.token.db.wallet.getMnemonic();return it.fromMnemonic(t.toString())}getDb(){const t=this.token.db.wallet.getMnemonic();return ut.fromMnemonic(t.toString())}async create(t){return this.token.create(t)}join(t){return this.token.join(t)}async send(t,e){const n=dt.fromString(e);return this.token.send(t,n)}async getBalance(){return this.token.getBalance()}getRandomMnemonic(){throw new Error("token.getRandomMnemonic does not exist. Use token.getWallet().getRandomMnemonic() instead.")}getPrivateKey(){throw new Error("token.getPrivateKey does not exist. Use token.getWallet().getPrivateKey() instead.")}getPublicKey(){throw new Error("token.getPublicKey does not exist. Use token.getWallet().getPublicKey() instead.")}getAddress(){throw new Error("token.getAddress does not exist. Use token.getWallet().getAddress() instead.")}static fromHdPrivateKey(){throw new Error("\ntoken.fromHdPrivateKey is not supported anymore. Use token.fromMnemonic instead.\n\nFor example:\nconst mnemonic = Wallet.getRandomMnemonic()\nconst token = Token.fromMnemonic(mnemonic)\n ")}},exports.Db=ut,exports.Wallet=it,exports.Source=e;