Skip to content
Open
28 changes: 21 additions & 7 deletions packages/bitcore-lib-xpi/lib/script/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -775,21 +775,21 @@ Script.buildMultisigIn = function(pubkeys, threshold, signatures, opts) {
} else {
s.add(Opcode(checkBitsHex));
}

}
else if (N >= 9 && N <= 16) {
s.add(0x02);
s.add(checkBitsHex);
}
}
else if (N >= 17 && N <= 20) {
s.add(0x03);
s.add(checkBitsHex);
}
} else {
s.add(Opcode.OP_0); // ecdsa schnorr mode; multisig dummy param of 0
}


_.each(signatures, function(signature) {
$.checkArgument(BufferUtil.isBuffer(signature), 'Signatures must be an array of Buffers');
// TODO: allow signatures to be an array of Signature objects
Expand Down Expand Up @@ -818,7 +818,7 @@ Script.buildP2SHMultisigIn = function(pubkeys, threshold, signatures, opts) {
$.checkArgument(_.isArray(signatures));
opts = opts || {};
var s = new Script();

if (opts.signingMethod === "schnorr" && opts.checkBits) {

// Spec according to https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/2019-11-15-schnorrmultisig.md#scriptsig-size
Expand All @@ -843,15 +843,15 @@ Script.buildP2SHMultisigIn = function(pubkeys, threshold, signatures, opts) {
else if (N >= 9 && N <= 16) {
s.add(0x02);
s.add(checkBitsHex);
}
}
else if (N >= 17 && N <= 20) {
s.add(0x03);
s.add(checkBitsHex);
}
} else {
s.add(Opcode.OP_0); // ecdsa schnorr mode; multisig dummy param of 0
}

_.each(signatures, function(signature) {
$.checkArgument(BufferUtil.isBuffer(signature), 'Signatures must be an array of Buffers');
// TODO: allow signatures to be an array of Signature objects
Expand Down Expand Up @@ -914,6 +914,20 @@ Script.buildDataOut = function(data, encoding) {
return s;
};

/**
* @returns {Script} a new OP_RETURN script with data
* @param {(string|Buffer)} data - the data to embed in the output
*/
Script.buildOnchainMessage = function(data) {
var s = new Script();
s.add(Opcode.OP_RETURN);
if (!_.isUndefined(data)) {
s.add( Buffer.from('03030303', 'hex'));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put the lokad id to constant instead of hardcode

s.add( Buffer.from(data));
}
return s;
};

/**
* @param {Script|Address} script - the redeemScript for the new p2sh output.
* It can also be a p2sh address
Expand Down
17 changes: 12 additions & 5 deletions packages/bitcore-lib-xpi/lib/transaction/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,13 @@ Transaction.prototype.addData = function(value) {
return this;
};

Transaction.prototype.addOnchainMessage = function(value) {
this.addOutput(new Output({
script: Script.buildOnchainMessage(value),
satoshis: 0
}));
return this;
};

/**
* Add an output to the transaction.
Expand Down Expand Up @@ -1089,7 +1096,7 @@ Transaction.prototype.removeOutput = function(index) {
Transaction.prototype.sort = function() {
this.sortInputs(function(inputs) {
var copy = Array.prototype.concat.apply([], inputs);
let i = 0;
let i = 0;
copy.forEach((x) => { x.i = i++});
copy.sort(function(first, second) {
return compare(first.prevTxId, second.prevTxId)
Expand All @@ -1100,7 +1107,7 @@ Transaction.prototype.sort = function() {
});
this.sortOutputs(function(outputs) {
var copy = Array.prototype.concat.apply([], outputs);
let i = 0;
let i = 0;
copy.forEach((x) => { x.i = i++});
copy.sort(function(first, second) {
return first.satoshis - second.satoshis
Expand Down Expand Up @@ -1198,7 +1205,7 @@ Transaction.prototype.removeInput = function(txId, outputIndex) {
*/
Transaction.prototype.sign = function(privateKey, sigtype, signingMethod) {
signingMethod = signingMethod || "ecdsa"

$.checkState(this.hasAllUtxoInfo(), 'Not all utxo information is available to sign the transaction.');
var self = this;
if (_.isArray(privateKey)) {
Expand All @@ -1220,7 +1227,7 @@ Transaction.prototype.getSignatures = function(privKey, sigtype, signingMethod)
sigtype = sigtype || (Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID);
var transaction = this;
var results = [];

var hashData = Hash.sha256ripemd160(privKey.publicKey.toBuffer());
_.each(this.inputs, function forEachInput(input, index) {
_.each(input.getSignatures(transaction, privKey, index, sigtype, hashData, signingMethod), function(signature) {
Expand Down Expand Up @@ -1353,7 +1360,7 @@ Transaction.prototype.isCoinbase = function() {

Transaction.prototype.setVersion = function(version) {
$.checkArgument(
JSUtil.isNaturalNumber(version) && version <= CURRENT_VERSION,
JSUtil.isNaturalNumber(version) && version <= CURRENT_VERSION,
'Wrong version number');
this.version = version;
return this;
Expand Down
1 change: 1 addition & 0 deletions packages/bitcore-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
"lru-cache": "4.1.3",
"mkdirp": "0.5.1",
"mongodb": "3.1.1",
"node-forge": "^1.3.1",
"progress": "2.0.0",
"request": "2.88.0",
"ripple-lib": "1.4.2",
Expand Down
1 change: 1 addition & 0 deletions packages/bitcore-wallet-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"bip38": "^1.3.0",
"json-stable-stringify": "^1.0.1",
"lodash": "^4.17.20",
"node-forge": "^1.3.1",
"preconditions": "^2.2.3",
"sjcl": "1.0.3",
"source-map-loader": "^0.2.4",
Expand Down
16 changes: 14 additions & 2 deletions packages/bitcore-wallet-client/src/lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ const crypto = Bitcore.crypto;
let SJCL = {};

const MAX_DECIMAL_ANY_COIN = 18; // more that 14 gives rounding errors

export class Utils {
static encryptMessageOnchain(message, privKey, addressTo) {}

static getChain(coin: string): string {
let normalizedChain = coin.toUpperCase();
if (Constants.ERC20.includes(coin)) {
Expand Down Expand Up @@ -345,10 +346,12 @@ export class Utils {
t.from(txp.inputs);
break;
}

if (txp.toAddress && txp.amount && !txp.outputs) {
t.to(txp.toAddress, txp.amount);
} else if (txp.outputs) {
if (txp.messageOnChain) {
t.addOnchainMessage(txp.messageOnChain);
}
_.each(txp.outputs, o => {
$.checkState(
o.script || o.toAddress,
Expand All @@ -370,6 +373,11 @@ export class Utils {
t.fee(txp.fee);
t.change(txp.changeAddress.address);

// backup opreturnOutput for checking other output
let opReturnOutput = null;
if (txp.messageOnChain) {
opReturnOutput = t.outputs.shift();
}
// Shuffle outputs for improved privacy
if (t.outputs.length > 1) {
var outputOrder = _.reject(txp.outputOrder, order => {
Expand Down Expand Up @@ -411,6 +419,10 @@ export class Utils {
'Failed state: totalInputs - totalOutputs <= Defaults.MAX_TX_FEE(coin) at buildTx'
);

// Return opreturnOuput after checking other output
if (opReturnOutput) {
t.outputs.unshift(opReturnOutput);
}
return t;
} else {
const {
Expand Down
26 changes: 21 additions & 5 deletions packages/bitcore-wallet-service/src/lib/chain/btc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export class BtcChain implements IChain {
}

checkDust(output) {
const dustThreshold = Math.max(Defaults.MIN_OUTPUT_AMOUNT, this.bitcoreLib.Transaction.DUST_AMOUNT);
const dustThreshold = this.bitcoreLib.Transaction.DUST_AMOUNT;

if (output.amount < dustThreshold) {
return Errors.DUST_AMOUNT;
Expand Down Expand Up @@ -285,8 +285,11 @@ export class BtcChain implements IChain {
if (!outputsSize) {
outputsSize = this.getEstimatedSizeForSingleOutput();
}

const size = overhead + inputSize * nbInputs + outputsSize;
let byteMessage = 0;
if (txp.messageOnChain) {
byteMessage = Buffer.from(txp.messageOnChain).length + 17;
}
const size = overhead + inputSize * nbInputs + outputsSize + byteMessage;
return Math.ceil(size * 1 + this.getSizeSafetyMargin(opts));
}

Expand Down Expand Up @@ -319,7 +322,6 @@ export class BtcChain implements IChain {

getBitcoreTx(txp, opts = { signed: true }) {
const t = new this.bitcoreLib.Transaction();

// BTC tx version
if (txp.version <= 3) {
t.setVersion(1);
Expand Down Expand Up @@ -359,12 +361,16 @@ export class BtcChain implements IChain {
t.from(inputs);
break;
}

if (txp.messageOnChain) {
t.addOnchainMessage(txp.messageOnChain);
}
_.each(txp.outputs, o => {
$.checkState(
o.script || o.toAddress,
'Failed state: Output should have either toAddress or script specified at <getBitcoreTx()>'
);
if (o.message) {
}
if (o.script) {
t.addOutput(
new this.bitcoreLib.Transaction.Output({
Expand All @@ -383,6 +389,11 @@ export class BtcChain implements IChain {
t.change(txp.changeAddress.address);
}

// backup opreturnOutput for checking other output
let opReturnOutput = null;
if (txp.messageOnChain) {
opReturnOutput = t.outputs.shift();
}
// Shuffle outputs for improved privacy
if (t.outputs.length > 1) {
const outputOrder = _.reject(txp.outputOrder, (order: number) => {
Expand Down Expand Up @@ -412,6 +423,11 @@ export class BtcChain implements IChain {
'Failed state: fee-too-high at <getBitcoreTx()>'
);

// Return opreturnOuput after checking other output
if (opReturnOutput) {
t.outputs.unshift(opReturnOutput);
}

if (opts.signed) {
const sigs = txp.getCurrentSignatures();
_.each(sigs, x => {
Expand Down
4 changes: 4 additions & 0 deletions packages/bitcore-wallet-service/src/lib/model/txproposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface ITxProposal {
chain: string;
network: string;
message: string;
messageOnChain: string;
payProUrl: string;
from: string;
changeAddress: string;
Expand Down Expand Up @@ -84,6 +85,7 @@ export class TxProposal {
chain: string;
network: string;
message: string;
messageOnChain: string;
payProUrl: string;
from: string;
changeAddress: any;
Expand Down Expand Up @@ -163,6 +165,7 @@ export class TxProposal {
x.network = opts.network;
x.signingMethod = opts.signingMethod;
x.message = opts.message;
x.messageOnChain = opts.messageOnChain;
x.payProUrl = opts.payProUrl;
x.changeAddress = opts.changeAddress;
x.outputs = _.map(opts.outputs, output => {
Expand Down Expand Up @@ -237,6 +240,7 @@ export class TxProposal {
x.outputs = obj.outputs;
x.amount = obj.amount;
x.message = obj.message;
x.messageOnChain = obj.messageOnChain;
x.payProUrl = obj.payProUrl;
x.changeAddress = obj.changeAddress;
x.inputs = obj.inputs;
Expand Down
Loading