From 39aa08bb54c4310f9505fd542251c77417f2019c Mon Sep 17 00:00:00 2001 From: Vis Virial Date: Sat, 1 Aug 2015 01:01:55 +0900 Subject: [PATCH 1/4] add support for PoS coins (nTime in CTransaction). --- src/block.js | 8 ++++---- src/networks.js | 24 ++++++++++++++++++++---- src/transaction.js | 21 ++++++++++++++------- src/transaction_builder.js | 9 ++++++--- test/fixtures/transaction.json | 34 ++++++++++++++++++++++++++++++++++ test/transaction.js | 19 +++++++++++-------- 6 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/block.js b/src/block.js index dca1f4cf3..c6ab42a6f 100644 --- a/src/block.js +++ b/src/block.js @@ -13,7 +13,7 @@ function Block () { this.nonce = 0 } -Block.fromBuffer = function (buffer) { +Block.fromBuffer = function (buffer, network) { assert(buffer.length >= 80, 'Buffer too small (< 80 bytes)') var offset = 0 @@ -46,7 +46,7 @@ Block.fromBuffer = function (buffer) { // FIXME: poor performance function readTransaction () { - var tx = Transaction.fromBuffer(buffer.slice(offset), true) + var tx = Transaction.fromBuffer(buffer.slice(offset), true, network) offset += tx.toBuffer().length return tx @@ -63,8 +63,8 @@ Block.fromBuffer = function (buffer) { return block } -Block.fromHex = function (hex) { - return Block.fromBuffer(new Buffer(hex, 'hex')) +Block.fromHex = function (hex, network) { + return Block.fromBuffer(new Buffer(hex, 'hex'), network) } Block.prototype.getHash = function () { diff --git a/src/networks.js b/src/networks.js index d694bf767..8c7a7942c 100644 --- a/src/networks.js +++ b/src/networks.js @@ -12,7 +12,8 @@ module.exports = { pubKeyHash: 0x00, scriptHash: 0x05, wif: 0x80, - dustThreshold: 546 // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162 + dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162 + timeInTransaction: false }, testnet: { magic: 0xd9b4bef9, @@ -24,7 +25,8 @@ module.exports = { pubKeyHash: 0x6f, scriptHash: 0xc4, wif: 0xef, - dustThreshold: 546 + dustThreshold: 546, + timeInTransaction: false }, litecoin: { magic: 0xd9b4bef9, @@ -36,7 +38,8 @@ module.exports = { pubKeyHash: 0x30, scriptHash: 0x05, wif: 0xb0, - dustThreshold: 0 // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365 + dustThreshold: 0, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365 + timeInTransaction: false }, dogecoin: { messagePrefix: '\x19Dogecoin Signed Message:\n', @@ -47,6 +50,19 @@ module.exports = { pubKeyHash: 0x1e, scriptHash: 0x16, wif: 0x9e, - dustThreshold: 0 // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160 + dustThreshold: 0, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160 + timeInTransaction: false + }, + peercoin: { + messagePrefix: '\x17PPcoin Signed Message:\n', + bip32: { + public: 0x01da950b, // start with "Ppub..." + private: 0x01da90d0 // start with "Pprv..." + }, + pubKeyHash: 0x37, + scriptHash: 0x75, + wif: 0xb7, + dustThreshold: 0, + timeInTransaction: true } } diff --git a/src/transaction.js b/src/transaction.js index 63705fa8f..de2e17652 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -3,11 +3,14 @@ var bufferutils = require('./bufferutils') var crypto = require('./crypto') var typeForce = require('typeforce') var opcodes = require('./opcodes') +var networks = require('./networks') var Script = require('./script') -function Transaction () { +function Transaction (network) { + this.network = network || networks.bitcoin this.version = 1 + this.time = Math.floor(new Date().getTime() / 1000) this.locktime = 0 this.ins = [] this.outs = [] @@ -19,7 +22,8 @@ Transaction.SIGHASH_NONE = 0x02 Transaction.SIGHASH_SINGLE = 0x03 Transaction.SIGHASH_ANYONECANPAY = 0x80 -Transaction.fromBuffer = function (buffer, __disableAssert) { +Transaction.fromBuffer = function (buffer, __disableAssert, network) { + network = network || networks.bitcoin var offset = 0 function readSlice (n) { offset += n @@ -52,8 +56,9 @@ Transaction.fromBuffer = function (buffer, __disableAssert) { return new Script(readSlice(readVarInt()), []) } - var tx = new Transaction() + var tx = new Transaction(network) tx.version = readUInt32() + if (network.timeInTransaction) tx.time = readUInt32() var vinLen = readVarInt() for (var i = 0; i < vinLen; ++i) { @@ -93,8 +98,8 @@ Transaction.fromBuffer = function (buffer, __disableAssert) { return tx } -Transaction.fromHex = function (hex) { - return Transaction.fromBuffer(new Buffer(hex, 'hex')) +Transaction.fromHex = function (hex, network) { + return Transaction.fromBuffer(new Buffer(hex, 'hex'), null, network) } Transaction.isCoinbaseHash = function (buffer) { @@ -145,7 +150,7 @@ Transaction.prototype.byteLength = function () { } return ( - 8 + + (this.network.timeInTransaction ? 12 : 8) + bufferutils.varIntSize(this.ins.length) + bufferutils.varIntSize(this.outs.length) + this.ins.reduce(function (sum, input) { return sum + 40 + scriptSize(input.script) }, 0) + @@ -154,8 +159,9 @@ Transaction.prototype.byteLength = function () { } Transaction.prototype.clone = function () { - var newTx = new Transaction() + var newTx = new Transaction(this.network) newTx.version = this.version + newTx.time = this.time newTx.locktime = this.locktime newTx.ins = this.ins.map(function (txIn) { @@ -295,6 +301,7 @@ Transaction.prototype.toBuffer = function () { } writeUInt32(this.version) + if (this.network.timeInTransaction) writeUInt32(this.time) writeVarInt(this.ins.length) this.ins.forEach(function (txIn) { diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 186c93db9..c20d6ca49 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -3,6 +3,7 @@ var bcrypto = require('./crypto') var bufferutils = require('./bufferutils') var ops = require('./opcodes') var scripts = require('./scripts') +var networks = require('./networks') var Address = require('./address') var ECPair = require('./ecpair') @@ -83,20 +84,22 @@ function extractInput (txIn) { } } -function TransactionBuilder () { +function TransactionBuilder (network) { + this.network = network || networks.bitcoin this.prevTxMap = {} this.prevOutScripts = {} this.prevOutTypes = {} this.inputs = [] - this.tx = new Transaction() + this.tx = new Transaction(network) } TransactionBuilder.fromTransaction = function (transaction) { - var txb = new TransactionBuilder() + var txb = new TransactionBuilder(transaction.network) // Copy other transaction fields txb.tx.version = transaction.version + txb.tx.time = transaction.time txb.tx.locktime = transaction.locktime // Extract/add inputs diff --git a/test/fixtures/transaction.json b/test/fixtures/transaction.json index 97108181c..4450e5d4d 100644 --- a/test/fixtures/transaction.json +++ b/test/fixtures/transaction.json @@ -2,6 +2,7 @@ "valid": [ { "description": "Standard transaction (1:1)", + "network": "bitcoin", "id": "a0ff943d3f644d8832b1fa74be4d0ad2577615dc28a7ef74ff8c271b603a082a", "hash": "2a083a601b278cff74efa728dc157657d20a4dbe74fab132884d643f3d94ffa0", "raw": { @@ -25,6 +26,7 @@ }, { "description": "Standard transaction (2:2)", + "network": "bitcoin", "id": "fcdd6d89c43e76dcff94285d9b6e31d5c60cb5e397a76ebc4920befad30907bc", "hash": "bc0709d3fabe2049bc6ea797e3b50cc6d5316e9b5d2894ffdc763ec4896dddfc", "raw": { @@ -59,6 +61,7 @@ }, { "description": "Standard transaction (14:2)", + "network": "bitcoin", "id": "39d57bc27f72e904d81f6b5ef7b4e6e17fa33a06b11e5114a43435830d7b5563", "hash": "63557b0d833534a414511eb1063aa37fe1e6b4f75e6b1fd804e9727fc27bd539", "raw": { @@ -152,6 +155,7 @@ }, { "description": "Coinbase transaction", + "network": "bitcoin", "id": "8e070d4eb85eb02e02dd938d6552316b9d723330707870c518064b7a0d232da3", "hash": "a32d230d7a4b0618c57078703033729d6b3152658d93dd022eb05eb84e0d078e", "hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff29032832051c4d696e656420627920416e74506f6f6c20626a343a45ef0454c5de8d5e5300004e2c0000ffffffff01414f1995000000001976a914b05793fe86a9f51a5f5ae3a6f07fd31932128a3f88ac00000000", @@ -176,6 +180,7 @@ }, { "description": "Transaction that ignores script chunking rules - Bug #367", + "network": "bitcoin", "id": "ebc9fa1196a59e192352d76c0f6e73167046b9d37b8302b6bb6968dfd279b767", "hash": "67b779d2df6869bbb602837bd3b9467016736e0f6cd75223199ea59611fac9eb", "hex": "01000000019ac03d5ae6a875d970128ef9086cef276a1919684a6988023cc7254691d97e6d010000006b4830450221009d41dc793ba24e65f571473d40b299b6459087cea1509f0d381740b1ac863cb6022039c425906fcaf51b2b84d8092569fb3213de43abaff2180e2a799d4fcb4dd0aa012102d5ede09a8ae667d0f855ef90325e27f6ce35bbe60a1e6e87af7f5b3c652140fdffffffff080100000000000000010101000000000000000202010100000000000000014c0100000000000000034c02010100000000000000014d0100000000000000044dffff010100000000000000014e0100000000000000064effffffff0100000000", @@ -224,6 +229,35 @@ } ] } + }, + { + "description": "PoS-type transaction", + "network": "peercoin", + "id": "38111c55598e25d907bfe068387a16e534994d60f51e5e94f476e461737bf8cc", + "hash": "ccf87b7361e476f4945e1ef5604d9934e5167a3868e0bf07d9258e59551c1138", + "raw": { + "version": 1, + "time": 1438355334, + "ins": [ + { + "hash": "b74cf38a325cd254272e6d2765968c32e131259b50df543ce11024ae8f2b9ea8", + "index": 1, + "script": "3045022037d3ff9e09d5fdc0bf7616347304e1b6054d41af61e05b35d76b7666e382072c022100873e394549dc81ce35621cef9b4163860de2a3d3da573aa09f07d193fd1f3c5a01 03c2344a43c43e8dcaeae6493ee1efd05e9600fc0fdc2128a6802bb6e1bf1ab5c6" + } + ], + "outs": [ + { + "script": "OP_DUP OP_HASH160 dd4b6cf230446589a3844faf08687993f3a3e545 OP_EQUALVERIFY OP_CHECKSIG", + "value": 809869 + }, + { + "script": "OP_DUP OP_HASH160 927e84c9f01066713223c59c9ad25986130b8a06 OP_EQUALVERIFY OP_CHECKSIG", + "value": 9970000 + } + ], + "locktime": 0 + }, + "hex": "01000000868fbb5501b74cf38a325cd254272e6d2765968c32e131259b50df543ce11024ae8f2b9ea8010000006b483045022037d3ff9e09d5fdc0bf7616347304e1b6054d41af61e05b35d76b7666e382072c022100873e394549dc81ce35621cef9b4163860de2a3d3da573aa09f07d193fd1f3c5a012103c2344a43c43e8dcaeae6493ee1efd05e9600fc0fdc2128a6802bb6e1bf1ab5c6ffffffff028d5b0c00000000001976a914dd4b6cf230446589a3844faf08687993f3a3e54588ac50219800000000001976a914927e84c9f01066713223c59c9ad25986130b8a0688ac00000000" } ], "invalid": { diff --git a/test/transaction.js b/test/transaction.js index 0cd376b3a..aabeedf5e 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -4,13 +4,16 @@ var assert = require('assert') var Transaction = require('../src/transaction') var Script = require('../src/script') +var networks = require('../src/networks') var fixtures = require('./fixtures/transaction') describe('Transaction', function () { - function fromRaw (raw) { - var tx = new Transaction() + function fromRaw (f) { + var raw = f.raw + var tx = new Transaction(networks[f.network]) tx.version = raw.version + tx.time = raw.time tx.locktime = raw.locktime raw.ins.forEach(function (txIn) { @@ -46,7 +49,7 @@ describe('Transaction', function () { describe('fromBuffer/fromHex', function () { fixtures.valid.forEach(function (f) { it('imports ' + f.description + ' (' + f.id + ')', function () { - var actual = Transaction.fromHex(f.hex) + var actual = Transaction.fromHex(f.hex, networks[f.network]) assert.strictEqual(actual.toHex(), f.hex, actual.toHex()) }) @@ -55,7 +58,7 @@ describe('Transaction', function () { fixtures.invalid.fromBuffer.forEach(function (f) { it('throws on ' + f.exception, function () { assert.throws(function () { - Transaction.fromHex(f.hex) + Transaction.fromHex(f.hex, networks[f.network]) }, new RegExp(f.exception)) }) }) @@ -64,7 +67,7 @@ describe('Transaction', function () { describe('toBuffer/toHex', function () { fixtures.valid.forEach(function (f) { it('exports ' + f.description + ' (' + f.id + ')', function () { - var actual = fromRaw(f.raw) + var actual = fromRaw(f) assert.strictEqual(actual.toHex(), f.hex, actual.toHex()) }) @@ -130,7 +133,7 @@ describe('Transaction', function () { var actual, expected beforeEach(function () { - expected = Transaction.fromHex(f.hex) + expected = Transaction.fromHex(f.hex, networks[f.network]) actual = expected.clone() }) @@ -147,7 +150,7 @@ describe('Transaction', function () { describe('getId', function () { fixtures.valid.forEach(function (f) { it('should return the id for ' + f.id, function () { - var tx = Transaction.fromHex(f.hex) + var tx = Transaction.fromHex(f.hex, networks[f.network]) assert.strictEqual(tx.getId(), f.id) }) @@ -157,7 +160,7 @@ describe('Transaction', function () { describe('getHash', function () { fixtures.valid.forEach(function (f) { it('should return the hash for ' + f.id, function () { - var tx = Transaction.fromHex(f.hex) + var tx = Transaction.fromHex(f.hex, networks[f.network]) assert.strictEqual(tx.getHash().toString('hex'), f.hash) }) From 8be556f83de49dd68d9f2a02204327e2e4bf2fb7 Mon Sep 17 00:00:00 2001 From: Vis Virial Date: Sat, 1 Aug 2015 02:00:08 +0900 Subject: [PATCH 2/4] add test for TransactionBuilder for PoS coins. --- test/fixtures/transaction_builder.json | 47 ++++++++++++++++++++++++++ test/transaction_builder.js | 11 ++++-- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 9fd03f3a1..e87801b64 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -3,6 +3,7 @@ "build": [ { "description": "Transaction w/ pubKeyHash -> pubKeyHash", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b483045022100a3b254e1c10b5d039f36c05f323995d6e5a367d98dd78a13d5bbc3991b35720e022022fccea3897d594de0689601fbd486588d5bfa6915be2386db0397ee9a6e80b601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", "inputs": [ { @@ -22,8 +23,32 @@ } ] }, + { + "description": "Transaction w/ pubKeyHash -> pubKeyHash (PoS)", + "network": "peercoin", + "txHex": "0100000080a7bb55014887981e33e8de0a6e8a9bac7ab70be474c3e90d964ba6d3e1293299388889d0010000006a47304402201577aa1a4c9e80543ccf7861a4977e65f94513482b5e9852bd77abee1f5d79690220332bef41d699f18e31d6092dbd5ba7c5c6d82007ff95ef90e8da43d51fa5d9420121034da3d58ac4258b96c89dfebfb00bfe2bdb058d5b035a9a313c9c9ae3a31a9b36ffffffff0140420f00000000001976a91480220acacb7c10f1b1ce3cbb31e7ce09e9f598f288ac00000000", + "time": 1438361472, + "inputs": [ + { + "txId": "d0898838993229e1d3a64b960de9c374e40bb77aac9b8a6e0adee8331e988748", + "vout": 1, + "signs": [ + { + "keyPair": "U9uSe8JE7ZXbwwvjDEMwmYidw1WLhedmSoxczEkquo1WQXYE8eiR" + } + ] + } + ], + "outputs": [ + { + "script": "OP_DUP OP_HASH160 80220acacb7c10f1b1ce3cbb31e7ce09e9f598f2 OP_EQUALVERIFY OP_CHECKSIG", + "value": 1000000 + } + ] + }, { "description": "Transaction w/ pubKey -> pubKeyHash", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000494830450221009833abb3ab49d7004c06bcc79eafd6905ada3eee91f3376ad388548034acd9a702202e84dda6ef2678c82256afcfc459aaa68e179b2bb0e6b2dc3f1410e132c5e6c301ffffffff0100f90295000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", "inputs": [ { @@ -46,6 +71,7 @@ }, { "description": "Transaction w/ scriptHash(pubKeyHash) -> pubKeyHash", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000085483045022100a3b254e1c10b5d039f36c05f323995d6e5a367d98dd78a13d5bbc3991b35720e022022fccea3897d594de0689601fbd486588d5bfa6915be2386db0397ee9a6e80b601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817981976a914751e76e8199196d454941c45d1b3a323f1433bd688acffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", "inputs": [ { @@ -68,6 +94,7 @@ }, { "description": "Transaction w/ scriptHash(multisig 2-of-2) -> pubKeyHash", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd1b0100483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a4014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", "inputs": [ { @@ -93,6 +120,7 @@ }, { "description": "Transaction w/ multisig 2-of-2 -> pubKeyHash", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009200483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a401ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", "inputs": [ { @@ -118,6 +146,7 @@ }, { "description": "Transaction w/ multisig 2-of-2 (reverse order) -> pubKeyHash", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009200483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a401ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", "inputs": [ { @@ -143,6 +172,7 @@ }, { "description": "Transaction w/ scriptHash(multisig 2-of-3)", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd5e0100483045022100eec19e061cad41610f9b42d2b06638b6b0fec3da0de9c6858e7f8c06053979900220622936dd47e202b2ad17639cda680e52334d407149252959936bb1f38e4acc52014830450221009aac215157a74a18234fd06be27448dccee809986bbf93be457a9262f0c69a9402203ff41d7c757f0e8951e4471f205087ecff499f986400ab18210eaad9a628e33c014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", "inputs": [ { @@ -168,6 +198,7 @@ }, { "description": "Transaction w/ scriptHash(pubKey) -> pubKeyHash", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006c47304402201115644b134932c8a7a8e925769d130a801288d477130e2bf6fadda20b33754d02202ecefbf63844d7cb2d5868539c39f973fe019f72e5c31a707836c0d61ef317db012321033e29aea1168a835d5e386c292082db7b7807172a10ec634ad34226f36d79e70facffffffff0100f90295000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", "inputs": [ { @@ -191,6 +222,7 @@ }, { "description": "Transaction w/ non-zero vin inputs", + "network": "bitcoin", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205c80bbb5125b35d5e5a8324b1336832d29a6fc004859c8a9ff6bef47ba7fc348022018612216e57a521b2c4543f1f4fd738a76814c37c074e88adfe12464fff31cf901210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", "inputs": [ { @@ -212,6 +244,7 @@ }, { "description": "Transaction w/ non-default input sequence numbers, version and locktime", + "network": "bitcoin", "txHex": "0400000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000006b483045022100c5bcd521df085481e2dcc2c0f14173043f0fa2001dca582b45186a95d248d28002204c571eabcec1410bd53a7da29b9da6b4c858c3fdabbfdb110a030c507ff5bc0501210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b9c220000110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac09990400", "version": 4, "locktime": 301321, @@ -238,6 +271,7 @@ "multisig": [ { "description": "P2SH 2of2 multisig, signed in correct order", + "network": "bitcoin", "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -267,6 +301,7 @@ }, { "description": "P2SH 2of2 multisig, signed in shuffled order", + "network": "bitcoin", "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -296,6 +331,7 @@ }, { "description": "P2SH 2of2 multisig, manually messed up order of signatures", + "network": "bitcoin", "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -325,6 +361,7 @@ }, { "description": "P2SH 2of3 multisig, signed by key 1 and 2", + "network": "bitcoin", "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -354,6 +391,7 @@ }, { "description": "P2SH 2of3 multisig, signed by key 1 and 3", + "network": "bitcoin", "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -383,6 +421,7 @@ }, { "description": "P2SH 2of3 multisig, signed by key 3 and 1", + "network": "bitcoin", "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -412,6 +451,7 @@ }, { "description": "P2SH 2of3 multisig, signed by key 1 and 3, manually messed up order of signatures", + "network": "bitcoin", "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -441,6 +481,7 @@ }, { "description": "P2SH 2of3 multisig, signed by key 3 and 1, manually removing OP_0s", + "network": "bitcoin", "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -497,6 +538,7 @@ }, { "description": "Incomplete transaction, nothing assumed", + "network": "bitcoin", "exception": "Transaction is not complete", "inputs": [ { @@ -514,6 +556,7 @@ }, { "description": "Incomplete transaction w/ prevTxScript defined", + "network": "bitcoin", "exception": "Transaction is missing signatures", "alwaysThrows": true, "inputs": [ @@ -542,6 +585,7 @@ }, { "description": "Complete transaction w/ non-standard inputs", + "network": "bitcoin", "exception": "nonstandard not supported", "txHex": "010000000100000000171a0000e028f2000000000050178500000000000d0000000e000000000000002009f691b2263260e71f363d1db51ff3100d285956a40cc0e4f8c8c2c4a80559b1ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" } @@ -549,6 +593,7 @@ "sign": [ { "description": "Too many signatures - pubKeyHash", + "network": "bitcoin", "exception": "Signature already exists", "inputs": [ { @@ -716,6 +761,7 @@ }, { "description": "Too many signatures - scriptHash(multisig 1-of-1)", + "network": "bitcoin", "exception": "Signature already exists", "inputs": [ { @@ -742,6 +788,7 @@ }, { "description": "Wrong key pair for multisig redeemScript", + "network": "bitcoin", "exception": "key pair cannot sign for this input", "inputs": [ { diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 09dcb9a08..a3554411e 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -3,6 +3,7 @@ var assert = require('assert') var ops = require('../src/opcodes') var scripts = require('../src/scripts') +var networks = require('../src/networks') var Address = require('../src/address') var BigInteger = require('bigi') @@ -14,6 +15,8 @@ var TransactionBuilder = require('../src/transaction_builder') var fixtures = require('./fixtures/transaction_builder') function construct (txb, f, sign) { + txb.tx.time = f.time + f.inputs.forEach(function (input) { var prevTxScript @@ -156,6 +159,7 @@ describe('TransactionBuilder', function () { describe('sign', function () { fixtures.invalid.sign.forEach(function (f) { it('throws on ' + f.exception + ' (' + f.description + ')', function () { + var txb = new TransactionBuilder(networks[f.network]) construct(txb, f, false) f.inputs.forEach(function (input, index) { @@ -184,6 +188,7 @@ describe('TransactionBuilder', function () { describe('build', function () { fixtures.valid.build.forEach(function (f) { it('builds "' + f.description + '"', function () { + var txb = new TransactionBuilder(networks[f.network]) construct(txb, f) var tx = txb.build() @@ -195,7 +200,7 @@ describe('TransactionBuilder', function () { describe('for ' + (f.description || f.exception), function () { beforeEach(function () { if (f.txHex) { - var tx = Transaction.fromHex(f.txHex) + var tx = Transaction.fromHex(f.txHex, networks[f.network]) txb = TransactionBuilder.fromTransaction(tx) } else { construct(txb, f) @@ -282,7 +287,7 @@ describe('TransactionBuilder', function () { describe('fromTransaction', function () { fixtures.valid.build.forEach(function (f) { it('builds the correct TransactionBuilder for ' + f.description, function () { - var tx = Transaction.fromHex(f.txHex) + var tx = Transaction.fromHex(f.txHex, networks[f.network]) var txb = TransactionBuilder.fromTransaction(tx) assert.strictEqual(txb.build().toHex(), f.txHex) @@ -291,7 +296,7 @@ describe('TransactionBuilder', function () { fixtures.invalid.fromTransaction.forEach(function (f) { it('throws on ' + f.exception, function () { - var tx = Transaction.fromHex(f.txHex) + var tx = Transaction.fromHex(f.txHex, networks[f.network]) assert.throws(function () { TransactionBuilder.fromTransaction(tx) From ba4ea211cc8c87f716c47cf00785c3841c41d63b Mon Sep 17 00:00:00 2001 From: Vis Virial Date: Sat, 1 Aug 2015 12:08:08 +0900 Subject: [PATCH 3/4] added block read/write support for PoS coins. --- src/block.js | 17 ++++++++++++++++- test/block.js | 13 +++++++------ test/fixtures/block.json | 17 +++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/block.js b/src/block.js index c6ab42a6f..0b4adc205 100644 --- a/src/block.js +++ b/src/block.js @@ -60,6 +60,13 @@ Block.fromBuffer = function (buffer, network) { block.transactions.push(tx) } + // Read block signature (vchBlockSig) for PoS coins. + block.blockSig = null + if (offset < buffer.length) { + var blockSigSize = readVarInt() + block.blockSig = readSlice(blockSigSize) + } + return block } @@ -110,7 +117,15 @@ Block.prototype.toBuffer = function (headersOnly) { return tx.toBuffer() }) - return Buffer.concat([buffer, txLenBuffer].concat(txBuffers)) + var ret = Buffer.concat([buffer, txLenBuffer].concat(txBuffers)) + + // Block Signature. + if (this.blockSig) { + var blockSigLenBuffer = bufferutils.varIntBuffer(this.blockSig.length) + ret = Buffer.concat([ret, blockSigLenBuffer, this.blockSig]) + } + + return ret } Block.prototype.toHex = function (headersOnly) { diff --git a/test/block.js b/test/block.js index 8c91b138d..909c32848 100644 --- a/test/block.js +++ b/test/block.js @@ -2,6 +2,7 @@ var assert = require('assert') +var networks = require('../src/networks') var Block = require('../src/block') var fixtures = require('./fixtures/block') @@ -10,7 +11,7 @@ describe('Block', function () { describe('fromBuffer/fromHex', function () { fixtures.valid.forEach(function (f) { it('imports the block: ' + f.description + ' correctly', function () { - var block = Block.fromHex(f.hex) + var block = Block.fromHex(f.hex, networks[f.network]) assert.strictEqual(block.version, f.version) assert.strictEqual(block.prevHash.toString('hex'), f.prevHash) @@ -24,7 +25,7 @@ describe('Block', function () { fixtures.invalid.forEach(function (f) { it('throws on ' + f.exception, function () { assert.throws(function () { - Block.fromHex(f.hex) + Block.fromHex(f.hex, networks[f.network]) }, new RegExp(f.exception)) }) }) @@ -35,7 +36,7 @@ describe('Block', function () { var block beforeEach(function () { - block = Block.fromHex(f.hex) + block = Block.fromHex(f.hex, networks[f.network]) }) it('exports the block: ' + f.description + ' correctly', function () { @@ -49,7 +50,7 @@ describe('Block', function () { var block beforeEach(function () { - block = Block.fromHex(f.hex) + block = Block.fromHex(f.hex, networks[f.network]) }) it('calculates ' + f.hash + ' for the block: ' + f.description, function () { @@ -63,7 +64,7 @@ describe('Block', function () { var block beforeEach(function () { - block = Block.fromHex(f.hex) + block = Block.fromHex(f.hex, networks[f.network]) }) it('calculates ' + f.id + ' for the block: ' + f.description, function () { @@ -77,7 +78,7 @@ describe('Block', function () { var block beforeEach(function () { - block = Block.fromHex(f.hex) + block = Block.fromHex(f.hex, networks[f.network]) }) it('returns UTC date of ' + f.id, function () { diff --git a/test/fixtures/block.json b/test/fixtures/block.json index 147a7ca2d..14b49295b 100644 --- a/test/fixtures/block.json +++ b/test/fixtures/block.json @@ -2,6 +2,7 @@ "valid": [ { "description": "Coinbase only - Headers only", + "network": "bitcoin", "hash": "55388f8f9b326bd0b8e50fbe44c1903d4be14febcfad4dffa50c846c00000000", "id": "000000006c840ca5ff4dadcfeb4fe14b3d90c144be0fe5b8d06b329b8f8f3855", "version": 2, @@ -14,6 +15,7 @@ }, { "description": "Coinbase only", + "network": "bitcoin", "hash": "55388f8f9b326bd0b8e50fbe44c1903d4be14febcfad4dffa50c846c00000000", "id": "000000006c840ca5ff4dadcfeb4fe14b3d90c144be0fe5b8d06b329b8f8f3855", "version": 2, @@ -24,8 +26,22 @@ "nonce": 3760981266, "hex": "020000003385c4b2a3499669987f5d04fa4127b59dbf2ee625694fa0bf08000000000000cf52f0ed6571367818a801a169e64030d8cab1a9f17e27170a6924127e19dbb8eba43e54ffff001d12052ce00101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2403089904174b6e434d696e65724251521defe5cdcf04ad543ea4eb0101000000165e0000ffffffff0100f90295000000001976a9149e8985f82bc4e0f753d0492aa8d11cc39925774088ac00000000" }, + { + "description": "Coinbase only (PoS-type; Peercoin #1)", + "network": "peercoin", + "hash": "586629ff7567a5fbc5409eec427f7610755c51ba7150af24e0e40b0000000000", + "id": "00000000000be4e024af5071ba515c7510767f42ec9e40c5fba56775ff296658", + "version": 1, + "prevHash": "e327cd80c8b17efda4ea08c5877e95d877462ab66349d5667167fe3200000000", + "merkleRoot": "68f623d206a1bde3b0382e97af9c0b6bfa708ca4c25f339953e134ff4ee8da1b", + "timestamp": 1345400356, + "bits": 469827583, + "nonce": 1915373966, + "hex": "01000000e327cd80c8b17efda4ea08c5877e95d877462ab66349d5667167fe320000000068f623d206a1bde3b0382e97af9c0b6bfa708ca4c25f339953e134ff4ee8da1b242e3150ffff001c8e492a720101000000742c3150010000000000000000000000000000000000000000000000000000000000000000ffffffff0f04242e3150021a02062f503253482fffffffff017028ff9400000000232102e5d9735f12cc4adfce708445c9e109dc945a135377bddf6a6f856757c88eecb3ac0000000047304502210091c860f869ade3c1536a94c7f9e51ae72aa04380f6d22198320d733a5bea6ac5022035e4daaff5b11f4dbd93a02cc79d207cbe33199c1c05fde9df8822d92e5c20c1" + }, { "description": "Low number of transactions", + "network": "bitcoin", "hash": "f0ca57cf84cc953194cd87de4bd9142720a056dc6f27484767f3e85e00000000", "id": "000000005ee8f3674748276fdc56a0202714d94bde87cd943195cc84cf57caf0", "version": 2, @@ -38,6 +54,7 @@ }, { "description": "Medium number of transactions", + "network": "bitcoin", "hash": "0cccf0b884a20113ea2c53a381dacc92a68ae9db1cf86525eb259f0c00000000", "id": "000000000c9f25eb2565f81cdbe98aa692ccda81a3532cea1301a284b8f0cc0c", "version": 2, From 5a4fd887f4bcca34592e51fab37ced450bf27e8e Mon Sep 17 00:00:00 2001 From: Vis Virial Date: Sat, 1 Aug 2015 12:11:21 +0900 Subject: [PATCH 4/4] change network option (timeInTransaction -> isPoS). --- src/networks.js | 10 +++++----- src/transaction.js | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/networks.js b/src/networks.js index 8c7a7942c..f78df8fba 100644 --- a/src/networks.js +++ b/src/networks.js @@ -13,7 +13,7 @@ module.exports = { scriptHash: 0x05, wif: 0x80, dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162 - timeInTransaction: false + isPoS: false }, testnet: { magic: 0xd9b4bef9, @@ -26,7 +26,7 @@ module.exports = { scriptHash: 0xc4, wif: 0xef, dustThreshold: 546, - timeInTransaction: false + isPoS: false }, litecoin: { magic: 0xd9b4bef9, @@ -39,7 +39,7 @@ module.exports = { scriptHash: 0x05, wif: 0xb0, dustThreshold: 0, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365 - timeInTransaction: false + isPoS: false }, dogecoin: { messagePrefix: '\x19Dogecoin Signed Message:\n', @@ -51,7 +51,7 @@ module.exports = { scriptHash: 0x16, wif: 0x9e, dustThreshold: 0, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160 - timeInTransaction: false + isPoS: false }, peercoin: { messagePrefix: '\x17PPcoin Signed Message:\n', @@ -63,6 +63,6 @@ module.exports = { scriptHash: 0x75, wif: 0xb7, dustThreshold: 0, - timeInTransaction: true + isPoS: true } } diff --git a/src/transaction.js b/src/transaction.js index de2e17652..b1b5fdfb6 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -58,7 +58,7 @@ Transaction.fromBuffer = function (buffer, __disableAssert, network) { var tx = new Transaction(network) tx.version = readUInt32() - if (network.timeInTransaction) tx.time = readUInt32() + if (network.isPoS) tx.time = readUInt32() var vinLen = readVarInt() for (var i = 0; i < vinLen; ++i) { @@ -150,7 +150,7 @@ Transaction.prototype.byteLength = function () { } return ( - (this.network.timeInTransaction ? 12 : 8) + + (this.network.isPoS ? 12 : 8) + bufferutils.varIntSize(this.ins.length) + bufferutils.varIntSize(this.outs.length) + this.ins.reduce(function (sum, input) { return sum + 40 + scriptSize(input.script) }, 0) + @@ -301,7 +301,7 @@ Transaction.prototype.toBuffer = function () { } writeUInt32(this.version) - if (this.network.timeInTransaction) writeUInt32(this.time) + if (this.network.isPoS) writeUInt32(this.time) writeVarInt(this.ins.length) this.ins.forEach(function (txIn) {