From 098989782b86bb7cb57d8cb9b2066a44aaeffcd0 Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Fri, 15 Sep 2017 16:56:38 +0200 Subject: [PATCH 01/15] First transactionBytes refactor, adding testsuite --- src/transactions/transactionBytes.js | 73 +++---- test/transactions/transactionBytes.js | 261 ++++++++++++++++++++++---- 2 files changed, 255 insertions(+), 79 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 09ef8627f..653afd535 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -16,6 +16,20 @@ import ByteBuffer from 'bytebuffer'; import bignum from 'browserify-bignum'; +const typeSizes = { + TRANSACTION_TYPE: 1, + TIMESTAMP: 4, + MULTISIGNATURE_PUBLICKEY: 32, + RECIPIENT_ID: 8, + AMOUNT: 8, + SIGNATURE_TRANSACTION: 64, + SECOND_SIGNATURE_TRANSACTION: 64, + DATA: 64, +}; + +const sumTypeSizes = Object.values(typeSizes) + .reduce((sum, typeSize) => sum + typeSize, 0); + /** * @method getEmptyBytesForTransaction * @param transaction Object @@ -41,20 +55,10 @@ function getEmptyBytesForTransaction(transaction) { */ function isSignatureTransaction() { - const bb = new ByteBuffer(32, true); - const publicKey = transaction.asset.signature.publicKey; - const publicKeyBuffer = Buffer.from(publicKey, 'hex'); - - for (let i = 0; i < publicKeyBuffer.length; i++) { - bb.writeByte(publicKeyBuffer[i]); - } - - bb.flip(); - const signatureBytes = new Uint8Array(bb.toArrayBuffer()); - + const transactionAssetBuffer = Buffer.from(transaction.asset.signature.publicKey, 'hex'); return { - assetBytes: signatureBytes, - assetSize: 32, + assetBytes: transactionAssetBuffer, + assetSize: transactionAssetBuffer.length, }; } @@ -64,9 +68,10 @@ function getEmptyBytesForTransaction(transaction) { */ function isDelegateTransaction() { + const transactionAssetBuffer = Buffer.from(transaction.asset.delegate.username, 'utf8'); return { - assetBytes: Buffer.from(transaction.asset.delegate.username), - assetSize: Buffer.from(transaction.asset.delegate.username).length, + assetBytes: transactionAssetBuffer, + assetSize: transactionAssetBuffer.length, }; } @@ -76,11 +81,11 @@ function getEmptyBytesForTransaction(transaction) { */ function isVoteTransaction() { - const voteTransactionBytes = (Buffer.from(transaction.asset.votes.join('')) || null); + const transactionAssetBuffer = Buffer.from(transaction.asset.votes.join('')); return { - assetBytes: voteTransactionBytes, - assetSize: (voteTransactionBytes.length || 0), + assetBytes: transactionAssetBuffer, + assetSize: transactionAssetBuffer.length, }; } @@ -157,11 +162,11 @@ function getEmptyBytesForTransaction(transaction) { */ function isDappInTransferTransaction() { - const buf = Buffer.from(transaction.asset.inTransfer.dappId); + const transactionAssetBuffer = Buffer.from(transaction.asset.inTransfer.dappId); return { - assetBytes: buf, - assetSize: buf.length, + assetBytes: transactionAssetBuffer, + assetSize: transactionAssetBuffer.length, }; } @@ -217,30 +222,6 @@ function createTransactionBuffer(transaction) { return partTransactionBuffer; } - /** - * @method createEmptyTransactionBuffer - * @param assetSize number - * @return {buffer} - */ - - function createEmptyTransactionBuffer(assetSize) { - const typeSizes = { - TRANSACTION_TYPE: 1, - TIMESTAMP: 4, - MULTISIGNATURE_PUBLICKEY: 32, - RECIPIENT_ID: 8, - AMOUNT: 8, - SIGNATURE_TRANSACTION: 64, - SECOND_SIGNATURE_TRANSACTION: 64, - DATA: 64, - }; - - const totalBytes = Object.values(typeSizes) - .reduce((sum, typeSize) => sum + typeSize, 0); - - return new ByteBuffer(totalBytes + assetSize, true); - } - /** * @method assignTransactionBuffer * @param transactionBuffer buffer @@ -310,7 +291,7 @@ function createTransactionBuffer(transaction) { const assetSize = transactionAssetSizeBuffer.assetSize; const assetBytes = transactionAssetSizeBuffer.assetBytes; - const emptyTransactionBuffer = createEmptyTransactionBuffer(assetSize); + const emptyTransactionBuffer = new ByteBuffer(sumTypeSizes + assetSize, true); const assignedTransactionBuffer = assignTransactionBuffer( emptyTransactionBuffer, assetSize, assetBytes, ); diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index 542b0d39e..7fb8d8487 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -13,52 +13,247 @@ * */ import { getTransactionBytes } from '../../src/transactions/transactionBytes'; +import { createOutTransfer } from '../../src/transactions/transfer'; +import cryptoModule from '../../src/crypto'; +import ByteBuffer from 'bytebuffer'; describe('#getTransactionBytes', () => { - let bytes = null; + const defaultSecret = 'secret'; + const defaultSecondSecret = 'second secret'; + const defaultRecipient = '58191285901858109L'; + const defaultSenderPublicKey = '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09'; + const defaultSenderSecondPublicKey = '0401c8ac9f29ded9e1e4d5b6b43051cb25b22f27c7b7b35092161e851946f82f' + const defaultAmount = 1000; + const defaultNoAmount = 0; + const defaultTimestamp = 141738; + const defaultAsset = {}; + const defaultTransactionId = '13987348420913138422'; + const defaultSignature = '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a'; + const defaultSecondSignature = 'b00c4ad1988bca245d74435660a278bfe6bf2f5efa8bda96d927fabf8b4f6fcfdcb2953f6abacaa119d6880987a55dea0e6354bc8366052b45fa23145522020f'; + const defaultAppId = '1234213'; - it('should be ok', () => { - (getTransactionBytes).should.be.ok(); + describe('send transaction, type 0', () => { + let defaultTransaction; + + beforeEach(() => { + defaultTransaction = { + type: 0, + amount: defaultAmount, + recipientId: defaultRecipient, + timestamp: defaultTimestamp, + asset: defaultAsset, + senderPublicKey: defaultSenderPublicKey, + signature: defaultSignature, + id: defaultTransactionId, + }; + }); + + it('should return Buffer of type 0 (send LSk) transaction and buffer most be 117 length', () => { + const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAABhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); + const transactionBytes = getTransactionBytes(defaultTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + (transactionBytes.length).should.be.equal(117); + }); + + it('should return Buffer of transaction with second signature and buffer most be 181 length', () => { + defaultTransaction.signSignature = defaultSecondSignature; + const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAABhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsKsAxK0ZiLyiRddENWYKJ4v+a/L176i9qW2Sf6v4tPb8/cspU/arrKoRnWiAmHpV3qDmNUvINmBStF+iMUVSICDw==', 'base64'); + const transactionBytes = getTransactionBytes(defaultTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + (transactionBytes.length).should.be.equal(181); + }); }); - it('should be a function', () => { - (getTransactionBytes).should.be.type('function'); + describe('signature transaction, type 1', () => { + const signatureTransaction = { + type: 1, + amount: defaultNoAmount, + fee: 500000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { signature: { publicKey: defaultSenderSecondPublicKey } }, + signature: defaultSignature, + id: defaultTransactionId, + }; + + it('should create correct transactionBytes from signature transaction', () => { + const expectedBuffer = Buffer.from('AaopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAAAEAcisnyne2eHk1ba0MFHLJbIvJ8e3s1CSFh6FGUb4L2GKVJdSEurZPfjIgWVcYlVEvOjtfM3+bwikLuz7Gt69BRMHvlAUuwUWF7r3gV1Q9iEp5wkYGQNh5dTdR5ZUGwo=', 'base64'); + const transactionBytes = getTransactionBytes(signatureTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }) }); - it('should return Buffer of simply transaction and buffer most be 117 length', () => { - const transaction = { - type: 0, - amount: 1000, - recipientId: '58191285901858109L', - timestamp: 141738, - asset: {}, - senderPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - signature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', - id: '13987348420913138422', + describe('delegate registration transaction, type 2', () => { + const delegateRegistrationTransaction = { + type: 2, + amount: defaultNoAmount, + fee: 2500000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { delegate: { username: 'MyDelegateUsername' } }, + signature: defaultSignature, + id: defaultTransactionId, }; - bytes = getTransactionBytes(transaction); - (bytes).should.be.ok(); - (bytes).should.be.type('object'); - (bytes.length).should.be.equal(117); + it('should create correct transactionBytes from delegate registration transaction', () => { + const expectedBuffer = Buffer.from('AqopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAABNeURlbGVnYXRlVXNlcm5hbWVhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); + const transactionBytes = getTransactionBytes(delegateRegistrationTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); + + }); + + describe('vote transaction, type 3', () => { + const voteTransaction = { + type: 3, + amount: 0, + fee: 100000000, + recipientId: '18160565574430594874L', + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { + votes: [ + `+${defaultSenderPublicKey}`, + `+${defaultSenderSecondPublicKey}` + ] + }, + signature: defaultSignature, + id: defaultTransactionId + }; + + it('should create correct transactionBytes from vote transaction', () => { + const expectedBuffer = Buffer.from('A6opAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCfwHSivQH5c6AAAAAAAAAAArNWQwMzZhODU4Y2U4OWY4NDQ0OTE3NjJlYjg5ZTJiZmJkNTBhNGEwYTBkYTY1OGU0YjI2MjhiMjViMTE3YWUwOSswNDAxYzhhYzlmMjlkZWQ5ZTFlNGQ1YjZiNDMwNTFjYjI1YjIyZjI3YzdiN2IzNTA5MjE2MWU4NTE5NDZmODJmYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); + const transactionBytes = getTransactionBytes(voteTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); + }); - it('should return Buffer of transaction with second signature and buffer most be 181 length', () => { - const transaction = { + describe('multisignature transaction, type 4', () => { + const createMultiSignatureTransaction = { + type: 4, + amount: 0, + fee: 1500000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { + multisignature: + { + min: 2, + lifetime: 5, + keysgroup: [ + `+${defaultSenderPublicKey}`, + `+${defaultSenderSecondPublicKey}` + ] + } + }, + signature: defaultSignature, + id: defaultTransactionId + }; + + const multiSignatureTransaction = { type: 0, - amount: 1000, - recipientId: '58191285901858109L', - timestamp: 141738, - asset: {}, - senderPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - signature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', - signSignature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', - id: '13987348420913138422', + amount: 1000, + fee: 10000000, + recipientId: defaultRecipient, + senderPublicKey: defaultSenderPublicKey, + requesterPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', + timestamp: defaultTimestamp, + asset: {}, + signatures: [], + signature: defaultSignature, + id: defaultTransactionId + }; + + it('should create correct transactionBytes from create multisignature transaction', () => { + const expectedBuffer = Buffer.from('BKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAAACBSs1ZDAzNmE4NThjZTg5Zjg0NDQ5MTc2MmViODllMmJmYmQ1MGE0YTBhMGRhNjU4ZTRiMjYyOGIyNWIxMTdhZTA5KzA0MDFjOGFjOWYyOWRlZDllMWU0ZDViNmI0MzA1MWNiMjViMjJmMjdjN2I3YjM1MDkyMTYxZTg1MTk0NmY4MmZhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); + const transactionBytes = getTransactionBytes(createMultiSignatureTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); + + it('should create correct transactionBytes from multisignature send transaction', () => { + const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCV0DaoWM6J+ERJF2LrieK/vVCkoKDaZY5LJiiyWxF64JAM68qo00FT3oAwAAAAAAAGGKVJdSEurZPfjIgWVcYlVEvOjtfM3+bwikLuz7Gt69BRMHvlAUuwUWF7r3gV1Q9iEp5wkYGQNh5dTdR5ZUGwo=', 'base64'); + const transactionBytes = getTransactionBytes(multiSignatureTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); + }); + + describe('dapp transaction, type 5', () => { + const dappTransaction = { + type: 5, + amount: 0, + fee: 2500000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: + { dapp: + { category: 0, + name: 'Lisk Guestbook', + description: 'The official Lisk guestbook', + tags: 'guestbook message sidechain', + type: 0, + link: 'https://github.com/MaxKK/guestbookDapp/archive/master.zip', + icon: 'https://raw.githubusercontent.com/MaxKK/guestbookDapp/master/icon.png' } }, + signature: defaultSignature, + id: defaultTransactionId, + } + + it('should create correct transactionBytes from dapp transaction', () => { + const expectedBuffer = Buffer.from('BaopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAABMaXNrIEd1ZXN0Ym9va1RoZSBvZmZpY2lhbCBMaXNrIGd1ZXN0Ym9va2d1ZXN0Ym9vayBtZXNzYWdlIHNpZGVjaGFpbmh0dHBzOi8vZ2l0aHViLmNvbS9NYXhLSy9ndWVzdGJvb2tEYXBwL2FyY2hpdmUvbWFzdGVyLnppcGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9NYXhLSy9ndWVzdGJvb2tEYXBwL21hc3Rlci9pY29uLnBuZwAAAAAAAAAAYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); + const transactionBytes = getTransactionBytes(dappTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); + }); + + describe('inTransfer transaction, type 6', () => { + const inTransferTransction = { + type: 6, + amount: defaultAmount, + fee: 10000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { inTransfer: { dappId: defaultAppId } }, + signature: defaultSignature, + id: defaultTransactionId, + }; + it('should create correct transactionBytes from inTransfer transaction', () => { + const expectedBuffer = Buffer.from('BqopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAA6AMAAAAAAAAxMjM0MjEzYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); + const transactionBytes = getTransactionBytes(inTransferTransction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); + }); + + describe('outTransfer transaction, type 7', () => { + const outTransferTransaction = { + type: 7, + amount: defaultAmount, + fee: 10000000, + recipientId: defaultRecipient, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { outTransfer: { dappId: defaultAppId, transactionId: defaultTransactionId } }, + signature: defaultSignature, + id: defaultTransactionId }; + it('should create correct transactionBytes from outTransfer transaction', () => { + const expectedBuffer = Buffer.from('B6opAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAAAxMjM0MjEzMTM5ODczNDg0MjA5MTMxMzg0MjJhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); + const transactionBytes = getTransactionBytes(outTransferTransaction); - bytes = getTransactionBytes(transaction); - (bytes).should.be.ok(); - (bytes).should.be.type('object'); - (bytes.length).should.be.equal(181); + (transactionBytes).should.be.eql(expectedBuffer); + }); }); }); From edcc4528c7af3509e74d9c584ebf85cdc9ca6fb4 Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Fri, 15 Sep 2017 17:26:24 +0200 Subject: [PATCH 02/15] Continue refactor transactionBytes --- src/transactions/transactionBytes.js | 25 ++-- test/transactions/transactionBytes.js | 187 +++++++++++++------------- 2 files changed, 100 insertions(+), 112 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 653afd535..e9b4c8f43 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -56,6 +56,7 @@ function getEmptyBytesForTransaction(transaction) { function isSignatureTransaction() { const transactionAssetBuffer = Buffer.from(transaction.asset.signature.publicKey, 'hex'); + return { assetBytes: transactionAssetBuffer, assetSize: transactionAssetBuffer.length, @@ -69,6 +70,7 @@ function getEmptyBytesForTransaction(transaction) { function isDelegateTransaction() { const transactionAssetBuffer = Buffer.from(transaction.asset.delegate.username, 'utf8'); + return { assetBytes: transactionAssetBuffer, assetSize: transactionAssetBuffer.length, @@ -95,24 +97,15 @@ function getEmptyBytesForTransaction(transaction) { */ function isMultisignatureTransaction() { - const MINSIGNATURES = 1; - const LIFETIME = 1; - const keysgroupBuffer = Buffer.from(transaction.asset.multisignature.keysgroup.join(''), 'utf8'); - - const bb = new ByteBuffer(MINSIGNATURES + LIFETIME + keysgroupBuffer.length, true); - bb.writeByte(transaction.asset.multisignature.min); - bb.writeByte(transaction.asset.multisignature.lifetime); - for (let i = 0; i < keysgroupBuffer.length; i++) { - bb.writeByte(keysgroupBuffer[i]); - } - bb.flip(); - - bb.toBuffer(); - const multiSigBuffer = new Uint8Array(bb.toArrayBuffer()); + const multisigTransactionAsset = transaction.asset.multisignature; + const minBuffer = Buffer.alloc(1).fill(multisigTransactionAsset.min); + const lifetimeBuffer = Buffer.alloc(1).fill(multisigTransactionAsset.lifetime); + const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join('')); + const assetBuffer = Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); return { - assetBytes: multiSigBuffer, - assetSize: multiSigBuffer.length, + assetBytes: assetBuffer, + assetSize: assetBuffer.length, }; } diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index 7fb8d8487..9f2b01774 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -13,16 +13,11 @@ * */ import { getTransactionBytes } from '../../src/transactions/transactionBytes'; -import { createOutTransfer } from '../../src/transactions/transfer'; -import cryptoModule from '../../src/crypto'; -import ByteBuffer from 'bytebuffer'; -describe('#getTransactionBytes', () => { - const defaultSecret = 'secret'; - const defaultSecondSecret = 'second secret'; +describe.only('#getTransactionBytes', () => { const defaultRecipient = '58191285901858109L'; const defaultSenderPublicKey = '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09'; - const defaultSenderSecondPublicKey = '0401c8ac9f29ded9e1e4d5b6b43051cb25b22f27c7b7b35092161e851946f82f' + const defaultSenderSecondPublicKey = '0401c8ac9f29ded9e1e4d5b6b43051cb25b22f27c7b7b35092161e851946f82f'; const defaultAmount = 1000; const defaultNoAmount = 0; const defaultTimestamp = 141738; @@ -69,14 +64,14 @@ describe('#getTransactionBytes', () => { describe('signature transaction, type 1', () => { const signatureTransaction = { type: 1, - amount: defaultNoAmount, - fee: 500000000, - recipientId: null, - senderPublicKey: defaultSenderPublicKey, - timestamp: defaultTimestamp, - asset: { signature: { publicKey: defaultSenderSecondPublicKey } }, - signature: defaultSignature, - id: defaultTransactionId, + amount: defaultNoAmount, + fee: 500000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { signature: { publicKey: defaultSenderSecondPublicKey } }, + signature: defaultSignature, + id: defaultTransactionId, }; it('should create correct transactionBytes from signature transaction', () => { @@ -84,20 +79,20 @@ describe('#getTransactionBytes', () => { const transactionBytes = getTransactionBytes(signatureTransaction); (transactionBytes).should.be.eql(expectedBuffer); - }) + }); }); describe('delegate registration transaction, type 2', () => { const delegateRegistrationTransaction = { type: 2, - amount: defaultNoAmount, - fee: 2500000000, - recipientId: null, - senderPublicKey: defaultSenderPublicKey, - timestamp: defaultTimestamp, - asset: { delegate: { username: 'MyDelegateUsername' } }, - signature: defaultSignature, - id: defaultTransactionId, + amount: defaultNoAmount, + fee: 2500000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { delegate: { username: 'MyDelegateUsername' } }, + signature: defaultSignature, + id: defaultTransactionId, }; it('should create correct transactionBytes from delegate registration transaction', () => { @@ -106,25 +101,24 @@ describe('#getTransactionBytes', () => { (transactionBytes).should.be.eql(expectedBuffer); }); - }); describe('vote transaction, type 3', () => { const voteTransaction = { type: 3, - amount: 0, - fee: 100000000, - recipientId: '18160565574430594874L', - senderPublicKey: defaultSenderPublicKey, - timestamp: defaultTimestamp, - asset: { + amount: 0, + fee: 100000000, + recipientId: '18160565574430594874L', + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { votes: [ - `+${defaultSenderPublicKey}`, - `+${defaultSenderSecondPublicKey}` - ] - }, - signature: defaultSignature, - id: defaultTransactionId + `+${defaultSenderPublicKey}`, + `+${defaultSenderSecondPublicKey}`, + ], + }, + signature: defaultSignature, + id: defaultTransactionId, }; it('should create correct transactionBytes from vote transaction', () => { @@ -133,44 +127,43 @@ describe('#getTransactionBytes', () => { (transactionBytes).should.be.eql(expectedBuffer); }); - }); describe('multisignature transaction, type 4', () => { const createMultiSignatureTransaction = { type: 4, - amount: 0, - fee: 1500000000, - recipientId: null, - senderPublicKey: defaultSenderPublicKey, - timestamp: defaultTimestamp, - asset: { + amount: 0, + fee: 1500000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { multisignature: { min: 2, lifetime: 5, keysgroup: [ - `+${defaultSenderPublicKey}`, - `+${defaultSenderSecondPublicKey}` - ] - } + `+${defaultSenderPublicKey}`, + `+${defaultSenderSecondPublicKey}`, + ], + }, }, - signature: defaultSignature, - id: defaultTransactionId + signature: defaultSignature, + id: defaultTransactionId, }; const multiSignatureTransaction = { type: 0, - amount: 1000, - fee: 10000000, - recipientId: defaultRecipient, - senderPublicKey: defaultSenderPublicKey, - requesterPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - timestamp: defaultTimestamp, - asset: {}, - signatures: [], - signature: defaultSignature, - id: defaultTransactionId + amount: 1000, + fee: 10000000, + recipientId: defaultRecipient, + senderPublicKey: defaultSenderPublicKey, + requesterPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', + timestamp: defaultTimestamp, + asset: {}, + signatures: [], + signature: defaultSignature, + id: defaultTransactionId, }; it('should create correct transactionBytes from create multisignature transaction', () => { @@ -181,33 +174,35 @@ describe('#getTransactionBytes', () => { }); it('should create correct transactionBytes from multisignature send transaction', () => { - const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCV0DaoWM6J+ERJF2LrieK/vVCkoKDaZY5LJiiyWxF64JAM68qo00FT3oAwAAAAAAAGGKVJdSEurZPfjIgWVcYlVEvOjtfM3+bwikLuz7Gt69BRMHvlAUuwUWF7r3gV1Q9iEp5wkYGQNh5dTdR5ZUGwo=', 'base64'); - const transactionBytes = getTransactionBytes(multiSignatureTransaction); + const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCV0DaoWM6J+ERJF2LrieK/vVCkoKDaZY5LJiiyWxF64JAM68qo00FT3oAwAAAAAAAGGKVJdSEurZPfjIgWVcYlVEvOjtfM3+bwikLuz7Gt69BRMHvlAUuwUWF7r3gV1Q9iEp5wkYGQNh5dTdR5ZUGwo=', 'base64'); + const transactionBytes = getTransactionBytes(multiSignatureTransaction); - (transactionBytes).should.be.eql(expectedBuffer); + (transactionBytes).should.be.eql(expectedBuffer); }); }); describe('dapp transaction, type 5', () => { const dappTransaction = { type: 5, - amount: 0, - fee: 2500000000, - recipientId: null, - senderPublicKey: defaultSenderPublicKey, - timestamp: defaultTimestamp, - asset: - { dapp: - { category: 0, - name: 'Lisk Guestbook', - description: 'The official Lisk guestbook', - tags: 'guestbook message sidechain', - type: 0, - link: 'https://github.com/MaxKK/guestbookDapp/archive/master.zip', - icon: 'https://raw.githubusercontent.com/MaxKK/guestbookDapp/master/icon.png' } }, - signature: defaultSignature, - id: defaultTransactionId, - } + amount: 0, + fee: 2500000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { + dapp: { + category: 0, + name: 'Lisk Guestbook', + description: 'The official Lisk guestbook', + tags: 'guestbook message sidechain', + type: 0, + link: 'https://github.com/MaxKK/guestbookDapp/archive/master.zip', + icon: 'https://raw.githubusercontent.com/MaxKK/guestbookDapp/master/icon.png', + }, + }, + signature: defaultSignature, + id: defaultTransactionId, + }; it('should create correct transactionBytes from dapp transaction', () => { const expectedBuffer = Buffer.from('BaopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAABMaXNrIEd1ZXN0Ym9va1RoZSBvZmZpY2lhbCBMaXNrIGd1ZXN0Ym9va2d1ZXN0Ym9vayBtZXNzYWdlIHNpZGVjaGFpbmh0dHBzOi8vZ2l0aHViLmNvbS9NYXhLSy9ndWVzdGJvb2tEYXBwL2FyY2hpdmUvbWFzdGVyLnppcGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9NYXhLSy9ndWVzdGJvb2tEYXBwL21hc3Rlci9pY29uLnBuZwAAAAAAAAAAYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); @@ -220,14 +215,14 @@ describe('#getTransactionBytes', () => { describe('inTransfer transaction, type 6', () => { const inTransferTransction = { type: 6, - amount: defaultAmount, - fee: 10000000, - recipientId: null, - senderPublicKey: defaultSenderPublicKey, - timestamp: defaultTimestamp, - asset: { inTransfer: { dappId: defaultAppId } }, - signature: defaultSignature, - id: defaultTransactionId, + amount: defaultAmount, + fee: 10000000, + recipientId: null, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { inTransfer: { dappId: defaultAppId } }, + signature: defaultSignature, + id: defaultTransactionId, }; it('should create correct transactionBytes from inTransfer transaction', () => { const expectedBuffer = Buffer.from('BqopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAA6AMAAAAAAAAxMjM0MjEzYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); @@ -240,14 +235,14 @@ describe('#getTransactionBytes', () => { describe('outTransfer transaction, type 7', () => { const outTransferTransaction = { type: 7, - amount: defaultAmount, - fee: 10000000, - recipientId: defaultRecipient, - senderPublicKey: defaultSenderPublicKey, - timestamp: defaultTimestamp, - asset: { outTransfer: { dappId: defaultAppId, transactionId: defaultTransactionId } }, - signature: defaultSignature, - id: defaultTransactionId + amount: defaultAmount, + fee: 10000000, + recipientId: defaultRecipient, + senderPublicKey: defaultSenderPublicKey, + timestamp: defaultTimestamp, + asset: { outTransfer: { dappId: defaultAppId, transactionId: defaultTransactionId } }, + signature: defaultSignature, + id: defaultTransactionId, }; it('should create correct transactionBytes from outTransfer transaction', () => { const expectedBuffer = Buffer.from('B6opAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAAAxMjM0MjEzMTM5ODczNDg0MjA5MTMxMzg0MjJhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); From 5d58512c5f37df31fe6719ac34c30d74ad43d5af Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Mon, 18 Sep 2017 16:52:23 +0200 Subject: [PATCH 03/15] transactionBytes refactor part2 --- src/transactions/transactionBytes.js | 206 +++++++++------------------ 1 file changed, 64 insertions(+), 142 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index e9b4c8f43..994f5360a 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -13,7 +13,6 @@ * */ /* eslint-disable no-plusplus */ -import ByteBuffer from 'bytebuffer'; import bignum from 'browserify-bignum'; const typeSizes = { @@ -27,16 +26,53 @@ const typeSizes = { DATA: 64, }; -const sumTypeSizes = Object.values(typeSizes) - .reduce((sum, typeSize) => sum + typeSize, 0); +function getTransactionBuffer(transaction) { -/** - * @method getEmptyBytesForTransaction - * @param transaction Object - * @return {object} - */ + const transactionType = Buffer.alloc(typeSizes.TRANSACTION_TYPE).fill(transaction.type); + + const transactionTimestamp = Buffer.alloc(typeSizes.TIMESTAMP); + transactionTimestamp.writeIntLE(transaction.timestamp, 0, typeSizes.TIMESTAMP); + + const transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); + + const transactionRequesterPublicKey = transaction.requesterPublicKey + ? Buffer.from(transaction.requesterPublicKey, 'hex') + : Buffer.alloc(0); + + const transactionRecipientID = transaction.recipientId + ? Buffer.from(bignum(transaction.recipientId.slice(0, -1)).toBuffer({ size: typeSizes.RECIPIENT_ID })) + : Buffer.alloc(typeSizes.RECIPIENT_ID).fill(0); + + const transactionAmount = Buffer.alloc(typeSizes.AMOUNT); + transactionAmount.writeInt32LE(transaction.amount, 0, typeSizes.AMOUNT); + + const transactionAssetData = getAssetData(transaction).assetBytes; + + const transactionSignature = transaction.signature + ? Buffer.from(transaction.signature, 'hex') + : Buffer.alloc(0); + + const transactionSecondSignature = transaction.signSignature + ? Buffer.from(transaction.signSignature, 'hex') + : Buffer.alloc(0); + + const transactionBuffer = Buffer.concat([ + transactionType, + transactionTimestamp, + transactionSenderPublicKey, + transactionRequesterPublicKey, + transactionRecipientID, + transactionAmount, + transactionAssetData, + transactionSignature, + transactionSecondSignature, + ]); + + return transactionBuffer; +} + +function getAssetData(transaction) { -function getEmptyBytesForTransaction(transaction) { /** * @method isSendTransaction * @return {object} @@ -44,7 +80,7 @@ function getEmptyBytesForTransaction(transaction) { function isSendTransaction() { return { - assetBytes: null, + assetBytes: Buffer.alloc(0), assetSize: 0, }; } @@ -116,36 +152,20 @@ function getEmptyBytesForTransaction(transaction) { function isDappTransaction() { const dapp = transaction.asset.dapp; - let buf = Buffer.from(dapp.name); - - if (dapp.description) { - const descriptionBuf = Buffer.from(dapp.description); - buf = Buffer.concat([buf, descriptionBuf]); - } - - if (dapp.tags) { - const tagsBuf = Buffer.from(dapp.tags); - buf = Buffer.concat([buf, tagsBuf]); - } - - if (dapp.link) { - buf = Buffer.concat([buf, Buffer.from(dapp.link)]); - } - - if (dapp.icon) { - buf = Buffer.concat([buf, Buffer.from(dapp.icon)]); - } - - const bb = new ByteBuffer(4 + 4, true); - bb.writeInt(dapp.type); - bb.writeInt(dapp.category); - bb.flip(); - - buf = Buffer.concat([buf, bb.toBuffer()]); + const dappNameBuffer = Buffer.from(dapp.name); + const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description) : Buffer.from(''); + const dappTagsBuffer = dapp.tags ? Buffer.from(dapp.tags) : Buffer.from(''); + const dappLinkBuffer = Buffer.from(dapp.link); + const dappIconBuffer = dapp.icon ? Buffer.from(dapp.icon) : Buffer.from(''); + const dappTypeBuffer = Buffer.alloc(4).fill(dapp.type); + const dappCategoryBuffer = Buffer.alloc(4).fill(dapp.category); + const dappBuffer = Buffer.concat([ + dappNameBuffer, dappDescriptionBuffer, dappTagsBuffer, dappLinkBuffer, dappIconBuffer, dappTypeBuffer, dappCategoryBuffer + ]); return { - assetBytes: buf, - assetSize: buf.length, + assetBytes: dappBuffer, + assetSize: dappBuffer.length, }; } @@ -169,23 +189,16 @@ function getEmptyBytesForTransaction(transaction) { */ function isDappOutTransferTransaction() { - const dappBuf = Buffer.from(transaction.asset.outTransfer.dappId); - const transactionBuf = Buffer.from(transaction.asset.outTransfer.transactionId); - const buf = Buffer.concat([dappBuf, transactionBuf]); + const dappOutAppIdBuffer = Buffer.from(transaction.asset.outTransfer.dappId); + const dappOutTransactionIdBuffer = Buffer.from(transaction.asset.outTransfer.transactionId); + const transactionAssetBuffer = Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); return { - assetBytes: buf, - assetSize: buf.length, + assetBytes: transactionAssetBuffer, + assetSize: transactionAssetBuffer.length, }; } - /** - * `transactionType` describes the available transaction types. - * - * @property transactionType - * @type object - */ - const transactionType = { 0: isSendTransaction, 1: isSignatureTransaction, @@ -200,97 +213,6 @@ function getEmptyBytesForTransaction(transaction) { return transactionType[transaction.type](); } -/** - * @method createTransactionBuffer - * @param transaction Object - * @return {buffer} - */ - -function createTransactionBuffer(transaction) { - function assignHexToTransactionBytes(partTransactionBuffer, hexValue) { - const hexBuffer = Buffer.from(hexValue, 'hex'); - for (let i = 0; i < hexBuffer.length; i++) { - partTransactionBuffer.writeByte(hexBuffer[i]); - } - return partTransactionBuffer; - } - - /** - * @method assignTransactionBuffer - * @param transactionBuffer buffer - * @param assetSize number - * @param assetBytes number - * @return {buffer} - */ - - function assignTransactionBuffer(transactionBuffer, assetSize, assetBytes) { - transactionBuffer.writeInt8(transaction.type); - transactionBuffer.writeInt(transaction.timestamp); - - assignHexToTransactionBytes(transactionBuffer, transaction.senderPublicKey); - - if (transaction.requesterPublicKey) { - assignHexToTransactionBytes(transactionBuffer, transaction.requesterPublicKey); - } - - if (transaction.recipientId) { - let recipient = transaction.recipientId.slice(0, -1); - recipient = bignum(recipient).toBuffer({ size: 8 }); - - for (let i = 0; i < 8; i++) { - transactionBuffer.writeByte(recipient[i] || 0); - } - } else { - for (let i = 0; i < 8; i++) { - transactionBuffer.writeByte(0); - } - } - transactionBuffer.writeLong(transaction.amount); - - if (transaction.asset.data) { - const dataBuffer = Buffer.from(transaction.asset.data); - for (let i = 0; i < dataBuffer.length; i++) { - transactionBuffer.writeByte(dataBuffer[i]); - } - } - - if (assetSize > 0) { - for (let i = 0; i < assetSize; i++) { - transactionBuffer.writeByte(assetBytes[i]); - } - } - - if (transaction.signature) { - assignHexToTransactionBytes(transactionBuffer, transaction.signature); - } - - if (transaction.signSignature) { - assignHexToTransactionBytes(transactionBuffer, transaction.signSignature); - } - - transactionBuffer.flip(); - const arrayBuffer = new Uint8Array(transactionBuffer.toArrayBuffer()); - const buffer = []; - - for (let i = 0; i < arrayBuffer.length; i++) { - buffer[i] = arrayBuffer[i]; - } - - return Buffer.from(buffer); - } - - // Get Transaction Size and Bytes - const transactionAssetSizeBuffer = getEmptyBytesForTransaction(transaction); - const assetSize = transactionAssetSizeBuffer.assetSize; - const assetBytes = transactionAssetSizeBuffer.assetBytes; - - const emptyTransactionBuffer = new ByteBuffer(sumTypeSizes + assetSize, true); - const assignedTransactionBuffer = assignTransactionBuffer( - emptyTransactionBuffer, assetSize, assetBytes, - ); - - return assignedTransactionBuffer; -} /** * @method getBytes @@ -300,7 +222,7 @@ function createTransactionBuffer(transaction) { */ function getTransactionBytes(transaction) { - return createTransactionBuffer(transaction); + return getTransactionBuffer(transaction); } module.exports = { From 179f668df2231ff5f8dce5c9721cae0f4f43d12b Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Mon, 18 Sep 2017 17:09:05 +0200 Subject: [PATCH 04/15] Eslint, remove bytebuffer from dependencies --- package.json | 1 - src/transactions/transactionBytes.js | 152 ++++++++++++--------------- 2 files changed, 65 insertions(+), 88 deletions(-) diff --git a/package.json b/package.json index 82169bf4f..eafa95f69 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "babel-polyfill": "=6.23.0", "bitcore-mnemonic": "LiskHQ/bitcore-mnemonic#v1.2.5", "browserify-bignum": "=1.3.0-2", - "bytebuffer": "=5.0.1", "ed2curve": "=0.2.1", "js-nacl": "LiskHQ/js-nacl#6dc1417", "popsicle": "=9.1.0" diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 994f5360a..57c2d5c23 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -26,63 +26,16 @@ const typeSizes = { DATA: 64, }; -function getTransactionBuffer(transaction) { - - const transactionType = Buffer.alloc(typeSizes.TRANSACTION_TYPE).fill(transaction.type); - - const transactionTimestamp = Buffer.alloc(typeSizes.TIMESTAMP); - transactionTimestamp.writeIntLE(transaction.timestamp, 0, typeSizes.TIMESTAMP); - - const transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); - - const transactionRequesterPublicKey = transaction.requesterPublicKey - ? Buffer.from(transaction.requesterPublicKey, 'hex') - : Buffer.alloc(0); - - const transactionRecipientID = transaction.recipientId - ? Buffer.from(bignum(transaction.recipientId.slice(0, -1)).toBuffer({ size: typeSizes.RECIPIENT_ID })) - : Buffer.alloc(typeSizes.RECIPIENT_ID).fill(0); - - const transactionAmount = Buffer.alloc(typeSizes.AMOUNT); - transactionAmount.writeInt32LE(transaction.amount, 0, typeSizes.AMOUNT); - - const transactionAssetData = getAssetData(transaction).assetBytes; - - const transactionSignature = transaction.signature - ? Buffer.from(transaction.signature, 'hex') - : Buffer.alloc(0); - - const transactionSecondSignature = transaction.signSignature - ? Buffer.from(transaction.signSignature, 'hex') - : Buffer.alloc(0); - - const transactionBuffer = Buffer.concat([ - transactionType, - transactionTimestamp, - transactionSenderPublicKey, - transactionRequesterPublicKey, - transactionRecipientID, - transactionAmount, - transactionAssetData, - transactionSignature, - transactionSecondSignature, - ]); - - return transactionBuffer; -} - function getAssetData(transaction) { - /** * @method isSendTransaction * @return {object} */ function isSendTransaction() { - return { - assetBytes: Buffer.alloc(0), - assetSize: 0, - }; + return transaction.asset.data + ? Buffer.from(transaction.asset.data, 'utf8') + : Buffer.alloc(0); } /** @@ -91,12 +44,7 @@ function getAssetData(transaction) { */ function isSignatureTransaction() { - const transactionAssetBuffer = Buffer.from(transaction.asset.signature.publicKey, 'hex'); - - return { - assetBytes: transactionAssetBuffer, - assetSize: transactionAssetBuffer.length, - }; + return Buffer.from(transaction.asset.signature.publicKey, 'hex'); } /** @@ -105,12 +53,7 @@ function getAssetData(transaction) { */ function isDelegateTransaction() { - const transactionAssetBuffer = Buffer.from(transaction.asset.delegate.username, 'utf8'); - - return { - assetBytes: transactionAssetBuffer, - assetSize: transactionAssetBuffer.length, - }; + return Buffer.from(transaction.asset.delegate.username, 'utf8'); } /** @@ -119,12 +62,7 @@ function getAssetData(transaction) { */ function isVoteTransaction() { - const transactionAssetBuffer = Buffer.from(transaction.asset.votes.join('')); - - return { - assetBytes: transactionAssetBuffer, - assetSize: transactionAssetBuffer.length, - }; + return Buffer.from(transaction.asset.votes.join('')); } /** @@ -139,10 +77,7 @@ function getAssetData(transaction) { const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join('')); const assetBuffer = Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); - return { - assetBytes: assetBuffer, - assetSize: assetBuffer.length, - }; + return assetBuffer; } /** @@ -160,13 +95,15 @@ function getAssetData(transaction) { const dappTypeBuffer = Buffer.alloc(4).fill(dapp.type); const dappCategoryBuffer = Buffer.alloc(4).fill(dapp.category); const dappBuffer = Buffer.concat([ - dappNameBuffer, dappDescriptionBuffer, dappTagsBuffer, dappLinkBuffer, dappIconBuffer, dappTypeBuffer, dappCategoryBuffer + dappNameBuffer, + dappDescriptionBuffer, + dappTagsBuffer, dappLinkBuffer, + dappIconBuffer, + dappTypeBuffer, + dappCategoryBuffer, ]); - return { - assetBytes: dappBuffer, - assetSize: dappBuffer.length, - }; + return dappBuffer; } /** @@ -175,12 +112,7 @@ function getAssetData(transaction) { */ function isDappInTransferTransaction() { - const transactionAssetBuffer = Buffer.from(transaction.asset.inTransfer.dappId); - - return { - assetBytes: transactionAssetBuffer, - assetSize: transactionAssetBuffer.length, - }; + return Buffer.from(transaction.asset.inTransfer.dappId); } /** @@ -193,10 +125,7 @@ function getAssetData(transaction) { const dappOutTransactionIdBuffer = Buffer.from(transaction.asset.outTransfer.transactionId); const transactionAssetBuffer = Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); - return { - assetBytes: transactionAssetBuffer, - assetSize: transactionAssetBuffer.length, - }; + return transactionAssetBuffer; } const transactionType = { @@ -214,6 +143,55 @@ function getAssetData(transaction) { } +function getTransactionBuffer(transaction) { + const transactionType = Buffer.alloc(typeSizes.TRANSACTION_TYPE).fill(transaction.type); + + const transactionTimestamp = Buffer.alloc(typeSizes.TIMESTAMP); + transactionTimestamp.writeIntLE(transaction.timestamp, 0, typeSizes.TIMESTAMP); + + const transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); + + const transactionRequesterPublicKey = transaction.requesterPublicKey + ? Buffer.from(transaction.requesterPublicKey, 'hex') + : Buffer.alloc(0); + + const transactionRecipientID = transaction.recipientId + ? Buffer.from( + bignum( + transaction.recipientId.slice(0, -1), + ).toBuffer({ size: typeSizes.RECIPIENT_ID }), + ) + : Buffer.alloc(typeSizes.RECIPIENT_ID).fill(0); + + const transactionAmount = Buffer.alloc(typeSizes.AMOUNT); + transactionAmount.writeInt32LE(transaction.amount, 0, typeSizes.AMOUNT); + + const transactionAssetData = getAssetData(transaction); + + const transactionSignature = transaction.signature + ? Buffer.from(transaction.signature, 'hex') + : Buffer.alloc(0); + + const transactionSecondSignature = transaction.signSignature + ? Buffer.from(transaction.signSignature, 'hex') + : Buffer.alloc(0); + + const transactionBuffer = Buffer.concat([ + transactionType, + transactionTimestamp, + transactionSenderPublicKey, + transactionRequesterPublicKey, + transactionRecipientID, + transactionAmount, + transactionAssetData, + transactionSignature, + transactionSecondSignature, + ]); + + return transactionBuffer; +} + + /** * @method getBytes * @param transaction Object From 731f3b43a96452278208d4b18d54f642eaa2978d Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Mon, 18 Sep 2017 17:09:48 +0200 Subject: [PATCH 05/15] remove only from test --- test/transactions/transactionBytes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index 9f2b01774..e1d6a927d 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -14,7 +14,7 @@ */ import { getTransactionBytes } from '../../src/transactions/transactionBytes'; -describe.only('#getTransactionBytes', () => { +describe('#getTransactionBytes', () => { const defaultRecipient = '58191285901858109L'; const defaultSenderPublicKey = '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09'; const defaultSenderSecondPublicKey = '0401c8ac9f29ded9e1e4d5b6b43051cb25b22f27c7b7b35092161e851946f82f'; From 2e070284103c62e0b58361a5c416890064f1b811 Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Tue, 19 Sep 2017 13:48:38 +0200 Subject: [PATCH 06/15] getAssetBytes function --- src/transactions/transactionBytes.js | 324 ++++++++++++++------------- 1 file changed, 166 insertions(+), 158 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 57c2d5c23..05b54fcd2 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -15,8 +15,8 @@ /* eslint-disable no-plusplus */ import bignum from 'browserify-bignum'; -const typeSizes = { - TRANSACTION_TYPE: 1, +const byteSizes = { + TYPE: 1, TIMESTAMP: 4, MULTISIGNATURE_PUBLICKEY: 32, RECIPIENT_ID: 8, @@ -26,183 +26,191 @@ const typeSizes = { DATA: 64, }; -function getAssetData(transaction) { - /** - * @method isSendTransaction - * @return {object} - */ - - function isSendTransaction() { - return transaction.asset.data - ? Buffer.from(transaction.asset.data, 'utf8') - : Buffer.alloc(0); - } - - /** - * @method isSignatureTransaction - * @return {object} - */ +class Transaction { - function isSignatureTransaction() { - return Buffer.from(transaction.asset.signature.publicKey, 'hex'); - } + constructor(transaction) { - /** - * @method isDelegateTransaction - * @return {object} - */ + this.transaction = transaction; - function isDelegateTransaction() { - return Buffer.from(transaction.asset.delegate.username, 'utf8'); - } + this.transactionType = Buffer.alloc(byteSizes.TYPE); + this.transactionType.writeInt8(transaction.type) - /** - * @method isVoteTransaction - * @return {object} - */ + this.transactionTimestamp = Buffer.alloc(byteSizes.TIMESTAMP) + this.transactionTimestamp.writeIntLE(transaction.timestamp, 0, byteSizes.TIMESTAMP); - function isVoteTransaction() { - return Buffer.from(transaction.asset.votes.join('')); - } + this.transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); + this.transactionRequesterPublicKey = transaction.requesterPublicKey + ? Buffer.from(transaction.requesterPublicKey, 'hex') + : Buffer.alloc(0); - /** - * @method isMultisignatureTransaction - * @return {object} - */ + this.transactionRecipientID = transaction.recipientId + ? Buffer.from( + bignum( + transaction.recipientId.slice(0, -1), + ).toBuffer({ size: byteSizes.RECIPIENT_ID }), + ) + : Buffer.alloc(byteSizes.RECIPIENT_ID).fill(0); - function isMultisignatureTransaction() { - const multisigTransactionAsset = transaction.asset.multisignature; - const minBuffer = Buffer.alloc(1).fill(multisigTransactionAsset.min); - const lifetimeBuffer = Buffer.alloc(1).fill(multisigTransactionAsset.lifetime); - const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join('')); - const assetBuffer = Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); + this.transactionAmount = Buffer.alloc(byteSizes.AMOUNT); + this.transactionAmount.writeInt32LE(transaction.amount, 0, byteSizes.AMOUNT); - return assetBuffer; - } + this.transactionAssetData = this.getAssetBytes(this.transaction); - /** - * @method isDappTransaction - * @return {object} - */ + this.transactionSignature = transaction.signature + ? Buffer.from(transaction.signature, 'hex') + : Buffer.alloc(0); - function isDappTransaction() { - const dapp = transaction.asset.dapp; - const dappNameBuffer = Buffer.from(dapp.name); - const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description) : Buffer.from(''); - const dappTagsBuffer = dapp.tags ? Buffer.from(dapp.tags) : Buffer.from(''); - const dappLinkBuffer = Buffer.from(dapp.link); - const dappIconBuffer = dapp.icon ? Buffer.from(dapp.icon) : Buffer.from(''); - const dappTypeBuffer = Buffer.alloc(4).fill(dapp.type); - const dappCategoryBuffer = Buffer.alloc(4).fill(dapp.category); - const dappBuffer = Buffer.concat([ - dappNameBuffer, - dappDescriptionBuffer, - dappTagsBuffer, dappLinkBuffer, - dappIconBuffer, - dappTypeBuffer, - dappCategoryBuffer, - ]); + this.transactionSecondSignature = transaction.signSignature + ? Buffer.from(transaction.signSignature, 'hex') + : Buffer.alloc(0); - return dappBuffer; } - /** - * @method isDappInTransferTransaction - * @return {object} - */ + get transactionBytes() { + return this.concatTransactionBytes(); + } - function isDappInTransferTransaction() { - return Buffer.from(transaction.asset.inTransfer.dappId); + concatTransactionBytes () { + return Buffer.concat([ + this.transactionType, + this.transactionTimestamp, + this.transactionSenderPublicKey, + this.transactionRequesterPublicKey, + this.transactionRecipientID, + this.transactionAmount, + this.transactionAssetData, + this.transactionSignature, + this.transactionSecondSignature, + ]); } /** - * @method isDappOutTransferTransaction - * @return {object} + * @method getAssetBytes + * @return {Buffer} */ - function isDappOutTransferTransaction() { - const dappOutAppIdBuffer = Buffer.from(transaction.asset.outTransfer.dappId); - const dappOutTransactionIdBuffer = Buffer.from(transaction.asset.outTransfer.transactionId); - const transactionAssetBuffer = Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); - - return transactionAssetBuffer; + getAssetBytes(transaction) { + /** + * @method isSendTransaction + * @return {Buffer} + */ + + function isSendTransaction() { + return transaction.asset.data + ? Buffer.from(transaction.asset.data, 'utf8') + : Buffer.alloc(0); + } + + /** + * @method isSignatureTransaction + * @return {Buffer} + */ + + function isSignatureTransaction() { + return Buffer.from(transaction.asset.signature.publicKey, 'hex'); + } + + /** + * @method isDelegateTransaction + * @return {Buffer} + */ + + function isDelegateTransaction() { + return Buffer.from(transaction.asset.delegate.username, 'utf8'); + } + + /** + * @method isVoteTransaction + * @return {Buffer} + */ + + function isVoteTransaction() { + return Buffer.from(transaction.asset.votes.join('')); + } + + /** + * @method isMultisignatureTransaction + * @return {Buffer} + */ + + function isMultisignatureTransaction() { + const multisigTransactionAsset = transaction.asset.multisignature; + const minBuffer = Buffer.alloc(1); + minBuffer.writeInt8(multisigTransactionAsset.min); + const lifetimeBuffer = Buffer.alloc(1); + lifetimeBuffer.writeInt8(multisigTransactionAsset.lifetime); + const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join('')); + const assetBuffer = Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); + + return assetBuffer; + } + + /** + * @method isDappTransaction + * @return {Buffer} + */ + + function isDappTransaction() { + const dapp = transaction.asset.dapp; + const dappNameBuffer = Buffer.from(dapp.name); + const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description) : Buffer.from(''); + const dappTagsBuffer = dapp.tags ? Buffer.from(dapp.tags) : Buffer.from(''); + const dappLinkBuffer = Buffer.from(dapp.link); + const dappIconBuffer = dapp.icon ? Buffer.from(dapp.icon) : Buffer.from(''); + const dappTypeBuffer = Buffer.alloc(4); + dappTypeBuffer.writeInt8(dapp.type); + const dappCategoryBuffer = Buffer.alloc(4); + dappCategoryBuffer.writeInt8(dapp.category); + const dappBuffer = Buffer.concat([ + dappNameBuffer, + dappDescriptionBuffer, + dappTagsBuffer, dappLinkBuffer, + dappIconBuffer, + dappTypeBuffer, + dappCategoryBuffer, + ]); + + return dappBuffer; + } + + /** + * @method isDappInTransferTransaction + * @return {Buffer} + */ + + function isDappInTransferTransaction() { + return Buffer.from(transaction.asset.inTransfer.dappId); + } + + /** + * @method isDappOutTransferTransaction + * @return {Buffer} + */ + + function isDappOutTransferTransaction() { + const dappOutAppIdBuffer = Buffer.from(transaction.asset.outTransfer.dappId); + const dappOutTransactionIdBuffer = Buffer.from(transaction.asset.outTransfer.transactionId); + const transactionAssetBuffer = Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); + + return transactionAssetBuffer; + } + + const transactionType = { + 0: isSendTransaction, + 1: isSignatureTransaction, + 2: isDelegateTransaction, + 3: isVoteTransaction, + 4: isMultisignatureTransaction, + 5: isDappTransaction, + 6: isDappInTransferTransaction, + 7: isDappOutTransferTransaction, + }; + + return transactionType[transaction.type](); } - const transactionType = { - 0: isSendTransaction, - 1: isSignatureTransaction, - 2: isDelegateTransaction, - 3: isVoteTransaction, - 4: isMultisignatureTransaction, - 5: isDappTransaction, - 6: isDappInTransferTransaction, - 7: isDappOutTransferTransaction, - }; - - return transactionType[transaction.type](); -} - - -function getTransactionBuffer(transaction) { - const transactionType = Buffer.alloc(typeSizes.TRANSACTION_TYPE).fill(transaction.type); - - const transactionTimestamp = Buffer.alloc(typeSizes.TIMESTAMP); - transactionTimestamp.writeIntLE(transaction.timestamp, 0, typeSizes.TIMESTAMP); - - const transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); - - const transactionRequesterPublicKey = transaction.requesterPublicKey - ? Buffer.from(transaction.requesterPublicKey, 'hex') - : Buffer.alloc(0); - - const transactionRecipientID = transaction.recipientId - ? Buffer.from( - bignum( - transaction.recipientId.slice(0, -1), - ).toBuffer({ size: typeSizes.RECIPIENT_ID }), - ) - : Buffer.alloc(typeSizes.RECIPIENT_ID).fill(0); - - const transactionAmount = Buffer.alloc(typeSizes.AMOUNT); - transactionAmount.writeInt32LE(transaction.amount, 0, typeSizes.AMOUNT); - - const transactionAssetData = getAssetData(transaction); - - const transactionSignature = transaction.signature - ? Buffer.from(transaction.signature, 'hex') - : Buffer.alloc(0); - - const transactionSecondSignature = transaction.signSignature - ? Buffer.from(transaction.signSignature, 'hex') - : Buffer.alloc(0); - - const transactionBuffer = Buffer.concat([ - transactionType, - transactionTimestamp, - transactionSenderPublicKey, - transactionRequesterPublicKey, - transactionRecipientID, - transactionAmount, - transactionAssetData, - transactionSignature, - transactionSecondSignature, - ]); - - return transactionBuffer; } - -/** - * @method getBytes - * @param transaction Object - * - * @return {buffer} - */ - -function getTransactionBytes(transaction) { - return getTransactionBuffer(transaction); +export function getTransactionBytes(transaction) { + return new Transaction(transaction).transactionBytes; } - -module.exports = { - getTransactionBytes, -}; From e4eb2e2ce1c6d0e01e36263ccc408ed17b671ad1 Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Tue, 19 Sep 2017 17:36:51 +0200 Subject: [PATCH 07/15] Refactor transactionBytes, add errors in data and type length --- src/transactions/transactionBytes.js | 279 ++++++++++++++------------ test/transactions/transactionBytes.js | 24 ++- 2 files changed, 163 insertions(+), 140 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 05b54fcd2..509d30070 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -12,7 +12,6 @@ * Removal or modification of this copyright notice is prohibited. * */ -/* eslint-disable no-plusplus */ import bignum from 'browserify-bignum'; const byteSizes = { @@ -26,17 +25,138 @@ const byteSizes = { DATA: 64, }; -class Transaction { +/** + * @method getAssetBytes + * @return {Buffer} + */ - constructor(transaction) { +function getAssetBytesHelper(transaction) { + /** + * @method isSendTransaction + * @return {Buffer} + */ + + function isSendTransaction() { + return transaction.asset.data + ? Buffer.from(transaction.asset.data, 'utf8') + : Buffer.alloc(0); + } + + /** + * @method isSignatureTransaction + * @return {Buffer} + */ + + function isSignatureTransaction() { + return Buffer.from(transaction.asset.signature.publicKey, 'hex'); + } + + /** + * @method isDelegateTransaction + * @return {Buffer} + */ + + function isDelegateTransaction() { + return Buffer.from(transaction.asset.delegate.username, 'utf8'); + } + + /** + * @method isVoteTransaction + * @return {Buffer} + */ + + function isVoteTransaction() { + return Buffer.from(transaction.asset.votes.join('')); + } + + /** + * @method isMultisignatureTransaction + * @return {Buffer} + */ + + function isMultisignatureTransaction() { + const multisigTransactionAsset = transaction.asset.multisignature; + const minBuffer = Buffer.alloc(1); + minBuffer.writeInt8(multisigTransactionAsset.min); + const lifetimeBuffer = Buffer.alloc(1); + lifetimeBuffer.writeInt8(multisigTransactionAsset.lifetime); + const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join('')); + + return Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); + } + + /** + * @method isDappTransaction + * @return {Buffer} + */ + + function isDappTransaction() { + const dapp = transaction.asset.dapp; + const dappNameBuffer = Buffer.from(dapp.name); + const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description) : Buffer.from(''); + const dappTagsBuffer = dapp.tags ? Buffer.from(dapp.tags) : Buffer.from(''); + const dappLinkBuffer = Buffer.from(dapp.link); + const dappIconBuffer = dapp.icon ? Buffer.from(dapp.icon) : Buffer.from(''); + const dappTypeBuffer = Buffer.alloc(4); + dappTypeBuffer.writeInt8(dapp.type); + const dappCategoryBuffer = Buffer.alloc(4); + dappCategoryBuffer.writeInt8(dapp.category); + + return Buffer.concat([ + dappNameBuffer, + dappDescriptionBuffer, + dappTagsBuffer, dappLinkBuffer, + dappIconBuffer, + dappTypeBuffer, + dappCategoryBuffer, + ]); + } + /** + * @method isDappInTransferTransaction + * @return {Buffer} + */ + + function isDappInTransferTransaction() { + return Buffer.from(transaction.asset.inTransfer.dappId); + } + + /** + * @method isDappOutTransferTransaction + * @return {Buffer} + */ + + function isDappOutTransferTransaction() { + const dappOutAppIdBuffer = Buffer.from(transaction.asset.outTransfer.dappId); + const dappOutTransactionIdBuffer = Buffer.from(transaction.asset.outTransfer.transactionId); + + return Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); + } + + const transactionType = { + 0: isSendTransaction, + 1: isSignatureTransaction, + 2: isDelegateTransaction, + 3: isVoteTransaction, + 4: isMultisignatureTransaction, + 5: isDappTransaction, + 6: isDappInTransferTransaction, + 7: isDappOutTransferTransaction, + }; + + return transactionType[transaction.type](); +} + +export class Transaction { + constructor(transaction) { + this.byteSizes = byteSizes; this.transaction = transaction; - this.transactionType = Buffer.alloc(byteSizes.TYPE); - this.transactionType.writeInt8(transaction.type) + this.transactionType = Buffer.alloc(this.byteSizes.TYPE); + this.transactionType.writeInt8(transaction.type); - this.transactionTimestamp = Buffer.alloc(byteSizes.TIMESTAMP) - this.transactionTimestamp.writeIntLE(transaction.timestamp, 0, byteSizes.TIMESTAMP); + this.transactionTimestamp = Buffer.alloc(this.byteSizes.TIMESTAMP); + this.transactionTimestamp.writeIntLE(transaction.timestamp, 0, this.byteSizes.TIMESTAMP); this.transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); this.transactionRequesterPublicKey = transaction.requesterPublicKey @@ -47,14 +167,14 @@ class Transaction { ? Buffer.from( bignum( transaction.recipientId.slice(0, -1), - ).toBuffer({ size: byteSizes.RECIPIENT_ID }), + ).toBuffer({ size: this.byteSizes.RECIPIENT_ID }), ) - : Buffer.alloc(byteSizes.RECIPIENT_ID).fill(0); + : Buffer.alloc(this.byteSizes.RECIPIENT_ID).fill(0); - this.transactionAmount = Buffer.alloc(byteSizes.AMOUNT); - this.transactionAmount.writeInt32LE(transaction.amount, 0, byteSizes.AMOUNT); + this.transactionAmount = Buffer.alloc(this.byteSizes.AMOUNT); + this.transactionAmount.writeInt32LE(transaction.amount, 0, this.byteSizes.AMOUNT); - this.transactionAssetData = this.getAssetBytes(this.transaction); + this.transactionAssetData = this.getAssetBytes(transaction); this.transactionSignature = transaction.signature ? Buffer.from(transaction.signature, 'hex') @@ -64,13 +184,15 @@ class Transaction { ? Buffer.from(transaction.signSignature, 'hex') : Buffer.alloc(0); + + if (!this.checkTransaction()) return false; } get transactionBytes() { return this.concatTransactionBytes(); } - concatTransactionBytes () { + concatTransactionBytes() { return Buffer.concat([ this.transactionType, this.transactionTimestamp, @@ -84,131 +206,22 @@ class Transaction { ]); } - /** - * @method getAssetBytes - * @return {Buffer} - */ - - getAssetBytes(transaction) { - /** - * @method isSendTransaction - * @return {Buffer} - */ - - function isSendTransaction() { - return transaction.asset.data - ? Buffer.from(transaction.asset.data, 'utf8') - : Buffer.alloc(0); - } - - /** - * @method isSignatureTransaction - * @return {Buffer} - */ - - function isSignatureTransaction() { - return Buffer.from(transaction.asset.signature.publicKey, 'hex'); - } - - /** - * @method isDelegateTransaction - * @return {Buffer} - */ - - function isDelegateTransaction() { - return Buffer.from(transaction.asset.delegate.username, 'utf8'); - } - - /** - * @method isVoteTransaction - * @return {Buffer} - */ - - function isVoteTransaction() { - return Buffer.from(transaction.asset.votes.join('')); - } - - /** - * @method isMultisignatureTransaction - * @return {Buffer} - */ - - function isMultisignatureTransaction() { - const multisigTransactionAsset = transaction.asset.multisignature; - const minBuffer = Buffer.alloc(1); - minBuffer.writeInt8(multisigTransactionAsset.min); - const lifetimeBuffer = Buffer.alloc(1); - lifetimeBuffer.writeInt8(multisigTransactionAsset.lifetime); - const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join('')); - const assetBuffer = Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); - - return assetBuffer; - } - - /** - * @method isDappTransaction - * @return {Buffer} - */ - - function isDappTransaction() { - const dapp = transaction.asset.dapp; - const dappNameBuffer = Buffer.from(dapp.name); - const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description) : Buffer.from(''); - const dappTagsBuffer = dapp.tags ? Buffer.from(dapp.tags) : Buffer.from(''); - const dappLinkBuffer = Buffer.from(dapp.link); - const dappIconBuffer = dapp.icon ? Buffer.from(dapp.icon) : Buffer.from(''); - const dappTypeBuffer = Buffer.alloc(4); - dappTypeBuffer.writeInt8(dapp.type); - const dappCategoryBuffer = Buffer.alloc(4); - dappCategoryBuffer.writeInt8(dapp.category); - const dappBuffer = Buffer.concat([ - dappNameBuffer, - dappDescriptionBuffer, - dappTagsBuffer, dappLinkBuffer, - dappIconBuffer, - dappTypeBuffer, - dappCategoryBuffer, - ]); - - return dappBuffer; - } - - /** - * @method isDappInTransferTransaction - * @return {Buffer} - */ + getAssetBytes() { + return getAssetBytesHelper(this.transaction); + } - function isDappInTransferTransaction() { - return Buffer.from(transaction.asset.inTransfer.dappId); + checkTransaction() { + if (this.transactionType > byteSizes.TYPE) { + throw new Error('Transaction type shall not be bigger than 1 byte.'); } - /** - * @method isDappOutTransferTransaction - * @return {Buffer} - */ - - function isDappOutTransferTransaction() { - const dappOutAppIdBuffer = Buffer.from(transaction.asset.outTransfer.dappId); - const dappOutTransactionIdBuffer = Buffer.from(transaction.asset.outTransfer.transactionId); - const transactionAssetBuffer = Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); - - return transactionAssetBuffer; + if (this.transaction.type === 0 && this.transaction.asset.data) { + if (this.transaction.asset.data.length > byteSizes.DATA + || this.transactionAssetData.length > byteSizes.DATA) { + throw new Error(`Transaction asset data exceeds size of ${byteSizes.DATA}.`); + } } - - const transactionType = { - 0: isSendTransaction, - 1: isSignatureTransaction, - 2: isDelegateTransaction, - 3: isVoteTransaction, - 4: isMultisignatureTransaction, - 5: isDappTransaction, - 6: isDappInTransferTransaction, - 7: isDappOutTransferTransaction, - }; - - return transactionType[transaction.type](); } - } export function getTransactionBytes(transaction) { diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index e1d6a927d..09d4ca7fb 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -21,13 +21,12 @@ describe('#getTransactionBytes', () => { const defaultAmount = 1000; const defaultNoAmount = 0; const defaultTimestamp = 141738; - const defaultAsset = {}; const defaultTransactionId = '13987348420913138422'; const defaultSignature = '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a'; const defaultSecondSignature = 'b00c4ad1988bca245d74435660a278bfe6bf2f5efa8bda96d927fabf8b4f6fcfdcb2953f6abacaa119d6880987a55dea0e6354bc8366052b45fa23145522020f'; const defaultAppId = '1234213'; - describe('send transaction, type 0', () => { + describe.only('send transaction, type 0', () => { let defaultTransaction; beforeEach(() => { @@ -36,28 +35,39 @@ describe('#getTransactionBytes', () => { amount: defaultAmount, recipientId: defaultRecipient, timestamp: defaultTimestamp, - asset: defaultAsset, + asset: {}, senderPublicKey: defaultSenderPublicKey, signature: defaultSignature, id: defaultTransactionId, }; }); - it('should return Buffer of type 0 (send LSk) transaction and buffer most be 117 length', () => { + it('should return Buffer of type 0 (send LSk) transaction', () => { const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAABhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); const transactionBytes = getTransactionBytes(defaultTransaction); (transactionBytes).should.be.eql(expectedBuffer); - (transactionBytes.length).should.be.equal(117); }); - it('should return Buffer of transaction with second signature and buffer most be 181 length', () => { + it('should return Buffer of type 0 (send LSk) with data', () => { + defaultTransaction.asset.data = 'Hello Lisk! Some data in here!...'; + const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAABIZWxsbyBMaXNrISBTb21lIGRhdGEgaW4gaGVyZSEuLi5hilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); + const transactionBytes = getTransactionBytes(defaultTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); + + it('should throw on type 0 with too much data', () => { + defaultTransaction.asset.data = '0123456789012345678901234567890123456789012345678901234567890123456789'; + (getTransactionBytes.bind(null, defaultTransaction)).should.throw('Transaction asset data exceeds size of 64.'); + }); + + it('should return Buffer of transaction with second signature', () => { defaultTransaction.signSignature = defaultSecondSignature; const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAABhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsKsAxK0ZiLyiRddENWYKJ4v+a/L176i9qW2Sf6v4tPb8/cspU/arrKoRnWiAmHpV3qDmNUvINmBStF+iMUVSICDw==', 'base64'); const transactionBytes = getTransactionBytes(defaultTransaction); (transactionBytes).should.be.eql(expectedBuffer); - (transactionBytes.length).should.be.equal(181); }); }); From 3e8f6c5780ef9c7bd5235f84db36be0f1aa9a03d Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Wed, 20 Sep 2017 09:32:39 +0200 Subject: [PATCH 08/15] Adds test and throws on exceeding data field --- src/transactions/transactionBytes.js | 40 +++++++++++++++++++++++---- test/transactions/transactionBytes.js | 2 +- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 509d30070..de64ae4ed 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -26,7 +26,7 @@ const byteSizes = { }; /** - * @method getAssetBytes + * @method getAssetBytesHelper * @return {Buffer} */ @@ -147,6 +147,14 @@ function getAssetBytesHelper(transaction) { return transactionType[transaction.type](); } +/** +* A utility class to get transaction byteSizes +* +* @class Transaction +* @param {Object} transaction +* @constructor +*/ + export class Transaction { constructor(transaction) { this.byteSizes = byteSizes; @@ -185,13 +193,25 @@ export class Transaction { : Buffer.alloc(0); - if (!this.checkTransaction()) return false; + this.checkTransaction(); + return this; } + /** + * @method get transactionBytes + * @return {Buffer} + */ + get transactionBytes() { return this.concatTransactionBytes(); } + /** + * @method concatTransactionBytes + * @private + * @return {Buffer} + */ + concatTransactionBytes() { return Buffer.concat([ this.transactionType, @@ -206,15 +226,23 @@ export class Transaction { ]); } + /** + * @method getAssetBytes + * @private + * @return {Buffer} + */ + getAssetBytes() { return getAssetBytesHelper(this.transaction); } - checkTransaction() { - if (this.transactionType > byteSizes.TYPE) { - throw new Error('Transaction type shall not be bigger than 1 byte.'); - } + /** + * @method checkTransaction + * @throws + * @return {} + */ + checkTransaction() { if (this.transaction.type === 0 && this.transaction.asset.data) { if (this.transaction.asset.data.length > byteSizes.DATA || this.transactionAssetData.length > byteSizes.DATA) { diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index 09d4ca7fb..e3fdb4c9e 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -26,7 +26,7 @@ describe('#getTransactionBytes', () => { const defaultSecondSignature = 'b00c4ad1988bca245d74435660a278bfe6bf2f5efa8bda96d927fabf8b4f6fcfdcb2953f6abacaa119d6880987a55dea0e6354bc8366052b45fa23145522020f'; const defaultAppId = '1234213'; - describe.only('send transaction, type 0', () => { + describe('send transaction, type 0', () => { let defaultTransaction; beforeEach(() => { From f6586c26a7657d548e68a3d74c6df8ff59be3d8e Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Fri, 22 Sep 2017 10:50:37 +0200 Subject: [PATCH 09/15] Adding and optimising tests and codebase for transactionBytes after review --- src/transactions/transactionBytes.js | 195 +++++++++++++------------- test/transactions/transactionBytes.js | 78 ++++++----- 2 files changed, 137 insertions(+), 136 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index de64ae4ed..ac9dbbf74 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -14,7 +14,7 @@ */ import bignum from 'browserify-bignum'; -const byteSizes = { +const BYTESIZES = { TYPE: 1, TIMESTAMP: 4, MULTISIGNATURE_PUBLICKEY: 32, @@ -25,114 +25,112 @@ const byteSizes = { DATA: 64, }; + /** - * @method getAssetBytesHelper + * @method isSendTransaction * @return {Buffer} */ -function getAssetBytesHelper(transaction) { - /** - * @method isSendTransaction - * @return {Buffer} - */ - - function isSendTransaction() { - return transaction.asset.data - ? Buffer.from(transaction.asset.data, 'utf8') - : Buffer.alloc(0); - } +function isSendTransaction(asset) { + return asset.data + ? Buffer.from(asset.data, 'utf8') + : Buffer.alloc(0); +} - /** - * @method isSignatureTransaction - * @return {Buffer} - */ +/** + * @method isSignatureTransaction + * @return {Buffer} + */ - function isSignatureTransaction() { - return Buffer.from(transaction.asset.signature.publicKey, 'hex'); - } +function isSignatureTransaction(asset) { + return Buffer.from(asset.signature.publicKey, 'hex'); +} - /** - * @method isDelegateTransaction - * @return {Buffer} - */ +/** + * @method isDelegateTransaction + * @return {Buffer} + */ - function isDelegateTransaction() { - return Buffer.from(transaction.asset.delegate.username, 'utf8'); - } +function isDelegateTransaction(asset) { + return Buffer.from(asset.delegate.username, 'utf8'); +} - /** - * @method isVoteTransaction - * @return {Buffer} - */ +/** + * @method isVoteTransaction + * @return {Buffer} + */ - function isVoteTransaction() { - return Buffer.from(transaction.asset.votes.join('')); - } +function isVoteTransaction(asset) { + return Buffer.from(asset.votes.join(''), 'utf8'); +} - /** - * @method isMultisignatureTransaction - * @return {Buffer} - */ +/** + * @method isMultisignatureTransaction + * @return {Buffer} + */ - function isMultisignatureTransaction() { - const multisigTransactionAsset = transaction.asset.multisignature; - const minBuffer = Buffer.alloc(1); - minBuffer.writeInt8(multisigTransactionAsset.min); - const lifetimeBuffer = Buffer.alloc(1); - lifetimeBuffer.writeInt8(multisigTransactionAsset.lifetime); - const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join('')); +function isMultisignatureTransaction(asset) { + const multisigTransactionAsset = asset.multisignature; + const minBuffer = Buffer.alloc(1, multisigTransactionAsset.min); + const lifetimeBuffer = Buffer.alloc(1, multisigTransactionAsset.lifetime); + const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join(''), 'utf8'); - return Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); - } + return Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); +} - /** - * @method isDappTransaction - * @return {Buffer} - */ +/** + * @method isDappTransaction + * @return {Buffer} + */ - function isDappTransaction() { - const dapp = transaction.asset.dapp; - const dappNameBuffer = Buffer.from(dapp.name); - const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description) : Buffer.from(''); - const dappTagsBuffer = dapp.tags ? Buffer.from(dapp.tags) : Buffer.from(''); - const dappLinkBuffer = Buffer.from(dapp.link); - const dappIconBuffer = dapp.icon ? Buffer.from(dapp.icon) : Buffer.from(''); - const dappTypeBuffer = Buffer.alloc(4); - dappTypeBuffer.writeInt8(dapp.type); - const dappCategoryBuffer = Buffer.alloc(4); - dappCategoryBuffer.writeInt8(dapp.category); +function isDappTransaction(asset) { + const dapp = asset.dapp; + const dappNameBuffer = Buffer.from(dapp.name, 'utf8'); + const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description, 'utf8') : Buffer.alloc(0); + const dappTagsBuffer = dapp.tags ? Buffer.from(dapp.tags) : Buffer.alloc(0); + const dappLinkBuffer = Buffer.from(dapp.link, 'utf8'); + const dappIconBuffer = dapp.icon ? Buffer.from(dapp.icon) : Buffer.alloc(0); + const dappTypeBuffer = Buffer.alloc(4, dapp.type); + const dappCategoryBuffer = Buffer.alloc(4, dapp.category); + + return Buffer.concat([ + dappNameBuffer, + dappDescriptionBuffer, + dappTagsBuffer, + dappLinkBuffer, + dappIconBuffer, + dappTypeBuffer, + dappCategoryBuffer, + ]); +} - return Buffer.concat([ - dappNameBuffer, - dappDescriptionBuffer, - dappTagsBuffer, dappLinkBuffer, - dappIconBuffer, - dappTypeBuffer, - dappCategoryBuffer, - ]); - } +/** + * @method isDappInTransferTransaction + * @return {Buffer} + */ - /** - * @method isDappInTransferTransaction - * @return {Buffer} - */ +function isDappInTransferTransaction(asset) { + return Buffer.from(asset.inTransfer.dappId, 'utf8'); +} - function isDappInTransferTransaction() { - return Buffer.from(transaction.asset.inTransfer.dappId); - } +/** + * @method isDappOutTransferTransaction + * @return {Buffer} + */ - /** - * @method isDappOutTransferTransaction - * @return {Buffer} - */ +function isDappOutTransferTransaction(asset) { + const dappOutAppIdBuffer = Buffer.from(asset.outTransfer.dappId, 'utf8'); + const dappOutTransactionIdBuffer = Buffer.from(asset.outTransfer.transactionId, 'utf8'); - function isDappOutTransferTransaction() { - const dappOutAppIdBuffer = Buffer.from(transaction.asset.outTransfer.dappId); - const dappOutTransactionIdBuffer = Buffer.from(transaction.asset.outTransfer.transactionId); + return Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); +} - return Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); - } +/** + * @method getAssetBytesHelper + * @return {Buffer} + */ +function getAssetBytesHelper(transaction) { const transactionType = { 0: isSendTransaction, 1: isSignatureTransaction, @@ -144,7 +142,7 @@ function getAssetBytesHelper(transaction) { 7: isDappOutTransferTransaction, }; - return transactionType[transaction.type](); + return transactionType[transaction.type](transaction.asset); } /** @@ -157,14 +155,13 @@ function getAssetBytesHelper(transaction) { export class Transaction { constructor(transaction) { - this.byteSizes = byteSizes; this.transaction = transaction; - this.transactionType = Buffer.alloc(this.byteSizes.TYPE); + this.transactionType = Buffer.alloc(BYTESIZES.TYPE); this.transactionType.writeInt8(transaction.type); - this.transactionTimestamp = Buffer.alloc(this.byteSizes.TIMESTAMP); - this.transactionTimestamp.writeIntLE(transaction.timestamp, 0, this.byteSizes.TIMESTAMP); + this.transactionTimestamp = Buffer.alloc(BYTESIZES.TIMESTAMP); + this.transactionTimestamp.writeIntLE(transaction.timestamp, 0, BYTESIZES.TIMESTAMP); this.transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); this.transactionRequesterPublicKey = transaction.requesterPublicKey @@ -175,12 +172,12 @@ export class Transaction { ? Buffer.from( bignum( transaction.recipientId.slice(0, -1), - ).toBuffer({ size: this.byteSizes.RECIPIENT_ID }), + ).toBuffer({ size: BYTESIZES.RECIPIENT_ID }), ) - : Buffer.alloc(this.byteSizes.RECIPIENT_ID).fill(0); + : Buffer.alloc(BYTESIZES.RECIPIENT_ID).fill(0); - this.transactionAmount = Buffer.alloc(this.byteSizes.AMOUNT); - this.transactionAmount.writeInt32LE(transaction.amount, 0, this.byteSizes.AMOUNT); + this.transactionAmount = Buffer.alloc(BYTESIZES.AMOUNT); + this.transactionAmount.writeInt32LE(transaction.amount, 0, BYTESIZES.AMOUNT); this.transactionAssetData = this.getAssetBytes(transaction); @@ -244,9 +241,9 @@ export class Transaction { checkTransaction() { if (this.transaction.type === 0 && this.transaction.asset.data) { - if (this.transaction.asset.data.length > byteSizes.DATA - || this.transactionAssetData.length > byteSizes.DATA) { - throw new Error(`Transaction asset data exceeds size of ${byteSizes.DATA}.`); + if (this.transaction.asset.data.length > BYTESIZES.DATA + || this.transactionAssetData.length > BYTESIZES.DATA) { + throw new Error(`Transaction asset data exceeds size of ${BYTESIZES.DATA}.`); } } } diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index e3fdb4c9e..835d6ea57 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -15,8 +15,10 @@ import { getTransactionBytes } from '../../src/transactions/transactionBytes'; describe('#getTransactionBytes', () => { + const fixedPoint = 10 ** 8; const defaultRecipient = '58191285901858109L'; const defaultSenderPublicKey = '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09'; + const defaultSenderId = '18160565574430594874L'; const defaultSenderSecondPublicKey = '0401c8ac9f29ded9e1e4d5b6b43051cb25b22f27c7b7b35092161e851946f82f'; const defaultAmount = 1000; const defaultNoAmount = 0; @@ -32,24 +34,26 @@ describe('#getTransactionBytes', () => { beforeEach(() => { defaultTransaction = { type: 0, + fee: 0.1 * fixedPoint, amount: defaultAmount, recipientId: defaultRecipient, timestamp: defaultTimestamp, asset: {}, senderPublicKey: defaultSenderPublicKey, + senderId: defaultSenderId, signature: defaultSignature, id: defaultTransactionId, }; }); - it('should return Buffer of type 0 (send LSk) transaction', () => { + it('should return Buffer of type 0 (send LSK) transaction', () => { const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAABhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); const transactionBytes = getTransactionBytes(defaultTransaction); (transactionBytes).should.be.eql(expectedBuffer); }); - it('should return Buffer of type 0 (send LSk) with data', () => { + it('should return Buffer of type 0 (send LSK) with data', () => { defaultTransaction.asset.data = 'Hello Lisk! Some data in here!...'; const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAABIZWxsbyBMaXNrISBTb21lIGRhdGEgaW4gaGVyZSEuLi5hilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); const transactionBytes = getTransactionBytes(defaultTransaction); @@ -58,7 +62,8 @@ describe('#getTransactionBytes', () => { }); it('should throw on type 0 with too much data', () => { - defaultTransaction.asset.data = '0123456789012345678901234567890123456789012345678901234567890123456789'; + const maxDataLength = 64; + defaultTransaction.asset.data = new Array(maxDataLength + 1).fill('1').join(''); (getTransactionBytes.bind(null, defaultTransaction)).should.throw('Transaction asset data exceeds size of 64.'); }); @@ -69,13 +74,33 @@ describe('#getTransactionBytes', () => { (transactionBytes).should.be.eql(expectedBuffer); }); + + it('should return Buffer from multisignature type 0 (send LSK) transaction', () => { + const multiSignatureTransaction = { + type: 0, + amount: 1000, + fee: 1 * fixedPoint, + recipientId: defaultRecipient, + senderPublicKey: defaultSenderPublicKey, + requesterPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', + timestamp: defaultTimestamp, + asset: {}, + signatures: [], + signature: defaultSignature, + id: defaultTransactionId, + }; + const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCV0DaoWM6J+ERJF2LrieK/vVCkoKDaZY5LJiiyWxF64JAM68qo00FT3oAwAAAAAAAGGKVJdSEurZPfjIgWVcYlVEvOjtfM3+bwikLuz7Gt69BRMHvlAUuwUWF7r3gV1Q9iEp5wkYGQNh5dTdR5ZUGwo=', 'base64'); + const transactionBytes = getTransactionBytes(multiSignatureTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); }); describe('signature transaction, type 1', () => { const signatureTransaction = { type: 1, amount: defaultNoAmount, - fee: 500000000, + fee: 5 * fixedPoint, recipientId: null, senderPublicKey: defaultSenderPublicKey, timestamp: defaultTimestamp, @@ -84,7 +109,7 @@ describe('#getTransactionBytes', () => { id: defaultTransactionId, }; - it('should create correct transactionBytes from signature transaction', () => { + it('should return Buffer of type 1 (register second signature) transaction', () => { const expectedBuffer = Buffer.from('AaopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAAAEAcisnyne2eHk1ba0MFHLJbIvJ8e3s1CSFh6FGUb4L2GKVJdSEurZPfjIgWVcYlVEvOjtfM3+bwikLuz7Gt69BRMHvlAUuwUWF7r3gV1Q9iEp5wkYGQNh5dTdR5ZUGwo=', 'base64'); const transactionBytes = getTransactionBytes(signatureTransaction); @@ -96,7 +121,7 @@ describe('#getTransactionBytes', () => { const delegateRegistrationTransaction = { type: 2, amount: defaultNoAmount, - fee: 2500000000, + fee: 25 * fixedPoint, recipientId: null, senderPublicKey: defaultSenderPublicKey, timestamp: defaultTimestamp, @@ -105,7 +130,7 @@ describe('#getTransactionBytes', () => { id: defaultTransactionId, }; - it('should create correct transactionBytes from delegate registration transaction', () => { + it('should return Buffer of type 2 (register delegate) transaction', () => { const expectedBuffer = Buffer.from('AqopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAABNeURlbGVnYXRlVXNlcm5hbWVhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); const transactionBytes = getTransactionBytes(delegateRegistrationTransaction); @@ -117,8 +142,8 @@ describe('#getTransactionBytes', () => { const voteTransaction = { type: 3, amount: 0, - fee: 100000000, - recipientId: '18160565574430594874L', + fee: 1 * fixedPoint, + recipientId: defaultRecipient, senderPublicKey: defaultSenderPublicKey, timestamp: defaultTimestamp, asset: { @@ -131,8 +156,8 @@ describe('#getTransactionBytes', () => { id: defaultTransactionId, }; - it('should create correct transactionBytes from vote transaction', () => { - const expectedBuffer = Buffer.from('A6opAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCfwHSivQH5c6AAAAAAAAAAArNWQwMzZhODU4Y2U4OWY4NDQ0OTE3NjJlYjg5ZTJiZmJkNTBhNGEwYTBkYTY1OGU0YjI2MjhiMjViMTE3YWUwOSswNDAxYzhhYzlmMjlkZWQ5ZTFlNGQ1YjZiNDMwNTFjYjI1YjIyZjI3YzdiN2IzNTA5MjE2MWU4NTE5NDZmODJmYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); + it('should return Buffer of type 3 (vote) transaction', () => { + const expectedBuffer = Buffer.from('A6opAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU9AAAAAAAAAAArNWQwMzZhODU4Y2U4OWY4NDQ0OTE3NjJlYjg5ZTJiZmJkNTBhNGEwYTBkYTY1OGU0YjI2MjhiMjViMTE3YWUwOSswNDAxYzhhYzlmMjlkZWQ5ZTFlNGQ1YjZiNDMwNTFjYjI1YjIyZjI3YzdiN2IzNTA5MjE2MWU4NTE5NDZmODJmYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); const transactionBytes = getTransactionBytes(voteTransaction); (transactionBytes).should.be.eql(expectedBuffer); @@ -143,7 +168,7 @@ describe('#getTransactionBytes', () => { const createMultiSignatureTransaction = { type: 4, amount: 0, - fee: 1500000000, + fee: 15 * fixedPoint, recipientId: null, senderPublicKey: defaultSenderPublicKey, timestamp: defaultTimestamp, @@ -162,40 +187,19 @@ describe('#getTransactionBytes', () => { id: defaultTransactionId, }; - const multiSignatureTransaction = { - type: 0, - amount: 1000, - fee: 10000000, - recipientId: defaultRecipient, - senderPublicKey: defaultSenderPublicKey, - requesterPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - timestamp: defaultTimestamp, - asset: {}, - signatures: [], - signature: defaultSignature, - id: defaultTransactionId, - }; - - it('should create correct transactionBytes from create multisignature transaction', () => { + it('should return Buffer from type 4 (register multisignature) transaction', () => { const expectedBuffer = Buffer.from('BKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAAACBSs1ZDAzNmE4NThjZTg5Zjg0NDQ5MTc2MmViODllMmJmYmQ1MGE0YTBhMGRhNjU4ZTRiMjYyOGIyNWIxMTdhZTA5KzA0MDFjOGFjOWYyOWRlZDllMWU0ZDViNmI0MzA1MWNiMjViMjJmMjdjN2I3YjM1MDkyMTYxZTg1MTk0NmY4MmZhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); const transactionBytes = getTransactionBytes(createMultiSignatureTransaction); (transactionBytes).should.be.eql(expectedBuffer); }); - - it('should create correct transactionBytes from multisignature send transaction', () => { - const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCV0DaoWM6J+ERJF2LrieK/vVCkoKDaZY5LJiiyWxF64JAM68qo00FT3oAwAAAAAAAGGKVJdSEurZPfjIgWVcYlVEvOjtfM3+bwikLuz7Gt69BRMHvlAUuwUWF7r3gV1Q9iEp5wkYGQNh5dTdR5ZUGwo=', 'base64'); - const transactionBytes = getTransactionBytes(multiSignatureTransaction); - - (transactionBytes).should.be.eql(expectedBuffer); - }); }); describe('dapp transaction, type 5', () => { const dappTransaction = { type: 5, amount: 0, - fee: 2500000000, + fee: 25 * fixedPoint, recipientId: null, senderPublicKey: defaultSenderPublicKey, timestamp: defaultTimestamp, @@ -226,7 +230,7 @@ describe('#getTransactionBytes', () => { const inTransferTransction = { type: 6, amount: defaultAmount, - fee: 10000000, + fee: 1 * fixedPoint, recipientId: null, senderPublicKey: defaultSenderPublicKey, timestamp: defaultTimestamp, @@ -246,7 +250,7 @@ describe('#getTransactionBytes', () => { const outTransferTransaction = { type: 7, amount: defaultAmount, - fee: 10000000, + fee: 1 * fixedPoint, recipientId: defaultRecipient, senderPublicKey: defaultSenderPublicKey, timestamp: defaultTimestamp, From 3f89acf32e233ffefc2773c99d81c9a04a01eb1c Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Fri, 22 Sep 2017 11:42:08 +0200 Subject: [PATCH 10/15] Refactor transactionBytes asset helper functions --- src/transactions/transactionBytes.js | 75 +++++++++++++--------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index ac9dbbf74..04837eaf1 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -25,51 +25,55 @@ const BYTESIZES = { DATA: 64, }; - /** - * @method isSendTransaction + * @method getAssetDataForSendTransaction + * @param transactionAsset {Object} * @return {Buffer} */ -function isSendTransaction(asset) { +function getAssetDataForSendTransaction(asset) { return asset.data ? Buffer.from(asset.data, 'utf8') : Buffer.alloc(0); } /** - * @method isSignatureTransaction + * @method getAssetDataForSignatureTransaction + * @param transactionAsset {Object} * @return {Buffer} */ -function isSignatureTransaction(asset) { +function getAssetDataForSignatureTransaction(asset) { return Buffer.from(asset.signature.publicKey, 'hex'); } /** - * @method isDelegateTransaction + * @method getAssetDataForDelegateTransaction + * @param transactionAsset {Object} * @return {Buffer} */ -function isDelegateTransaction(asset) { +function getAssetDataForDelegateTransaction(asset) { return Buffer.from(asset.delegate.username, 'utf8'); } /** - * @method isVoteTransaction + * @method getAssetDataForVotesTransaction + * @param transactionAsset {Object} * @return {Buffer} */ -function isVoteTransaction(asset) { +function getAssetDataForVotesTransaction(asset) { return Buffer.from(asset.votes.join(''), 'utf8'); } /** - * @method isMultisignatureTransaction + * @method getAssetDataForMultisignatureTransaction + * @param transactionAsset {Object} * @return {Buffer} */ -function isMultisignatureTransaction(asset) { +function getAssetDataForMultisignatureTransaction(asset) { const multisigTransactionAsset = asset.multisignature; const minBuffer = Buffer.alloc(1, multisigTransactionAsset.min); const lifetimeBuffer = Buffer.alloc(1, multisigTransactionAsset.lifetime); @@ -79,11 +83,12 @@ function isMultisignatureTransaction(asset) { } /** - * @method isDappTransaction + * @method getAssetDataForDappTransaction + * @param transactionAsset {Object} * @return {Buffer} */ -function isDappTransaction(asset) { +function getAssetDataForDappTransaction(asset) { const dapp = asset.dapp; const dappNameBuffer = Buffer.from(dapp.name, 'utf8'); const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description, 'utf8') : Buffer.alloc(0); @@ -105,20 +110,22 @@ function isDappTransaction(asset) { } /** - * @method isDappInTransferTransaction + * @method getAssetDataForDappInTransaction + * @param transactionAsset {Object} * @return {Buffer} */ -function isDappInTransferTransaction(asset) { +function getAssetDataForDappInTransaction(asset) { return Buffer.from(asset.inTransfer.dappId, 'utf8'); } /** - * @method isDappOutTransferTransaction + * @method getASsetDataForDappOutTransaction + * @param transactionAsset {Object} * @return {Buffer} */ -function isDappOutTransferTransaction(asset) { +function getAssetDataForDappOutTransaction(asset) { const dappOutAppIdBuffer = Buffer.from(asset.outTransfer.dappId, 'utf8'); const dappOutTransactionIdBuffer = Buffer.from(asset.outTransfer.transactionId, 'utf8'); @@ -127,22 +134,23 @@ function isDappOutTransferTransaction(asset) { /** * @method getAssetBytesHelper + * @param transaction {Object} * @return {Buffer} */ function getAssetBytesHelper(transaction) { - const transactionType = { - 0: isSendTransaction, - 1: isSignatureTransaction, - 2: isDelegateTransaction, - 3: isVoteTransaction, - 4: isMultisignatureTransaction, - 5: isDappTransaction, - 6: isDappInTransferTransaction, - 7: isDappOutTransferTransaction, + const assetDataGetters = { + 0: getAssetDataForSendTransaction, + 1: getAssetDataForSignatureTransaction, + 2: getAssetDataForDelegateTransaction, + 3: getAssetDataForVotesTransaction, + 4: getAssetDataForMultisignatureTransaction, + 5: getAssetDataForDappTransaction, + 6: getAssetDataForDappInTransaction, + 7: getAssetDataForDappOutTransaction, }; - return transactionType[transaction.type](transaction.asset); + return assetDataGetters[transaction.type](transaction.asset); } /** @@ -179,7 +187,7 @@ export class Transaction { this.transactionAmount = Buffer.alloc(BYTESIZES.AMOUNT); this.transactionAmount.writeInt32LE(transaction.amount, 0, BYTESIZES.AMOUNT); - this.transactionAssetData = this.getAssetBytes(transaction); + this.transactionAssetData = getAssetBytesHelper(transaction); this.transactionSignature = transaction.signature ? Buffer.from(transaction.signature, 'hex') @@ -189,7 +197,6 @@ export class Transaction { ? Buffer.from(transaction.signSignature, 'hex') : Buffer.alloc(0); - this.checkTransaction(); return this; } @@ -223,16 +230,6 @@ export class Transaction { ]); } - /** - * @method getAssetBytes - * @private - * @return {Buffer} - */ - - getAssetBytes() { - return getAssetBytesHelper(this.transaction); - } - /** * @method checkTransaction * @throws From 7786730e37b967f5f557984bc4418bb3dd9a4edd Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Fri, 22 Sep 2017 17:29:20 +0200 Subject: [PATCH 11/15] Adding further test to transactionBytes, making transactionBytes a function --- src/transactions/transactionBytes.js | 168 +++++++++++--------------- test/transactions/transactionBytes.js | 86 ++++++++++--- 2 files changed, 139 insertions(+), 115 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 04837eaf1..31b758dfe 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -14,7 +14,7 @@ */ import bignum from 'browserify-bignum'; -const BYTESIZES = { +export const BYTESIZES = { TYPE: 1, TIMESTAMP: 4, MULTISIGNATURE_PUBLICKEY: 32, @@ -27,11 +27,11 @@ const BYTESIZES = { /** * @method getAssetDataForSendTransaction - * @param transactionAsset {Object} + * @param {Object} transactionAsset * @return {Buffer} */ -function getAssetDataForSendTransaction(asset) { +export function getAssetDataForSendTransaction(asset) { return asset.data ? Buffer.from(asset.data, 'utf8') : Buffer.alloc(0); @@ -39,41 +39,41 @@ function getAssetDataForSendTransaction(asset) { /** * @method getAssetDataForSignatureTransaction - * @param transactionAsset {Object} + * @param {Object} transactionAsset * @return {Buffer} */ -function getAssetDataForSignatureTransaction(asset) { +export function getAssetDataForSignatureTransaction(asset) { return Buffer.from(asset.signature.publicKey, 'hex'); } /** * @method getAssetDataForDelegateTransaction - * @param transactionAsset {Object} + * @param {Object} transactionAsset * @return {Buffer} */ -function getAssetDataForDelegateTransaction(asset) { +export function getAssetDataForDelegateTransaction(asset) { return Buffer.from(asset.delegate.username, 'utf8'); } /** * @method getAssetDataForVotesTransaction - * @param transactionAsset {Object} + * @param {Object} transactionAsset * @return {Buffer} */ -function getAssetDataForVotesTransaction(asset) { +export function getAssetDataForVotesTransaction(asset) { return Buffer.from(asset.votes.join(''), 'utf8'); } /** * @method getAssetDataForMultisignatureTransaction - * @param transactionAsset {Object} + * @param {Object} transactionAsset * @return {Buffer} */ -function getAssetDataForMultisignatureTransaction(asset) { +export function getAssetDataForMultisignatureTransaction(asset) { const multisigTransactionAsset = asset.multisignature; const minBuffer = Buffer.alloc(1, multisigTransactionAsset.min); const lifetimeBuffer = Buffer.alloc(1, multisigTransactionAsset.lifetime); @@ -84,11 +84,11 @@ function getAssetDataForMultisignatureTransaction(asset) { /** * @method getAssetDataForDappTransaction - * @param transactionAsset {Object} + * @param {Object} transactionAsset * @return {Buffer} */ -function getAssetDataForDappTransaction(asset) { +export function getAssetDataForDappTransaction(asset) { const dapp = asset.dapp; const dappNameBuffer = Buffer.from(dapp.name, 'utf8'); const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description, 'utf8') : Buffer.alloc(0); @@ -111,21 +111,21 @@ function getAssetDataForDappTransaction(asset) { /** * @method getAssetDataForDappInTransaction - * @param transactionAsset {Object} + * @param {Object} transactionAsset * @return {Buffer} */ -function getAssetDataForDappInTransaction(asset) { +export function getAssetDataForDappInTransaction(asset) { return Buffer.from(asset.inTransfer.dappId, 'utf8'); } /** * @method getASsetDataForDappOutTransaction - * @param transactionAsset {Object} + * @param {Object} transactionAsset * @return {Buffer} */ -function getAssetDataForDappOutTransaction(asset) { +export function getAssetDataForDappOutTransaction(asset) { const dappOutAppIdBuffer = Buffer.from(asset.outTransfer.dappId, 'utf8'); const dappOutTransactionIdBuffer = Buffer.from(asset.outTransfer.transactionId, 'utf8'); @@ -134,11 +134,11 @@ function getAssetDataForDappOutTransaction(asset) { /** * @method getAssetBytesHelper - * @param transaction {Object} + * @param {Object} transaction * @return {Buffer} */ -function getAssetBytesHelper(transaction) { +export function getAssetBytesHelper(transaction) { const assetDataGetters = { 0: getAssetDataForSendTransaction, 1: getAssetDataForSignatureTransaction, @@ -153,99 +153,71 @@ function getAssetBytesHelper(transaction) { return assetDataGetters[transaction.type](transaction.asset); } +/** + * @method checkTransaction + * @throws + */ + +export function checkTransaction(transaction) { + if (transaction.asset.data) { + if (transaction.asset.data.length > BYTESIZES.DATA) { + throw new Error(`Transaction asset data exceeds size of ${BYTESIZES.DATA}.`); + } + } +} + /** * A utility class to get transaction byteSizes * -* @class Transaction +* @class TransactionBytes * @param {Object} transaction * @constructor */ -export class Transaction { - constructor(transaction) { - this.transaction = transaction; - - this.transactionType = Buffer.alloc(BYTESIZES.TYPE); - this.transactionType.writeInt8(transaction.type); - - this.transactionTimestamp = Buffer.alloc(BYTESIZES.TIMESTAMP); - this.transactionTimestamp.writeIntLE(transaction.timestamp, 0, BYTESIZES.TIMESTAMP); - - this.transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); - this.transactionRequesterPublicKey = transaction.requesterPublicKey - ? Buffer.from(transaction.requesterPublicKey, 'hex') - : Buffer.alloc(0); - - this.transactionRecipientID = transaction.recipientId - ? Buffer.from( - bignum( - transaction.recipientId.slice(0, -1), - ).toBuffer({ size: BYTESIZES.RECIPIENT_ID }), - ) - : Buffer.alloc(BYTESIZES.RECIPIENT_ID).fill(0); - - this.transactionAmount = Buffer.alloc(BYTESIZES.AMOUNT); - this.transactionAmount.writeInt32LE(transaction.amount, 0, BYTESIZES.AMOUNT); +export function getTransactionBytes(transaction) { + checkTransaction(transaction); - this.transactionAssetData = getAssetBytesHelper(transaction); + const transactionType = Buffer.alloc(BYTESIZES.TYPE); + transactionType.writeInt8(transaction.type); - this.transactionSignature = transaction.signature - ? Buffer.from(transaction.signature, 'hex') - : Buffer.alloc(0); + const transactionTimestamp = Buffer.alloc(BYTESIZES.TIMESTAMP); + transactionTimestamp.writeIntLE(transaction.timestamp, 0, BYTESIZES.TIMESTAMP); - this.transactionSecondSignature = transaction.signSignature - ? Buffer.from(transaction.signSignature, 'hex') - : Buffer.alloc(0); + const transactionSenderPublicKey = Buffer.from(transaction.senderPublicKey, 'hex'); + const transactionRequesterPublicKey = transaction.requesterPublicKey + ? Buffer.from(transaction.requesterPublicKey, 'hex') + : Buffer.alloc(0); - this.checkTransaction(); - return this; - } + const transactionRecipientID = transaction.recipientId + ? Buffer.from( + bignum( + transaction.recipientId.slice(0, -1), + ).toBuffer({ size: BYTESIZES.RECIPIENT_ID }), + ) + : Buffer.alloc(BYTESIZES.RECIPIENT_ID).fill(0); - /** - * @method get transactionBytes - * @return {Buffer} - */ + const transactionAmount = Buffer.alloc(BYTESIZES.AMOUNT); + transactionAmount.writeInt32LE(transaction.amount, 0, BYTESIZES.AMOUNT); - get transactionBytes() { - return this.concatTransactionBytes(); - } + const transactionAssetData = getAssetBytesHelper(transaction); - /** - * @method concatTransactionBytes - * @private - * @return {Buffer} - */ - - concatTransactionBytes() { - return Buffer.concat([ - this.transactionType, - this.transactionTimestamp, - this.transactionSenderPublicKey, - this.transactionRequesterPublicKey, - this.transactionRecipientID, - this.transactionAmount, - this.transactionAssetData, - this.transactionSignature, - this.transactionSecondSignature, - ]); - } + const transactionSignature = transaction.signature + ? Buffer.from(transaction.signature, 'hex') + : Buffer.alloc(0); - /** - * @method checkTransaction - * @throws - * @return {} - */ - - checkTransaction() { - if (this.transaction.type === 0 && this.transaction.asset.data) { - if (this.transaction.asset.data.length > BYTESIZES.DATA - || this.transactionAssetData.length > BYTESIZES.DATA) { - throw new Error(`Transaction asset data exceeds size of ${BYTESIZES.DATA}.`); - } - } - } -} + const transactionSecondSignature = transaction.signSignature + ? Buffer.from(transaction.signSignature, 'hex') + : Buffer.alloc(0); -export function getTransactionBytes(transaction) { - return new Transaction(transaction).transactionBytes; + return Buffer.concat([ + transactionType, + transactionTimestamp, + transactionSenderPublicKey, + transactionRequesterPublicKey, + transactionRecipientID, + transactionAmount, + transactionAssetData, + transactionSignature, + transactionSecondSignature, + ]); } diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index 835d6ea57..2e1e7a707 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -12,22 +12,34 @@ * Removal or modification of this copyright notice is prohibited. * */ -import { getTransactionBytes } from '../../src/transactions/transactionBytes'; +import { + getTransactionBytes, + getAssetDataForSendTransaction, + getAssetDataForSignatureTransaction, + getAssetDataForDelegateTransaction, + getAssetDataForVotesTransaction, + getAssetDataForMultisignatureTransaction, + getAssetDataForDappTransaction, + getAssetDataForDappInTransaction, + getAssetDataForDappOutTransaction, + getAssetBytesHelper, + checkTransaction, +} from '../../src/transactions/transactionBytes'; -describe('#getTransactionBytes', () => { - const fixedPoint = 10 ** 8; - const defaultRecipient = '58191285901858109L'; - const defaultSenderPublicKey = '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09'; - const defaultSenderId = '18160565574430594874L'; - const defaultSenderSecondPublicKey = '0401c8ac9f29ded9e1e4d5b6b43051cb25b22f27c7b7b35092161e851946f82f'; - const defaultAmount = 1000; - const defaultNoAmount = 0; - const defaultTimestamp = 141738; - const defaultTransactionId = '13987348420913138422'; - const defaultSignature = '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a'; - const defaultSecondSignature = 'b00c4ad1988bca245d74435660a278bfe6bf2f5efa8bda96d927fabf8b4f6fcfdcb2953f6abacaa119d6880987a55dea0e6354bc8366052b45fa23145522020f'; - const defaultAppId = '1234213'; +const fixedPoint = 10 ** 8; +const defaultRecipient = '58191285901858109L'; +const defaultSenderPublicKey = '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09'; +const defaultSenderId = '18160565574430594874L'; +const defaultSenderSecondPublicKey = '0401c8ac9f29ded9e1e4d5b6b43051cb25b22f27c7b7b35092161e851946f82f'; +const defaultAmount = 1000; +const defaultNoAmount = 0; +const defaultTimestamp = 141738; +const defaultTransactionId = '13987348420913138422'; +const defaultSignature = '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a'; +const defaultSecondSignature = 'b00c4ad1988bca245d74435660a278bfe6bf2f5efa8bda96d927fabf8b4f6fcfdcb2953f6abacaa119d6880987a55dea0e6354bc8366052b45fa23145522020f'; +const defaultAppId = '1234213'; +describe('#getTransactionBytes', () => { describe('send transaction, type 0', () => { let defaultTransaction; @@ -94,6 +106,14 @@ describe('#getTransactionBytes', () => { (transactionBytes).should.be.eql(expectedBuffer); }); + + it('should return Buffer of type 0 (send LSK) with additional properties', () => { + defaultTransaction.skip = false; + const expectedBuffer = Buffer.from('AKopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAABhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); + const transactionBytes = getTransactionBytes(defaultTransaction); + + (transactionBytes).should.be.eql(expectedBuffer); + }); }); describe('signature transaction, type 1', () => { @@ -218,7 +238,7 @@ describe('#getTransactionBytes', () => { id: defaultTransactionId, }; - it('should create correct transactionBytes from dapp transaction', () => { + it('should return Buffer of type 5 (register dapp) transaction', () => { const expectedBuffer = Buffer.from('BaopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAAAAAAAAAAAABMaXNrIEd1ZXN0Ym9va1RoZSBvZmZpY2lhbCBMaXNrIGd1ZXN0Ym9va2d1ZXN0Ym9vayBtZXNzYWdlIHNpZGVjaGFpbmh0dHBzOi8vZ2l0aHViLmNvbS9NYXhLSy9ndWVzdGJvb2tEYXBwL2FyY2hpdmUvbWFzdGVyLnppcGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9NYXhLSy9ndWVzdGJvb2tEYXBwL21hc3Rlci9pY29uLnBuZwAAAAAAAAAAYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); const transactionBytes = getTransactionBytes(dappTransaction); @@ -238,7 +258,7 @@ describe('#getTransactionBytes', () => { signature: defaultSignature, id: defaultTransactionId, }; - it('should create correct transactionBytes from inTransfer transaction', () => { + it('should return Buffer of type 6 (dapp inTransfer) transaction', () => { const expectedBuffer = Buffer.from('BqopAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQAAAAAAAAAA6AMAAAAAAAAxMjM0MjEzYYpUl1IS6tk9+MiBZVxiVUS86O18zf5vCKQu7Psa3r0FEwe+UBS7BRYXuveBXVD2ISnnCRgZA2Hl1N1HllQbCg==', 'base64'); const transactionBytes = getTransactionBytes(inTransferTransction); @@ -258,7 +278,7 @@ describe('#getTransactionBytes', () => { signature: defaultSignature, id: defaultTransactionId, }; - it('should create correct transactionBytes from outTransfer transaction', () => { + it('should return Buffer of type 7 (dapp outTransfer) transaction', () => { const expectedBuffer = Buffer.from('B6opAgBdA2qFjOifhESRdi64niv71QpKCg2mWOSyYoslsReuCQDOvKqNNBU96AMAAAAAAAAxMjM0MjEzMTM5ODczNDg0MjA5MTMxMzg0MjJhilSXUhLq2T34yIFlXGJVRLzo7XzN/m8IpC7s+xrevQUTB75QFLsFFhe694FdUPYhKecJGBkDYeXU3UeWVBsK', 'base64'); const transactionBytes = getTransactionBytes(outTransferTransaction); @@ -266,3 +286,35 @@ describe('#getTransactionBytes', () => { }); }); }); + +describe.only('getTransactionBytes helper functions', () => { + const defaultEmptyBuffer = Buffer.alloc(0); + describe('#getAssetDataForSendTransaction', () => { + it('should return Buffer of asset data', () => { + const expectedBuffer = Buffer.from('my data input', 'utf8'); + const assetDataBuffer = getAssetDataForSendTransaction({ + data: 'my data input', + }); + + (assetDataBuffer).should.be.eql(expectedBuffer); + }); + + it('should return empty Buffer for no asset data', () => { + const assetDataBuffer = getAssetDataForSendTransaction({}); + (assetDataBuffer).should.be.eql(defaultEmptyBuffer); + }); + }); + + describe('#getAssetDataForSignatureTransaction', () => { + it('should return Buffer of asset data', () => { + const expectedBuffer = Buffer.from(defaultSenderPublicKey, 'hex'); + const assetSignaturesPublicKeyBuffer = getAssetDataForSignatureTransaction({ + signature: { + publicKey: defaultSenderPublicKey, + }, + }); + + (assetSignaturesPublicKeyBuffer).should.be.eql(expectedBuffer); + }); + }); +}); From 31434137999ad3f6acf650cebaf2ea1098679cb6 Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Mon, 25 Sep 2017 16:49:14 +0200 Subject: [PATCH 12/15] Adds test for helper functions transactionBytes --- src/transactions/transactionBytes.js | 88 +++++++--- test/transactions/transactionBytes.js | 240 +++++++++++++++++++++++++- 2 files changed, 295 insertions(+), 33 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 31b758dfe..8ce927dc9 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -25,15 +25,33 @@ export const BYTESIZES = { DATA: 64, }; +/** + * @method checkRequiredFields + * @param {Array} requiredFields + * @param {Object} data + * @throws + * @return {Boolean} + */ + +export function checkRequiredFields(requiredFields, data) { + requiredFields.forEach((parameter) => { + const dataFields = Object.keys(data); + if (!dataFields.includes(parameter)) { + throw new Error(`${parameter} is a required parameter.`); + } + }); + return true; +} + /** * @method getAssetDataForSendTransaction * @param {Object} transactionAsset * @return {Buffer} */ -export function getAssetDataForSendTransaction(asset) { - return asset.data - ? Buffer.from(asset.data, 'utf8') +export function getAssetDataForSendTransaction({ data }) { + return data + ? Buffer.from(data, 'utf8') : Buffer.alloc(0); } @@ -43,8 +61,10 @@ export function getAssetDataForSendTransaction(asset) { * @return {Buffer} */ -export function getAssetDataForSignatureTransaction(asset) { - return Buffer.from(asset.signature.publicKey, 'hex'); +export function getAssetDataForSignatureTransaction({ signature }) { + checkRequiredFields(['publicKey'], signature); + const { publicKey } = signature; + return Buffer.from(publicKey, 'hex'); } /** @@ -53,8 +73,10 @@ export function getAssetDataForSignatureTransaction(asset) { * @return {Buffer} */ -export function getAssetDataForDelegateTransaction(asset) { - return Buffer.from(asset.delegate.username, 'utf8'); +export function getAssetDataForDelegateTransaction({ delegate }) { + checkRequiredFields(['username'], delegate); + const { username } = delegate; + return Buffer.from(username, 'utf8'); } /** @@ -63,8 +85,11 @@ export function getAssetDataForDelegateTransaction(asset) { * @return {Buffer} */ -export function getAssetDataForVotesTransaction(asset) { - return Buffer.from(asset.votes.join(''), 'utf8'); +export function getAssetDataForVotesTransaction({ votes }) { + if (!Array.isArray(votes)) { + throw new Error('votes parameter must be an Array.'); + } + return Buffer.from(votes.join(''), 'utf8'); } /** @@ -73,11 +98,12 @@ export function getAssetDataForVotesTransaction(asset) { * @return {Buffer} */ -export function getAssetDataForMultisignatureTransaction(asset) { - const multisigTransactionAsset = asset.multisignature; - const minBuffer = Buffer.alloc(1, multisigTransactionAsset.min); - const lifetimeBuffer = Buffer.alloc(1, multisigTransactionAsset.lifetime); - const keysgroupBuffer = Buffer.from(multisigTransactionAsset.keysgroup.join(''), 'utf8'); +export function getAssetDataForMultisignatureTransaction({ multisignature }) { + checkRequiredFields(['min', 'lifetime', 'keysgroup'], multisignature); + const { min, lifetime, keysgroup } = multisignature; + const minBuffer = Buffer.alloc(1, min); + const lifetimeBuffer = Buffer.alloc(1, lifetime); + const keysgroupBuffer = Buffer.from(keysgroup.join(''), 'utf8'); return Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); } @@ -88,15 +114,17 @@ export function getAssetDataForMultisignatureTransaction(asset) { * @return {Buffer} */ -export function getAssetDataForDappTransaction(asset) { - const dapp = asset.dapp; - const dappNameBuffer = Buffer.from(dapp.name, 'utf8'); - const dappDescriptionBuffer = dapp.description ? Buffer.from(dapp.description, 'utf8') : Buffer.alloc(0); - const dappTagsBuffer = dapp.tags ? Buffer.from(dapp.tags) : Buffer.alloc(0); - const dappLinkBuffer = Buffer.from(dapp.link, 'utf8'); - const dappIconBuffer = dapp.icon ? Buffer.from(dapp.icon) : Buffer.alloc(0); - const dappTypeBuffer = Buffer.alloc(4, dapp.type); - const dappCategoryBuffer = Buffer.alloc(4, dapp.category); +export function getAssetDataForDappTransaction({ dapp }) { + checkRequiredFields(['name', 'link', 'type', 'category'], dapp); + const { name, description, tags, link, icon, type, category } = dapp; + const dappNameBuffer = Buffer.from(name, 'utf8'); + const dappLinkBuffer = Buffer.from(link, 'utf8'); + const dappTypeBuffer = Buffer.alloc(4, type); + const dappCategoryBuffer = Buffer.alloc(4, category); + + const dappDescriptionBuffer = description ? Buffer.from(description, 'utf8') : Buffer.alloc(0); + const dappTagsBuffer = tags ? Buffer.from(tags, 'utf8') : Buffer.alloc(0); + const dappIconBuffer = icon ? Buffer.from(icon, 'utf8') : Buffer.alloc(0); return Buffer.concat([ dappNameBuffer, @@ -115,8 +143,10 @@ export function getAssetDataForDappTransaction(asset) { * @return {Buffer} */ -export function getAssetDataForDappInTransaction(asset) { - return Buffer.from(asset.inTransfer.dappId, 'utf8'); +export function getAssetDataForDappInTransaction({ inTransfer }) { + checkRequiredFields(['dappId'], inTransfer); + const { dappId } = inTransfer; + return Buffer.from(dappId, 'utf8'); } /** @@ -125,9 +155,11 @@ export function getAssetDataForDappInTransaction(asset) { * @return {Buffer} */ -export function getAssetDataForDappOutTransaction(asset) { - const dappOutAppIdBuffer = Buffer.from(asset.outTransfer.dappId, 'utf8'); - const dappOutTransactionIdBuffer = Buffer.from(asset.outTransfer.transactionId, 'utf8'); +export function getAssetDataForDappOutTransaction({ outTransfer }) { + checkRequiredFields(['dappId', 'transactionId'], outTransfer); + const { dappId, transactionId } = outTransfer; + const dappOutAppIdBuffer = Buffer.from(dappId, 'utf8'); + const dappOutTransactionIdBuffer = Buffer.from(transactionId, 'utf8'); return Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); } diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index 2e1e7a707..9d23342ca 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -22,8 +22,8 @@ import { getAssetDataForDappTransaction, getAssetDataForDappInTransaction, getAssetDataForDappOutTransaction, - getAssetBytesHelper, checkTransaction, + checkRequiredFields, } from '../../src/transactions/transactionBytes'; const fixedPoint = 10 ** 8; @@ -38,6 +38,7 @@ const defaultTransactionId = '13987348420913138422'; const defaultSignature = '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a'; const defaultSecondSignature = 'b00c4ad1988bca245d74435660a278bfe6bf2f5efa8bda96d927fabf8b4f6fcfdcb2953f6abacaa119d6880987a55dea0e6354bc8366052b45fa23145522020f'; const defaultAppId = '1234213'; +const defaultDelegateUsername = 'MyDelegateUsername'; describe('#getTransactionBytes', () => { describe('send transaction, type 0', () => { @@ -145,7 +146,7 @@ describe('#getTransactionBytes', () => { recipientId: null, senderPublicKey: defaultSenderPublicKey, timestamp: defaultTimestamp, - asset: { delegate: { username: 'MyDelegateUsername' } }, + asset: { delegate: { username: defaultDelegateUsername } }, signature: defaultSignature, id: defaultTransactionId, }; @@ -287,10 +288,32 @@ describe('#getTransactionBytes', () => { }); }); -describe.only('getTransactionBytes helper functions', () => { +describe('getTransactionBytes helper functions', () => { + describe('#checkRequiredFields', () => { + const arrayToCheck = ['OneValue', 'SecondValue', 'ThirdValue']; + it('should accept array and object to check for required fields', () => { + const objectParameter = { + OneValue: '1', + SecondValue: '2', + ThirdValue: '3', + }; + + (checkRequiredFields(arrayToCheck, objectParameter)).should.be.true(); + }); + + it('should throw on missing value', () => { + const objectParameter = { + OneValue: '1', + SecondValue: '2', + }; + + (checkRequiredFields.bind(null, arrayToCheck, objectParameter)).should.throw('ThirdValue is a required parameter.'); + }); + }); + const defaultEmptyBuffer = Buffer.alloc(0); describe('#getAssetDataForSendTransaction', () => { - it('should return Buffer of asset data', () => { + it('should return Buffer for data asset', () => { const expectedBuffer = Buffer.from('my data input', 'utf8'); const assetDataBuffer = getAssetDataForSendTransaction({ data: 'my data input', @@ -306,7 +329,7 @@ describe.only('getTransactionBytes helper functions', () => { }); describe('#getAssetDataForSignatureTransaction', () => { - it('should return Buffer of asset data', () => { + it('should return Buffer for signature asset', () => { const expectedBuffer = Buffer.from(defaultSenderPublicKey, 'hex'); const assetSignaturesPublicKeyBuffer = getAssetDataForSignatureTransaction({ signature: { @@ -316,5 +339,212 @@ describe.only('getTransactionBytes helper functions', () => { (assetSignaturesPublicKeyBuffer).should.be.eql(expectedBuffer); }); + + it('should throw on missing publicKey in the signature asset', () => { + (getAssetDataForSignatureTransaction.bind(null, { signature: {} })).should.throw( + 'publicKey is a required parameter.', + ); + }); + }); + + describe('#getAssetDataForDelegateTransaction', () => { + it('should return Buffer for delegate asset', () => { + const expectedBuffer = Buffer.from(defaultDelegateUsername, 'utf8'); + const assetDelegateUsernameBuffer = getAssetDataForDelegateTransaction({ + delegate: { + username: defaultDelegateUsername, + }, + }); + + (assetDelegateUsernameBuffer).should.be.eql(expectedBuffer); + }); + it('should throw on missing username in the delegate asset', () => { + (getAssetDataForDelegateTransaction.bind(null, { delegate: {} })).should.throw( + 'username is a required parameter.', + ); + }); + }); + + describe('#getAssetDataForVotesTransaction', () => { + it('should return Buffer for votes asset', () => { + const votesAsset = { + votes: [ + `+${defaultSenderPublicKey}`, + `+${defaultSenderSecondPublicKey}`, + ], + }; + const expectedBuffer = Buffer.from(`+${defaultSenderPublicKey}+${defaultSenderSecondPublicKey}`, 'utf8'); + const assetVoteBuffer = getAssetDataForVotesTransaction(votesAsset); + + (assetVoteBuffer).should.be.eql(expectedBuffer); + }); + + it('should throw on missing votes in the vote asset', () => { + (getAssetDataForVotesTransaction.bind(null, { votes: {} })).should.throw( + 'votes parameter must be an Array.', + ); + }); + }); + + describe('#getAssetDataForMultisignatureTransaction', () => { + const min = 2; + const lifetime = 5; + const keysgroup = ['+123456789', '-987654321']; + let multisignatureAsset; + beforeEach(() => { + multisignatureAsset = { + multisignature: { + min, + lifetime, + keysgroup, + }, + }; + }); + it('should return Buffer for multisignature asset', () => { + const minBuffer = Buffer.alloc(1, min); + const lifetimeBuffer = Buffer.alloc(1, lifetime); + const keysgroupBuffer = Buffer.from('+123456789-987654321', 'utf8'); + + const expectedBuffer = Buffer.concat([minBuffer, lifetimeBuffer, keysgroupBuffer]); + const multisignatureBuffer = getAssetDataForMultisignatureTransaction(multisignatureAsset); + + (multisignatureBuffer).should.be.eql(expectedBuffer); + }); + + it('should throw on missing required parameters', () => { + const requiredProperties = ['min', 'lifetime', 'keysgroup']; + + requiredProperties.forEach((parameter) => { + const multisigAsset = Object.assign({}, multisignatureAsset.multisignature); + delete multisigAsset[parameter]; + (getAssetDataForMultisignatureTransaction.bind(null, { multisignature: multisigAsset })).should.throw(`${parameter} is a required parameter.`); + }); + }); + }); + + describe('#getAssetDataForDappTransaction', () => { + const defaultCategory = 0; + const defaultDappName = 'Lisk Guestbook'; + const defaultDescription = 'The official Lisk guestbook'; + const defaultTags = 'guestbook message sidechain'; + const defaultType = 0; + const defaultLink = 'https://github.com/MaxKK/guestbookDapp/archive/master.zip'; + const defaultIcon = 'https://raw.githubusercontent.com/MaxKK/guestbookDapp/master/icon.png'; + const dappNameBuffer = Buffer.from(defaultDappName, 'utf8'); + const dappDescriptionBuffer = Buffer.from(defaultDescription, 'utf8'); + const dappTagsBuffer = Buffer.from(defaultTags, 'utf8'); + const dappLinkBuffer = Buffer.from(defaultLink, 'utf8'); + const dappIconBuffer = Buffer.from(defaultIcon, 'utf8'); + const dappTypeBuffer = Buffer.alloc(4, defaultType); + const dappCategoryBuffer = Buffer.alloc(4, defaultCategory); + it('should return Buffer for create dapp asset', () => { + const dappAsset = { + dapp: { + category: defaultCategory, + name: defaultDappName, + description: defaultDescription, + tags: defaultTags, + type: defaultType, + link: defaultLink, + icon: defaultIcon, + }, + }; + + const expectedBuffer = Buffer.concat([ + dappNameBuffer, + dappDescriptionBuffer, + dappTagsBuffer, + dappLinkBuffer, + dappIconBuffer, + dappTypeBuffer, + dappCategoryBuffer, + ]); + const dappBuffer = getAssetDataForDappTransaction(dappAsset); + + (dappBuffer).should.be.eql(expectedBuffer); + }); + + it('should throw for create dapp asset without required fields', () => { + const dapp = { + category: defaultCategory, + name: defaultDappName, + description: defaultDescription, + tags: defaultTags, + type: defaultType, + link: defaultLink, + icon: defaultIcon, + }; + const requiredProperties = ['name', 'link', 'type', 'category']; + + requiredProperties.forEach((parameter) => { + const dappClone = Object.assign({}, dapp); + delete dappClone[parameter]; + (getAssetDataForDappTransaction.bind(null, { dapp: dappClone })).should.throw(`${parameter} is a required parameter.`); + }); + }); + }); + + describe('#getAssetDataForDappInTransaction', () => { + it('should return Buffer for dappIn asset', () => { + const dappInAsset = { + inTransfer: { + dappId: defaultAppId, + }, + }; + const expectedBuffer = Buffer.from(defaultAppId, 'utf8'); + const dappInTransferBuffer = getAssetDataForDappInTransaction(dappInAsset); + + (dappInTransferBuffer).should.be.eql(expectedBuffer); + }); + + it('should throw on missing votes in the vote asset', () => { + (getAssetDataForDappInTransaction.bind(null, { inTransfer: {} })).should.throw( + 'dappId is a required parameter.', + ); + }); + }); + + describe('#getAssetDataForDappOutTransaction', () => { + it('should return Buffer for dappOut asset', () => { + const dappOutAsset = { + outTransfer: { + dappId: defaultAppId, + transactionId: defaultTransactionId, + }, + }; + const dappIdBuffer = Buffer.from(defaultAppId, 'utf8'); + const transactionIdBuffer = Buffer.from(defaultTransactionId); + const expectedBuffer = Buffer.concat([dappIdBuffer, transactionIdBuffer]); + const dappOutTransferBuffer = getAssetDataForDappOutTransaction(dappOutAsset); + + (dappOutTransferBuffer).should.be.eql(expectedBuffer); + }); + + it('should throw on missing votes in the vote asset', () => { + (getAssetDataForDappOutTransaction.bind(null, { outTransfer: {} })).should.throw( + 'dappId is a required parameter.', + ); + }); + }); + + describe('#checkTransaction', () => { + it('should throw on too many data in send asset', () => { + const maxDataLength = 64; + const defaultTransaction = { + type: 0, + fee: 0.1 * fixedPoint, + amount: defaultAmount, + recipientId: defaultRecipient, + timestamp: defaultTimestamp, + asset: { + data: new Array(maxDataLength + 1).fill('1').join(''), + }, + senderPublicKey: defaultSenderPublicKey, + senderId: defaultSenderId, + signature: defaultSignature, + id: defaultTransactionId, + }; + (checkTransaction.bind(null, defaultTransaction)).should.throw('Transaction asset data exceeds size of 64.'); + }); }); }); From 822ec6e8143c752624c96d9b493ac8869930004b Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Mon, 25 Sep 2017 17:21:23 +0200 Subject: [PATCH 13/15] Add required transaction parameter --- src/transactions/transactionBytes.js | 8 ++++++++ test/transactions/transactionBytes.js | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 8ce927dc9..3d29dca3c 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -198,6 +198,13 @@ export function checkTransaction(transaction) { } } +const REQUIRED_TRANSACTION_PARAMETER = [ + 'type', + 'timestamp', + 'senderPublicKey', + 'amount', +]; + /** * A utility class to get transaction byteSizes * @@ -208,6 +215,7 @@ export function checkTransaction(transaction) { export function getTransactionBytes(transaction) { checkTransaction(transaction); + checkRequiredFields(REQUIRED_TRANSACTION_PARAMETER, transaction); const transactionType = Buffer.alloc(BYTESIZES.TYPE); transactionType.writeInt8(transaction.type); diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index 9d23342ca..f62d52a90 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -115,6 +115,16 @@ describe('#getTransactionBytes', () => { (transactionBytes).should.be.eql(expectedBuffer); }); + + it('should throw on missing required parameters', () => { + const requiredProperties = ['type', 'timestamp', 'senderPublicKey', 'amount']; + + requiredProperties.forEach((parameter) => { + const defaultTransactionClone = Object.assign({}, defaultTransaction); + delete defaultTransactionClone[parameter]; + (getTransactionBytes.bind(null, defaultTransactionClone)).should.throw(`${parameter} is a required parameter.`); + }); + }); }); describe('signature transaction, type 1', () => { From 65c2d54858144e72817fe38f841d2e59bef2f21a Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Tue, 26 Sep 2017 14:31:24 +0200 Subject: [PATCH 14/15] transactionBytes after review optimazations --- src/crypto/convert.js | 2 +- src/crypto/hash.js | 2 +- src/crypto/sign.js | 2 +- src/transactions/transactionBytes.js | 84 +++++++++++++-------------- src/transactions/utils.js | 13 +++++ test/transactions/transactionBytes.js | 67 ++++++++++++++++----- test/transactions/utils.js | 17 ++++++ 7 files changed, 126 insertions(+), 61 deletions(-) diff --git a/src/crypto/convert.js b/src/crypto/convert.js index d273ddfb5..c0394517b 100644 --- a/src/crypto/convert.js +++ b/src/crypto/convert.js @@ -15,7 +15,7 @@ import bignum from 'browserify-bignum'; import ed2curve from 'ed2curve'; import { getSha256Hash } from './hash'; -import { getTransactionBytes } from '../transactions/transactionBytes'; +import getTransactionBytes from '../transactions/transactionBytes'; /** * @method bufferToHex diff --git a/src/crypto/hash.js b/src/crypto/hash.js index e0d80537f..4e7406be3 100644 --- a/src/crypto/hash.js +++ b/src/crypto/hash.js @@ -12,7 +12,7 @@ * Removal or modification of this copyright notice is prohibited. * */ -import { getTransactionBytes } from './../transactions/transactionBytes'; +import getTransactionBytes from './../transactions/transactionBytes'; /** * @method getSha256Hash diff --git a/src/crypto/sign.js b/src/crypto/sign.js index b2d2e025b..0b15e97fa 100644 --- a/src/crypto/sign.js +++ b/src/crypto/sign.js @@ -13,7 +13,7 @@ * */ import crypto from 'crypto'; -import { getTransactionBytes } from '../transactions/transactionBytes'; +import getTransactionBytes from '../transactions/transactionBytes'; import { hexToBuffer, bufferToHex, diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index 3d29dca3c..b4fd149e7 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -13,6 +13,7 @@ * */ import bignum from 'browserify-bignum'; +import { isValidValue } from './utils'; export const BYTESIZES = { TYPE: 1, @@ -34,9 +35,9 @@ export const BYTESIZES = { */ export function checkRequiredFields(requiredFields, data) { + const dataFields = Object.keys(data); requiredFields.forEach((parameter) => { - const dataFields = Object.keys(data); - if (!dataFields.includes(parameter)) { + if (!dataFields.includes(parameter.toString()) || !isValidValue(data[parameter])) { throw new Error(`${parameter} is a required parameter.`); } }); @@ -117,23 +118,23 @@ export function getAssetDataForMultisignatureTransaction({ multisignature }) { export function getAssetDataForDappTransaction({ dapp }) { checkRequiredFields(['name', 'link', 'type', 'category'], dapp); const { name, description, tags, link, icon, type, category } = dapp; - const dappNameBuffer = Buffer.from(name, 'utf8'); - const dappLinkBuffer = Buffer.from(link, 'utf8'); - const dappTypeBuffer = Buffer.alloc(4, type); - const dappCategoryBuffer = Buffer.alloc(4, category); + const nameBuffer = Buffer.from(name, 'utf8'); + const linkBuffer = Buffer.from(link, 'utf8'); + const typeBuffer = Buffer.alloc(4, type); + const categoryBuffer = Buffer.alloc(4, category); - const dappDescriptionBuffer = description ? Buffer.from(description, 'utf8') : Buffer.alloc(0); - const dappTagsBuffer = tags ? Buffer.from(tags, 'utf8') : Buffer.alloc(0); - const dappIconBuffer = icon ? Buffer.from(icon, 'utf8') : Buffer.alloc(0); + const descriptionBuffer = description ? Buffer.from(description, 'utf8') : Buffer.alloc(0); + const tagsBuffer = tags ? Buffer.from(tags, 'utf8') : Buffer.alloc(0); + const iconBuffer = icon ? Buffer.from(icon, 'utf8') : Buffer.alloc(0); return Buffer.concat([ - dappNameBuffer, - dappDescriptionBuffer, - dappTagsBuffer, - dappLinkBuffer, - dappIconBuffer, - dappTypeBuffer, - dappCategoryBuffer, + nameBuffer, + descriptionBuffer, + tagsBuffer, + linkBuffer, + iconBuffer, + typeBuffer, + categoryBuffer, ]); } @@ -158,19 +159,19 @@ export function getAssetDataForDappInTransaction({ inTransfer }) { export function getAssetDataForDappOutTransaction({ outTransfer }) { checkRequiredFields(['dappId', 'transactionId'], outTransfer); const { dappId, transactionId } = outTransfer; - const dappOutAppIdBuffer = Buffer.from(dappId, 'utf8'); - const dappOutTransactionIdBuffer = Buffer.from(transactionId, 'utf8'); + const outAppIdBuffer = Buffer.from(dappId, 'utf8'); + const outTransactionIdBuffer = Buffer.from(transactionId, 'utf8'); - return Buffer.concat([dappOutAppIdBuffer, dappOutTransactionIdBuffer]); + return Buffer.concat([outAppIdBuffer, outTransactionIdBuffer]); } /** - * @method getAssetBytesHelper + * @method getAssetBytes * @param {Object} transaction * @return {Buffer} */ -export function getAssetBytesHelper(transaction) { +export function getAssetBytes(transaction) { const assetDataGetters = { 0: getAssetDataForSendTransaction, 1: getAssetDataForSignatureTransaction, @@ -185,41 +186,38 @@ export function getAssetBytesHelper(transaction) { return assetDataGetters[transaction.type](transaction.asset); } +const REQUIRED_TRANSACTION_PARAMETERS = [ + 'type', + 'timestamp', + 'senderPublicKey', + 'amount', +]; + /** * @method checkTransaction * @throws */ export function checkTransaction(transaction) { - if (transaction.asset.data) { - if (transaction.asset.data.length > BYTESIZES.DATA) { - throw new Error(`Transaction asset data exceeds size of ${BYTESIZES.DATA}.`); - } + checkRequiredFields(REQUIRED_TRANSACTION_PARAMETERS, transaction); + const { asset: { data } } = transaction; + if (data && data.length > BYTESIZES.DATA) { + throw new Error(`Transaction asset data exceeds size of ${BYTESIZES.DATA}.`); } + return true; } -const REQUIRED_TRANSACTION_PARAMETER = [ - 'type', - 'timestamp', - 'senderPublicKey', - 'amount', -]; - /** -* A utility class to get transaction byteSizes +* A utility method to get transaction byteSizes * -* @class TransactionBytes +* @method TransactionBytes * @param {Object} transaction -* @constructor +* */ -export function getTransactionBytes(transaction) { +export default function getTransactionBytes(transaction) { checkTransaction(transaction); - checkRequiredFields(REQUIRED_TRANSACTION_PARAMETER, transaction); - - const transactionType = Buffer.alloc(BYTESIZES.TYPE); - transactionType.writeInt8(transaction.type); - + const transactionType = Buffer.alloc(BYTESIZES.TYPE, transaction.type); const transactionTimestamp = Buffer.alloc(BYTESIZES.TIMESTAMP); transactionTimestamp.writeIntLE(transaction.timestamp, 0, BYTESIZES.TIMESTAMP); @@ -234,12 +232,12 @@ export function getTransactionBytes(transaction) { transaction.recipientId.slice(0, -1), ).toBuffer({ size: BYTESIZES.RECIPIENT_ID }), ) - : Buffer.alloc(BYTESIZES.RECIPIENT_ID).fill(0); + : Buffer.alloc(BYTESIZES.RECIPIENT_ID); const transactionAmount = Buffer.alloc(BYTESIZES.AMOUNT); transactionAmount.writeInt32LE(transaction.amount, 0, BYTESIZES.AMOUNT); - const transactionAssetData = getAssetBytesHelper(transaction); + const transactionAssetData = getAssetBytes(transaction); const transactionSignature = transaction.signature ? Buffer.from(transaction.signature, 'hex') diff --git a/src/transactions/utils.js b/src/transactions/utils.js index 2ef3aa25d..74dd77ae4 100644 --- a/src/transactions/utils.js +++ b/src/transactions/utils.js @@ -35,6 +35,19 @@ const prepareTransaction = (transaction, secret, secondSecret) => { return transactionWithId; }; +const isValidValue = (value) => { + if ( + // eslint-disable-next-line no-self-compare + value !== value + || value === undefined + || value === false + ) { + return false; + } + return true; +}; + module.exports = { prepareTransaction, + isValidValue, }; diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index f62d52a90..f9b240151 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -12,8 +12,7 @@ * Removal or modification of this copyright notice is prohibited. * */ -import { - getTransactionBytes, +import getTransactionBytes, { getAssetDataForSendTransaction, getAssetDataForSignatureTransaction, getAssetDataForDelegateTransaction, @@ -125,6 +124,16 @@ describe('#getTransactionBytes', () => { (getTransactionBytes.bind(null, defaultTransactionClone)).should.throw(`${parameter} is a required parameter.`); }); }); + + it('should throw on required parameters as undefined', () => { + const requiredProperties = ['type', 'timestamp', 'senderPublicKey', 'amount']; + + requiredProperties.forEach((parameter) => { + const defaultTransactionClone = Object.assign({}, defaultTransaction); + defaultTransactionClone[parameter] = undefined; + (getTransactionBytes.bind(null, defaultTransactionClone)).should.throw(`${parameter} is a required parameter.`); + }); + }); }); describe('signature transaction, type 1', () => { @@ -298,7 +307,7 @@ describe('#getTransactionBytes', () => { }); }); -describe('getTransactionBytes helper functions', () => { +describe('getTransactionBytes functions', () => { describe('#checkRequiredFields', () => { const arrayToCheck = ['OneValue', 'SecondValue', 'ThirdValue']; it('should accept array and object to check for required fields', () => { @@ -319,10 +328,31 @@ describe('getTransactionBytes helper functions', () => { (checkRequiredFields.bind(null, arrayToCheck, objectParameter)).should.throw('ThirdValue is a required parameter.'); }); + + it('should work with non-string keys', () => { + const arrayToCheckNonString = [0, 1, 2, 3]; + const objectParameter = { + 0: 0, + 1: 10, + 2: 20, + 3: 30, + }; + (checkRequiredFields(arrayToCheckNonString, objectParameter)).should.be.true(); + }); + + it('should work with non-string keys', () => { + const arrayToCheckNonString = [0, 1, 2, 3]; + const objectParameter = { + 0: 0, + 1: 10, + 2: 20, + }; + (checkRequiredFields.bind(null, arrayToCheckNonString, objectParameter)).should.throw('3 is a required parameter.'); + }); }); - const defaultEmptyBuffer = Buffer.alloc(0); describe('#getAssetDataForSendTransaction', () => { + const defaultEmptyBuffer = Buffer.alloc(0); it('should return Buffer for data asset', () => { const expectedBuffer = Buffer.from('my data input', 'utf8'); const assetDataBuffer = getAssetDataForSendTransaction({ @@ -440,11 +470,11 @@ describe('getTransactionBytes helper functions', () => { const defaultType = 0; const defaultLink = 'https://github.com/MaxKK/guestbookDapp/archive/master.zip'; const defaultIcon = 'https://raw.githubusercontent.com/MaxKK/guestbookDapp/master/icon.png'; - const dappNameBuffer = Buffer.from(defaultDappName, 'utf8'); - const dappDescriptionBuffer = Buffer.from(defaultDescription, 'utf8'); - const dappTagsBuffer = Buffer.from(defaultTags, 'utf8'); - const dappLinkBuffer = Buffer.from(defaultLink, 'utf8'); - const dappIconBuffer = Buffer.from(defaultIcon, 'utf8'); + const dappNameBuffer = Buffer.from('TGlzayBHdWVzdGJvb2s=', 'base64'); + const dappDescriptionBuffer = Buffer.from('VGhlIG9mZmljaWFsIExpc2sgZ3Vlc3Rib29r', 'base64'); + const dappTagsBuffer = Buffer.from('Z3Vlc3Rib29rIG1lc3NhZ2Ugc2lkZWNoYWlu', 'base64'); + const dappLinkBuffer = Buffer.from('aHR0cHM6Ly9naXRodWIuY29tL01heEtLL2d1ZXN0Ym9va0RhcHAvYXJjaGl2ZS9tYXN0ZXIuemlw', 'base64'); + const dappIconBuffer = Buffer.from('aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL01heEtLL2d1ZXN0Ym9va0RhcHAvbWFzdGVyL2ljb24ucG5n', 'base64'); const dappTypeBuffer = Buffer.alloc(4, defaultType); const dappCategoryBuffer = Buffer.alloc(4, defaultCategory); it('should return Buffer for create dapp asset', () => { @@ -538,23 +568,30 @@ describe('getTransactionBytes helper functions', () => { }); describe('#checkTransaction', () => { - it('should throw on too many data in send asset', () => { - const maxDataLength = 64; - const defaultTransaction = { + const maxDataLength = 64; + let defaultTransaction; + beforeEach(() => { + defaultTransaction = { type: 0, fee: 0.1 * fixedPoint, amount: defaultAmount, recipientId: defaultRecipient, timestamp: defaultTimestamp, - asset: { - data: new Array(maxDataLength + 1).fill('1').join(''), - }, + asset: {}, senderPublicKey: defaultSenderPublicKey, senderId: defaultSenderId, signature: defaultSignature, id: defaultTransactionId, }; + }); + it('should throw on too many data in send asset', () => { + defaultTransaction.asset.data = new Array(maxDataLength + 1).fill('1').join(''); (checkTransaction.bind(null, defaultTransaction)).should.throw('Transaction asset data exceeds size of 64.'); }); + + it('should return true on asset data exactly at max data length', () => { + defaultTransaction.asset.data = new Array(maxDataLength).fill('1').join(''); + (checkTransaction(defaultTransaction)).should.be.true(); + }); }); }); diff --git a/test/transactions/utils.js b/test/transactions/utils.js index f01e47d52..9ef01b481 100644 --- a/test/transactions/utils.js +++ b/test/transactions/utils.js @@ -81,4 +81,21 @@ describe('transactions utils module', () => { }); }); }); + + describe('isInvalidValue', () => { + it('should return false on invalid values', () => { + const allInvalidValues = [NaN, false, undefined]; + allInvalidValues.forEach((value) => { + const invalid = utils.isValidValue(value); + (invalid).should.be.false(); + }); + }); + it('should return true on valid values', () => { + const exampleValidValues = ['123', 123, { 1: 2, 3: 4 }, [1, 2, 3]]; + exampleValidValues.forEach((value) => { + const valid = utils.isValidValue(value); + (valid).should.be.true(); + }); + }); + }); }); From f91106b608c010b184a97ed0f3bf714063c40a32 Mon Sep 17 00:00:00 2001 From: Tobias Schwarz Date: Tue, 26 Sep 2017 15:03:28 +0200 Subject: [PATCH 15/15] Refactor isValidValue and test for isValidValue utils --- src/transactions/transactionBytes.js | 4 ++-- src/transactions/utils.js | 12 +----------- test/transactions/transactionBytes.js | 25 +++---------------------- 3 files changed, 6 insertions(+), 35 deletions(-) diff --git a/src/transactions/transactionBytes.js b/src/transactions/transactionBytes.js index b4fd149e7..f39e7d4f1 100644 --- a/src/transactions/transactionBytes.js +++ b/src/transactions/transactionBytes.js @@ -151,7 +151,7 @@ export function getAssetDataForDappInTransaction({ inTransfer }) { } /** - * @method getASsetDataForDappOutTransaction + * @method getAssetDataForDappOutTransaction * @param {Object} transactionAsset * @return {Buffer} */ @@ -208,7 +208,7 @@ export function checkTransaction(transaction) { } /** -* A utility method to get transaction byteSizes +* A utility method to get transaction bytes * * @method TransactionBytes * @param {Object} transaction diff --git a/src/transactions/utils.js b/src/transactions/utils.js index 74dd77ae4..1bb2c8bef 100644 --- a/src/transactions/utils.js +++ b/src/transactions/utils.js @@ -35,17 +35,7 @@ const prepareTransaction = (transaction, secret, secondSecret) => { return transactionWithId; }; -const isValidValue = (value) => { - if ( - // eslint-disable-next-line no-self-compare - value !== value - || value === undefined - || value === false - ) { - return false; - } - return true; -}; +const isValidValue = value => ![undefined, false, NaN].includes(value); module.exports = { prepareTransaction, diff --git a/test/transactions/transactionBytes.js b/test/transactions/transactionBytes.js index f9b240151..e13d71bae 100644 --- a/test/transactions/transactionBytes.js +++ b/test/transactions/transactionBytes.js @@ -309,12 +309,13 @@ describe('#getTransactionBytes', () => { describe('getTransactionBytes functions', () => { describe('#checkRequiredFields', () => { - const arrayToCheck = ['OneValue', 'SecondValue', 'ThirdValue']; + const arrayToCheck = ['OneValue', 'SecondValue', 'ThirdValue', 1]; it('should accept array and object to check for required fields', () => { const objectParameter = { OneValue: '1', SecondValue: '2', ThirdValue: '3', + 1: 10, }; (checkRequiredFields(arrayToCheck, objectParameter)).should.be.true(); @@ -324,30 +325,10 @@ describe('getTransactionBytes functions', () => { const objectParameter = { OneValue: '1', SecondValue: '2', - }; - - (checkRequiredFields.bind(null, arrayToCheck, objectParameter)).should.throw('ThirdValue is a required parameter.'); - }); - - it('should work with non-string keys', () => { - const arrayToCheckNonString = [0, 1, 2, 3]; - const objectParameter = { - 0: 0, 1: 10, - 2: 20, - 3: 30, }; - (checkRequiredFields(arrayToCheckNonString, objectParameter)).should.be.true(); - }); - it('should work with non-string keys', () => { - const arrayToCheckNonString = [0, 1, 2, 3]; - const objectParameter = { - 0: 0, - 1: 10, - 2: 20, - }; - (checkRequiredFields.bind(null, arrayToCheckNonString, objectParameter)).should.throw('3 is a required parameter.'); + (checkRequiredFields.bind(null, arrayToCheck, objectParameter)).should.throw('ThirdValue is a required parameter.'); }); });