script parsing.
This commit is contained in:
parent
caf52c0579
commit
00e7fcc1ad
@ -42,7 +42,7 @@ function Coin(tx, index) {
|
||||
this.version = options.version;
|
||||
this.height = options.height;
|
||||
this.value = options.value;
|
||||
this.script = options.script;
|
||||
this.script = bcoin.script(options.script);
|
||||
this.coinbase = options.coinbase;
|
||||
this.hash = options.hash;
|
||||
this.index = options.index;
|
||||
@ -117,7 +117,7 @@ Coin.prototype.toJSON = function toJSON() {
|
||||
version: this.version,
|
||||
height: this.height,
|
||||
value: utils.btc(this.value),
|
||||
script: utils.toHex(this.script.encode()),
|
||||
script: utils.toHex(bcoin.protocol.framer(this.script)),
|
||||
coinbase: this.coinbase,
|
||||
hash: this.hash ? utils.revHex(this.hash) : null,
|
||||
index: this.index
|
||||
|
||||
@ -21,9 +21,9 @@ function Input(options, tx) {
|
||||
assert(typeof options.script !== 'string');
|
||||
|
||||
this.prevout = options.prevout;
|
||||
this.script = options.script || new bcoin.script([]);
|
||||
this.script = bcoin.script(options.script);
|
||||
this.sequence = options.sequence == null ? 0xffffffff : options.sequence;
|
||||
this.witness = options.witness || new bcoin.script.witness([]);
|
||||
this.witness = bcoin.script.witness(options.witness);
|
||||
this._mutable = !tx || (tx instanceof bcoin.mtx);
|
||||
|
||||
if (options.coin)
|
||||
@ -205,8 +205,8 @@ Input.prototype.toJSON = function toJSON() {
|
||||
index: this.prevout.index
|
||||
},
|
||||
coin: this.coin ? this.coin.toJSON() : null,
|
||||
script: utils.toHex(this.script.encode()),
|
||||
witness: utils.toHex(this.witness.encode()),
|
||||
script: utils.toHex(bcoin.protocol.framer.script(this.script)),
|
||||
witness: utils.toHex(bcoin.protocol.framer.witness(this.witness)),
|
||||
sequence: this.sequence
|
||||
};
|
||||
};
|
||||
@ -218,8 +218,8 @@ Input._fromJSON = function _fromJSON(json) {
|
||||
index: json.prevout.index
|
||||
},
|
||||
coin: json.coin ? bcoin.coin._fromJSON(json.coin) : null,
|
||||
script: new bcoin.script(new Buffer(json.script, 'hex')),
|
||||
witness: new bcoin.script.witness(new Buffer(json.witness, 'hex')),
|
||||
script: bcoin.protocol.parser.parseScript(new Buffer(json.script, 'hex')),
|
||||
witness: bcoin.protocol.parser.parseWitness(new Buffer(json.witness, 'hex')),
|
||||
sequence: json.sequence
|
||||
};
|
||||
};
|
||||
|
||||
@ -140,10 +140,10 @@ MTX.prototype.addInput = function addInput(options, index) {
|
||||
|
||||
input = bcoin.input(options, this);
|
||||
|
||||
if (options.script)
|
||||
if (options.script instanceof bcoin.script)
|
||||
input.script = options.script.clone();
|
||||
|
||||
if (options.witness)
|
||||
if (options.witness instanceof bcoin.script.witness)
|
||||
input.witness = options.witness.clone();
|
||||
|
||||
this.inputs.push(input);
|
||||
@ -666,8 +666,10 @@ MTX.prototype.scriptOutput = function scriptOutput(index, options) {
|
||||
output = this.outputs[index];
|
||||
assert(output);
|
||||
|
||||
if (options.script)
|
||||
if (options.script instanceof bcoin.script)
|
||||
output.script = options.script.clone();
|
||||
else if (options.script)
|
||||
output.script = bcoin.script(options.script);
|
||||
else
|
||||
output.script = Script.createOutputScript(options);
|
||||
};
|
||||
|
||||
@ -29,7 +29,7 @@ function Output(options, tx) {
|
||||
}
|
||||
|
||||
this.value = utils.satoshi(value || new bn(0));
|
||||
this.script = options.script || new bcoin.script([]);
|
||||
this.script = bcoin.script(options.script);
|
||||
this._mutable = !tx || (tx instanceof bcoin.mtx);
|
||||
|
||||
// For safety: do not allow usage of
|
||||
@ -114,7 +114,7 @@ Output.prototype.inspect = function inspect() {
|
||||
Output.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
value: utils.btc(this.value),
|
||||
script: utils.toHex(this.script.encode())
|
||||
script: utils.toHex(bcoin.protocol.framer.script(this.script))
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -335,7 +335,7 @@ Framer.utxo = function _utxo(coin, writer) {
|
||||
p.writeU32(coin.version);
|
||||
p.writeU32(height);
|
||||
p.write64(coin.value);
|
||||
p.writeVarBytes(coin.script.encode());
|
||||
Framer.script(coin.script, p);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
@ -355,7 +355,7 @@ Framer.coin = function _coin(coin, extended, writer) {
|
||||
p.writeU32(coin.version);
|
||||
p.writeU32(height);
|
||||
p.write64(coin.value);
|
||||
p.writeVarBytes(coin.script.encode());
|
||||
Framer.script(coin.script, p);
|
||||
p.writeU8(coin.coinbase ? 1 : 0);
|
||||
|
||||
if (extended) {
|
||||
@ -398,7 +398,7 @@ Framer.input = function _input(input, writer) {
|
||||
|
||||
p.writeHash(input.prevout.hash);
|
||||
p.writeU32(input.prevout.index);
|
||||
p.writeVarBytes(input.script.encode());
|
||||
Framer.script(input.script, p);
|
||||
p.writeU32(input.sequence);
|
||||
|
||||
if (!writer)
|
||||
@ -413,7 +413,7 @@ Framer.output = function _output(output, writer) {
|
||||
assert(output.value.byteLength() <= 8);
|
||||
|
||||
p.write64(output.value);
|
||||
p.writeVarBytes(output.script.encode());
|
||||
Framer.script(output.script, p);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
@ -456,12 +456,28 @@ Framer.witnessTX = function _witnessTX(tx, writer) {
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.witnessBlockSize = function witnessBlockSize(block) {
|
||||
return Framer.witnessBlock(block, new BufferWriter())._witnessSize;
|
||||
};
|
||||
// Scripts require extra magic since they're
|
||||
// so goddamn bizarre. Normally in an "encoded"
|
||||
// script we don't include the varint size
|
||||
// because scripthashes don't include them. This
|
||||
// is why script.encode/decode is separate
|
||||
// from the framer and parser.
|
||||
Framer.script = function _script(script, writer) {
|
||||
var data;
|
||||
|
||||
Framer.witnessTXSize = function witnessTXSize(tx) {
|
||||
return Framer.witnessTXSize(tx, new BufferWriter())._witnessSize;
|
||||
p = new BufferWriter(writer);
|
||||
|
||||
if (script.encode)
|
||||
data = script.encode();
|
||||
else
|
||||
data = script.raw || bcoin.script.encode(script.code);
|
||||
|
||||
p.writeVarBytes(data);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
Framer.witness = function _witness(witness, writer) {
|
||||
@ -672,6 +688,54 @@ Framer.alert = function alert(data, writer) {
|
||||
return p;
|
||||
};
|
||||
|
||||
// Witness size
|
||||
Framer.blockSizes = function blockSizes(block) {
|
||||
var sizes = Framer.witnessBlock(block, new BufferWriter());
|
||||
return {
|
||||
size: sizes._size,
|
||||
witnessSize: sizes._witnessSize
|
||||
};
|
||||
};
|
||||
|
||||
Framer.txSizes = function txSizes(tx) {
|
||||
var sizes = Framer.renderTX(tx, true, new BufferWriter());
|
||||
return {
|
||||
size: sizes._size,
|
||||
witnessSize: sizes._witnessSize
|
||||
};
|
||||
};
|
||||
|
||||
// Size with witness (if present)
|
||||
Framer.blockRealSize = function blockRealSize(block) {
|
||||
return Framer.blockSizes(block).size;
|
||||
};
|
||||
|
||||
Framer.txRealSize = function txRealSize(tx) {
|
||||
return Framer.txSizes(tx).size;
|
||||
};
|
||||
|
||||
// Size without witness
|
||||
Framer.blockSize = function blockSize(block) {
|
||||
return Framer.block(block, new BufferWriter())._size;
|
||||
};
|
||||
|
||||
Framer.txSize = function txSize(tx) {
|
||||
return Framer.renderTX(tx, false, new BufferWriter())._size;
|
||||
};
|
||||
|
||||
// Virtual size
|
||||
Framer.blockVsize = function blockVsize(block) {
|
||||
var sizes = this.blockSizes(block);
|
||||
var base = sizes.size - sizes.witnessSize;
|
||||
return (base * 4 + sizes.witnessSize + 3) / 4 | 0;
|
||||
};
|
||||
|
||||
Framer.txVsize = function txVsize(tx) {
|
||||
var sizes = this.txSizes(tx);
|
||||
var base = sizes.size - sizes.witnessSize;
|
||||
return (base * 4 + sizes.witnessSize + 3) / 4 | 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
@ -405,7 +405,7 @@ Parser.parseInput = function parseInput(p) {
|
||||
|
||||
hash = p.readHash('hex');
|
||||
index = p.readU32();
|
||||
script = new bcoin.script(p.readVarBytes());
|
||||
script = Parser.parseScript(p);
|
||||
sequence = p.readU32();
|
||||
|
||||
return {
|
||||
@ -413,6 +413,7 @@ Parser.parseInput = function parseInput(p) {
|
||||
hash: hash,
|
||||
index: index
|
||||
},
|
||||
coin: null,
|
||||
script: script,
|
||||
sequence: sequence,
|
||||
_size: p.end()
|
||||
@ -426,7 +427,7 @@ Parser.parseOutput = function parseOutput(p) {
|
||||
p.start();
|
||||
|
||||
value = p.read64();
|
||||
script = new bcoin.script(p.readVarBytes());
|
||||
script = Parser.parseScript(p);
|
||||
|
||||
return {
|
||||
value: value,
|
||||
@ -444,7 +445,7 @@ Parser.parseUTXO = function parseUTXO(p) {
|
||||
version = p.readU32();
|
||||
height = p.readU32();
|
||||
value = p.read64();
|
||||
script = new bcoin.script(p.readVarBytes());
|
||||
script = Parser.parseScript(p);
|
||||
|
||||
if (height === 0x7fffffff)
|
||||
height = -1;
|
||||
@ -467,7 +468,7 @@ Parser.parseCoin = function parseCoin(p, extended) {
|
||||
version = p.readU32();
|
||||
height = p.readU32();
|
||||
value = p.read64();
|
||||
script = new bcoin.script(p.readVarBytes());
|
||||
script = Parser.parseScript(p);
|
||||
coinbase = p.readU8() === 1;
|
||||
|
||||
if (extended) {
|
||||
@ -510,7 +511,7 @@ Parser.parseTX = function parseTX(p) {
|
||||
tx = Parser.parseInput(p);
|
||||
|
||||
txIn[i] = tx;
|
||||
txIn[i].witness = new bcoin.script.witness([]);
|
||||
txIn[i].witness = { items: [] };
|
||||
}
|
||||
|
||||
outCount = p.readVarint();
|
||||
@ -611,9 +612,23 @@ Parser.parseWitnessTX = function parseWitnessTX(p) {
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseScript = function parseScript(p) {
|
||||
var data;
|
||||
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
data = p.readVarBytes();
|
||||
|
||||
return {
|
||||
raw: data,
|
||||
code: bcoin.script.decode(data),
|
||||
_size: p.end()
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseWitness = function parseWitness(p) {
|
||||
var items = [];
|
||||
var witness, chunkCount, i;
|
||||
var chunkCount, i;
|
||||
|
||||
p = new BufferReader(p);
|
||||
p.start();
|
||||
@ -623,10 +638,10 @@ Parser.parseWitness = function parseWitness(p) {
|
||||
for (i = 0; i < chunkCount; i++)
|
||||
items.push(p.readVarBytes());
|
||||
|
||||
witness = new bcoin.script.witness(items);
|
||||
witness._size = p.end();
|
||||
|
||||
return witness;
|
||||
return {
|
||||
items: items,
|
||||
_size: p.end()
|
||||
};
|
||||
};
|
||||
|
||||
Parser.parseReject = function parseReject(p) {
|
||||
|
||||
@ -16,13 +16,19 @@ var STACK_FALSE = new Buffer([]);
|
||||
var STACK_NEGATE = new Buffer([0xff]);
|
||||
|
||||
function Witness(items) {
|
||||
if (items instanceof Witness)
|
||||
return items;
|
||||
|
||||
if (!(this instanceof Witness))
|
||||
return new Witness(items);
|
||||
|
||||
if (Buffer.isBuffer(items))
|
||||
this.items = Witness.decode(items);
|
||||
else if (items)
|
||||
this.items = items || [];
|
||||
if (!items)
|
||||
items = [];
|
||||
|
||||
if (items.items)
|
||||
items = items.items;
|
||||
|
||||
this.items = items || [];
|
||||
|
||||
this.redeem = null;
|
||||
}
|
||||
@ -31,18 +37,6 @@ Witness.prototype.inspect = function inspect() {
|
||||
return Script.format(this.items);
|
||||
};
|
||||
|
||||
Witness.prototype.encode = function encode() {
|
||||
return bcoin.protocol.framer.witness(this);
|
||||
};
|
||||
|
||||
Witness.encode = function encode(witness) {
|
||||
return bcoin.protocol.framer.witness(witness);
|
||||
};
|
||||
|
||||
Witness.decode = function decode(buf) {
|
||||
return bcoin.protocol.parser.parseWitness(buf).items;
|
||||
};
|
||||
|
||||
Witness.prototype.clone = function clone() {
|
||||
return new Witness(this.items.slice());
|
||||
};
|
||||
@ -91,19 +85,11 @@ Witness.fromString = function fromString(items) {
|
||||
|
||||
items = items.trim().split(/\s+/);
|
||||
|
||||
// Remove OP_ prefixes and lowercase
|
||||
for (i = 0; i < items.length; i++) {
|
||||
op = items[i].toLowerCase();
|
||||
|
||||
if (op.indexOf('op_') === 0)
|
||||
op = op.slice(3);
|
||||
items[i] = op;
|
||||
}
|
||||
|
||||
// Convert OP_FALSE to 0, convert OP_1-OP_16
|
||||
// to number literals, convert -1 to OP_1NEGATE.
|
||||
// Convert hex strings to arrays.
|
||||
for (i = 0; i < items.length; i++) {
|
||||
op = items[i];
|
||||
|
||||
if (op === '-1' || op === '1negate') {
|
||||
op = STACK_NEGATE;
|
||||
@ -115,13 +101,12 @@ Witness.fromString = function fromString(items) {
|
||||
op = new Buffer([+op]);
|
||||
} else {
|
||||
symbol = 'OP_' + op.toUpperCase();
|
||||
|
||||
if (opcodes[symbol] == null) {
|
||||
if (op[0] === '[')
|
||||
op = op.slice(1, -1);
|
||||
if (op.indexOf('0x') === 0)
|
||||
op = op.substring(2);
|
||||
assert(utils.isHex(op), 'Non hex-string.');
|
||||
assert(utils.isHex(op), 'Non-stack item in witness string.');
|
||||
op = new Buffer(op, 'hex');
|
||||
} else {
|
||||
assert(false, 'Non-stack item in witness string.');
|
||||
@ -445,6 +430,9 @@ Stack.isStack = function isStack(obj) {
|
||||
*/
|
||||
|
||||
function Script(code) {
|
||||
if (code instanceof Script)
|
||||
return code;
|
||||
|
||||
if (!(this instanceof Script))
|
||||
return new Script(code);
|
||||
|
||||
@ -454,9 +442,16 @@ function Script(code) {
|
||||
} else {
|
||||
if (!code)
|
||||
code = [];
|
||||
assert(Array.isArray(code));
|
||||
this.raw = null;
|
||||
this.code = code;
|
||||
if (code.code) {
|
||||
this.raw = code.raw || null;
|
||||
this.code = code.code;
|
||||
if (!this.code)
|
||||
this.code = Script.decode(this.raw);
|
||||
} else {
|
||||
assert(Array.isArray(code));
|
||||
this.raw = null;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
this.redeem = null;
|
||||
@ -2331,37 +2326,22 @@ Script.fromString = function fromString(code) {
|
||||
|
||||
code = code.trim().split(/\s+/);
|
||||
|
||||
// Remove OP_ prefixes and lowercase
|
||||
for (i = 0; i < code.length; i++) {
|
||||
op = code[i].toLowerCase();
|
||||
if (op.indexOf('op_') === 0)
|
||||
op = op.slice(3);
|
||||
code[i] = op;
|
||||
}
|
||||
|
||||
// Convert OP_FALSE to 0, convert OP_1-OP_16
|
||||
// to number literals, convert -1 to OP_1NEGATE.
|
||||
// Convert hex strings to arrays.
|
||||
for (i = 0; i < code.length; i++) {
|
||||
op = code[i];
|
||||
|
||||
if (op === '-1')
|
||||
op = '1negate';
|
||||
else if (op === '0' || op === 'false')
|
||||
op = '0';
|
||||
else if (op === 'true')
|
||||
op = '1';
|
||||
else if (+op >= 1 && +op <= 16)
|
||||
op = +op + '';
|
||||
|
||||
symbol = 'OP_' + op.toUpperCase();
|
||||
symbol = (op + '').toUpperCase();
|
||||
if (symbol.indexOf('OP_') !== 0)
|
||||
symbol = 'OP_' + op;
|
||||
|
||||
if (opcodes[symbol] == null) {
|
||||
if (op[0] === '[')
|
||||
op = op.slice(1, -1);
|
||||
if (op.indexOf('0x') === 0)
|
||||
op = op.substring(2);
|
||||
assert(utils.isHex(op), 'Non hex-string.');
|
||||
assert(utils.isHex(op), 'Unknown opcode.');
|
||||
op = new Buffer(op, 'hex');
|
||||
code[i] = op;
|
||||
continue;
|
||||
@ -2404,7 +2384,7 @@ Script.toSmall = function toSmall(op) {
|
||||
|
||||
Script.fromSymbolic = function fromSymbolic(items) {
|
||||
var code = new Array(items.length);
|
||||
var i, op, symbol;
|
||||
var i, op;
|
||||
|
||||
for (i = 0; i < items.length; i++) {
|
||||
op = items[i];
|
||||
@ -2414,24 +2394,16 @@ Script.fromSymbolic = function fromSymbolic(items) {
|
||||
continue;
|
||||
}
|
||||
|
||||
op = (op + '').toLowerCase();
|
||||
if (op.indexOf('op_') === 0)
|
||||
op = op.slice(3);
|
||||
|
||||
if (+op === -1)
|
||||
op = '1negate';
|
||||
else if (+op === 0 || op === 'false')
|
||||
op = '0';
|
||||
else if (+op === 1 || op === 'true')
|
||||
op = '1';
|
||||
else if (+op >= 1 && +op <= 16)
|
||||
op = +op + '';
|
||||
|
||||
symbol = 'OP_' + op.toUpperCase();
|
||||
op = (op + '').toUpperCase();
|
||||
if (op.indexOf('OP_') !== 0)
|
||||
op = 'OP_' + op;
|
||||
|
||||
code[i] = opcodes[symbol];
|
||||
assert(opcodes[op] != null, 'Unknown opcode.');
|
||||
|
||||
assert(code[i] != null, 'Unknown opcode.');
|
||||
code[i] = opcodes[op];
|
||||
}
|
||||
|
||||
return new Script(code);
|
||||
|
||||
@ -66,6 +66,52 @@ function TX(data, block, index) {
|
||||
}
|
||||
}
|
||||
|
||||
TX.prototype.clone = function clone() {
|
||||
var copy, i;
|
||||
|
||||
copy = {
|
||||
version: this.version,
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
locktime: this.locktime,
|
||||
flag: this.flag,
|
||||
ts: this.ts,
|
||||
block: this.block,
|
||||
index: this.index,
|
||||
height: this.height
|
||||
};
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
copy.inputs.push({
|
||||
prevout: {
|
||||
hash: this.inputs[i].prevout.hash,
|
||||
index: this.inputs[i].prevout.index
|
||||
},
|
||||
coin: null,
|
||||
script: {
|
||||
code: this.inputs[i].script.code.slice(),
|
||||
raw: this.inputs[i].script.raw
|
||||
},
|
||||
witness: {
|
||||
items: this.inputs[i].witness.items.slice()
|
||||
},
|
||||
sequence: this.inputs[i].sequence
|
||||
});
|
||||
}
|
||||
|
||||
for (i = 0; i < this.outputs.length; i++) {
|
||||
copy.outputs.push({
|
||||
value: this.outputs[i].value.clone(),
|
||||
script: {
|
||||
code: this.inputs[i].script.code.slice(),
|
||||
raw: this.inputs[i].script.raw
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return new TX(copy);
|
||||
};
|
||||
|
||||
TX.prototype.setBlock = function setBlock(block, index) {
|
||||
this.ts = block.ts;
|
||||
this.block = block.hash('hex');
|
||||
@ -252,7 +298,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) {
|
||||
for (i = 0; i < copy.outputs.length; i++) {
|
||||
if (i !== index) {
|
||||
copy.outputs[i].script = new Script([]);
|
||||
copy.outputs[i].value = new bn('ffffffffffffffff', 'hex');
|
||||
copy.outputs[i].value = utils.U64;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user