From a642114fb53d732849f3c4dc0c37da09523af0d2 Mon Sep 17 00:00:00 2001 From: Stephen Pair Date: Wed, 10 Jul 2013 13:03:05 -0400 Subject: [PATCH] refactoring of Address --- Address.js | 139 +++--------------------------------------- util/EncodedData.js | 136 +++++++++++++++++++++++++++++++++++++++++ util/VersionedData.js | 39 ++++++++++++ 3 files changed, 184 insertions(+), 130 deletions(-) create mode 100644 util/EncodedData.js create mode 100644 util/VersionedData.js diff --git a/Address.js b/Address.js index 2337a44..b7c9866 100644 --- a/Address.js +++ b/Address.js @@ -1,143 +1,22 @@ require('classtool'); function ClassSpec(b) { - var base58 = b.base58 || require('base58-native').base58Check; + var superclass = b.superclass || require('./util/VersionedData').class(); - // Constructor. Takes the following forms: - // new Address(); - // new Address() - // new Address(<21-byte-buffer>) - // new Address(, ) - // new Address(, <20-byte-hash>) - function Address(arg1, arg2) { - if(typeof arg1 == 'number') { - this.data = new Buffer(21); - this.__proto__ = encodings['binary']; - this.version(arg1); - this.hash(arg2); - } else { - this.data = arg1 || new Buffer(21); - if(!arg2 && (typeof arg1 == 'string')) { - this.__proto__ = encodings['base58']; - } else { - this.__proto__ = encodings[arg2 || 'binary']; - } - } + function Address() { + Address.super(this, arguments); }; - // get or set the bitcoin address version (the first byte of the address) - Address.prototype.version = function(num) { - if(num || (num === 0)) { - this.doAsBinary(function() {this.data.writeUInt8(num, 0);}); - return num; - } - return this.as('binary').readUInt8(0); - }; - - // get or set the hash data (as a Buffer object) - Address.prototype.hash = function(data) { - if(data) { - this.doAsBinary(function() {data.copy(this.data,1);}); - return data; - } - return this.as('binary').slice(1); - }; - - // get or set the encoding used (transforms data) - Address.prototype.encoding = function(encoding) { - if(encoding && (encoding != this._encoding)) { - this.data = this.as(encoding); - this.__proto__ = encodings[encoding]; - } - return this._encoding; - }; + Address.superclass = superclass; + superclass.applyEncodingsTo(Address); - // answer a new instance having the given encoding - Address.prototype.withEncoding = function(encoding) { - return new Address(this.as(encoding), encoding); - }; - - // answer the data in the given encoding - Address.prototype.as = function(encoding) { - if(!encodings[encoding]) throw new Error('invalid encoding'); - return this.converters[encoding].call(this); - }; - - // validate the address (basically just check that we have 21 bytes) Address.prototype.validate = function() { - this.withEncoding('binary').validate(); + this.doAsBinary(function() { + Address.super(this, 'validate', arguments); + if(this.data.length != 21) throw new Error('invalid data length'); + }); }; - // convert to a string (in base58 form) - Address.prototype.toString = function() { - return this.as('base58'); - }; - - // utility - Address.prototype.doAsBinary = function(callback) { - var oldEncoding = this.encoding(); - this.encoding('binary'); - callback.apply(this); - this.encoding(oldEncoding); - }; - - // Setup support for various address encodings. The object for - // each encoding inherits from the Address prototype. This - // allows any encoding to override any method...changing the encoding - // for an instance will change the encoding it inherits from. Note, - // this will present some problems for anyone wanting to inherit from - // Address (we'll deal with that when needed). - var encodings = { - 'binary': { - converters: { - 'binary': function() { - var answer = new Buffer(this.data.length); - this.data.copy(answer); - return answer; - }, - 'base58': function() { - return base58.encode(this.data); - }, - 'hex': function() { - return this.data.toString('hex'); - }, - }, - - validate: function() { - if(this.data.length != 21) throw new Error('invalid data length'); - }, - }, - - 'base58': { - converters: { - 'binary': function() { - return base58.decode(this.data); - }, - 'hex': function() { - return this.withEncoding('binary').as('hex'); - }, - }, - }, - - 'hex': { - converters: { - 'binary': function() { - return new Buffer(this.data, 'hex'); - }, - 'base58': function() { - return this.withEncoding('binary').as('base58'); - }, - }, - }, - }; - - for(var k in encodings) { - if(!encodings[k].converters[k]) - encodings[k].converters[k] = function() {return this.data;}; - encodings[k]._encoding = k; - encodings[k].__proto__ = Address.prototype; - }; - return Address; }; module.defineClass(ClassSpec); diff --git a/util/EncodedData.js b/util/EncodedData.js new file mode 100644 index 0000000..c8ee33c --- /dev/null +++ b/util/EncodedData.js @@ -0,0 +1,136 @@ +require('classtool'); + +function ClassSpec(b) { + var base58 = b.base58 || require('base58-native').base58Check; + + // Constructor. Takes the following forms: + // new EncodedData() + // new EncodedData() + // new EncodedData(, ) + // new EncodedData(, <20-byte-hash>) + function EncodedData(data, encoding) { + this.data = data; + if(!encoding && (typeof data == 'string')) { + this.__proto__ = this.encodings['base58']; + } else { + this.__proto__ = this.encodings[encoding || 'binary']; + } + }; + + // get or set the encoding used (transforms data) + EncodedData.prototype.encoding = function(encoding) { + if(encoding && (encoding != this._encoding)) { + this.data = this.as(encoding); + this.__proto__ = this.encodings[encoding]; + } + return this._encoding; + }; + + // answer a new instance having the given encoding + EncodedData.prototype.withEncoding = function(encoding) { + return new EncodedData(this.as(encoding), encoding); + }; + + // answer the data in the given encoding + EncodedData.prototype.as = function(encoding) { + if(!encodings[encoding]) throw new Error('invalid encoding'); + return this.converters[encoding].call(this); + }; + + // validate that we can convert to binary + EncodedData.prototype._validate = function() { + this.withEncoding('binary'); + }; + + // subclasses can override to do more stuff + EncodedData.prototype.validate = function() { + this._validate(); + }; + + // convert to a string (in base58 form) + EncodedData.prototype.toString = function() { + return this.as('base58'); + }; + + // utility + EncodedData.prototype.doAsBinary = function(callback) { + var oldEncoding = this.encoding(); + this.encoding('binary'); + callback.apply(this); + this.encoding(oldEncoding); + }; + + // Setup support for various address encodings. The object for + // each encoding inherits from the EncodedData prototype. This + // allows any encoding to override any method...changing the encoding + // for an instance will change the encoding it inherits from. Note, + // this will present some problems for anyone wanting to inherit from + // EncodedData (we'll deal with that when needed). + var encodings = { + 'binary': { + converters: { + 'binary': function() { + var answer = new Buffer(this.data.length); + this.data.copy(answer); + return answer; + }, + 'base58': function() { + return base58.encode(this.data); + }, + 'hex': function() { + return this.data.toString('hex'); + }, + }, + + _validate: function() { + //nothing to do here...we make no assumptions about the data + }, + }, + + 'base58': { + converters: { + 'binary': function() { + return base58.decode(this.data); + }, + 'hex': function() { + return this.withEncoding('binary').as('hex'); + }, + }, + }, + + 'hex': { + converters: { + 'binary': function() { + return new Buffer(this.data, 'hex'); + }, + 'base58': function() { + return this.withEncoding('binary').as('base58'); + }, + }, + }, + }; + + for(var k in encodings) { + if(!encodings[k].converters[k]) + encodings[k].converters[k] = function() {return this.data;}; + encodings[k]._encoding = k; + } + + EncodedData.applyEncodingsTo = function(aClass) { + var tmp = {}; + for(var k in encodings) { + var enc = encodings[k]; + var obj = {}; + for(var j in enc) { + obj[j] = enc[j]; + } + obj.__proto__ = aClass.prototype; + tmp[k] = obj; + } + aClass.prototype.encodings = tmp; + }; + + EncodedData.applyEncodingsTo(EncodedData); + return EncodedData; +}; +module.defineClass(ClassSpec); diff --git a/util/VersionedData.js b/util/VersionedData.js new file mode 100644 index 0000000..b4317fd --- /dev/null +++ b/util/VersionedData.js @@ -0,0 +1,39 @@ +require('classtool'); + +function ClassSpec(b) { + var superclass = b.superclass || require('./EncodedData').class(); + + function VersionedData(version, payload) { + if(typeof version != 'number') { + VersionedData.super(this, arguments); + return; + }; + this.data = new Buffer(payload.length + 1); + this.__proto__ = this.encodings['binary']; + this.version(version); + this.payload(payload); + }; + VersionedData.superclass = superclass; + superclass.applyEncodingsTo(VersionedData); + + // get or set the version data (the first byte of the address) + VersionedData.prototype.version = function(num) { + if(num || (num === 0)) { + this.doAsBinary(function() {this.data.writeUInt8(num, 0);}); + return num; + } + return this.as('binary').readUInt8(0); + }; + + // get or set the payload data (as a Buffer object) + VersionedData.prototype.payload = function(data) { + if(data) { + this.doAsBinary(function() {data.copy(this.data,1);}); + return data; + } + return this.as('binary').slice(1); + }; + + return VersionedData; +}; +module.defineClass(ClassSpec);