Skip to content

Commit

Permalink
remove bitcore
Browse files Browse the repository at this point in the history
  • Loading branch information
fanatid committed Oct 19, 2015
1 parent 8ee368f commit e996a34
Show file tree
Hide file tree
Showing 5 changed files with 439 additions and 135 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@
"test:node": "istanbul test mocha -- --compilers js:babel/register --reporter spec test/*.js"
},
"dependencies": {
"babel-runtime": "^5.8.20"
},
"peerDependencies": {
"bitcore": "^0.13.3"
"babel-runtime": "^5.8.20",
"bs58check": "^1.0.6",
"create-hash": "^1.1.2"
},
"devDependencies": {
"babel": "^5.8.21",
"babel-core": "^5.8.22",
"babel-eslint": "^4.0.5",
"babelify": "^6.1.3",
"bitcoinjs-lib": "^2.1.1",
"chai": "^3.2.0",
"coveralls": "^2.11.3",
"eslint": "^1.0.0",
Expand Down
199 changes: 124 additions & 75 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import bitcore from 'bitcore'
import createHash from 'create-hash'
import bs58check from 'bs58check'

let Address = bitcore.Address
let base58encode = bitcore.encoding.Base58Check.encode
let Networks = bitcore.Networks
let Opcode = bitcore.Opcode
let Script = bitcore.Script
let sha256ripemd160 = bitcore.crypto.Hash.sha256ripemd160
import networks from './networks.json'
import opcodes from './opcodes.json'

/**
* @param {Buffer} buf
* @return {Buffer}
*/
function sha256ripemd160 (buf) {
buf = createHash('sha256').update(buf).digest()
return createHash('ripemd160').update(buf).digest()
}

/**
* @param {number} version
Expand All @@ -15,7 +21,7 @@ let sha256ripemd160 = bitcore.crypto.Hash.sha256ripemd160
function createAddress (version, hashBuffer) {
let versionBuffer = new Buffer([version])
let buffer = Buffer.concat([versionBuffer, hashBuffer])
return base58encode(buffer)
return bs58check.encode(buffer)
}

/**
Expand All @@ -24,10 +30,7 @@ function createAddress (version, hashBuffer) {
* @return {boolean}
*/
function isPublicKey (buffer, strict) {
if (!buffer) {
return false
}

// TODO: need more check
switch (buffer[0]) {
case 0x02:
case 0x03:
Expand All @@ -43,98 +46,144 @@ function isPublicKey (buffer, strict) {
}

/**
* @param {*} script
* @param {*} [network=bitcore.Networks.defaultNetwork]
* @param {Buffer} buf
* @param {number} offset
* @param {boolean} strict
* @return {?{bytes: number, size: number}}
*/
function readDataSize (buf, offset, strict) {
let opcode = buf[offset]

if ((strict && opcode >= opcodes.OP_PUSHDATA1) ||
opcode > opcodes.OP_PUSHDATA4 ||
buf.length < offset + opcode - opcodes.OP_PUSHDATA1 + (opcode === opcodes.OP_PUSHDATA4 ? 3 : 2)) {
return null
}

switch (opcode) {
case opcodes.OP_PUSHDATA4:
return {bytes: 5, size: buf.readUInt32LE(offset + 1)}
case opcodes.OP_PUSHDATA2:
return {bytes: 3, size: buf.readUInt16LE(offset + 1)}
case opcodes.OP_PUSHDATA1:
return {bytes: 2, size: buf.readUInt8(offset + 1)}
default:
return {bytes: 1, size: opcode}
}
}

/**
* @param {(Buffer|string)} buf
* @param {({pubkeyhash: number, scripthash: number}|string)} [network={pubkeyhash: 0x80, scripthash: 0x05}]
* @param {boolean} [strict=false]
* @return {{type: string, addresses: Array.<string>}}
*/
export default function (script, network, strict) {
try {
script = new Script(script)
} catch (err) {
return {type: 'unknow'}
export default function (buf, network, strict) {
if (!Buffer.isBuffer(buf)) {
try {
buf = new Buffer(buf, 'hex')
} catch (err) {
return {type: 'unknow', addresses: []}
}
}

let sChunks = script.chunks
if (sChunks.length === 0) {
return {type: 'unknow'}
if (Object.prototype.toString.call(network) === '[object String]') {
network = networks[network]
}

network = network || Networks.get(network) || Networks.defaultNetwork
switch (sChunks[0].opcodenum) {
if (Object.prototype.toString.call(network) !== '[object Object]') {
network = networks.mainnet
}

let dataSize
switch (buf[0]) {
// pubkeyhash
case Opcode.OP_DUP:
if (!(sChunks.length === 5 &&
sChunks[1].opcodenum === Opcode.OP_HASH160 &&
sChunks[2].buf &&
sChunks[2].buf.length === 20 &&
sChunks[3].opcodenum === Opcode.OP_EQUALVERIFY &&
sChunks[4].opcodenum === Opcode.OP_CHECKSIG) ||
(strict && sChunks[2].opcodenum !== 20)) {
return {type: 'unknow'}
case opcodes.OP_DUP:
if (buf.length < 25 ||
buf.length > (strict ? 25 : 29) ||
buf[1] !== opcodes.OP_HASH160 ||
buf[buf.length - 2] !== opcodes.OP_EQUALVERIFY ||
buf[buf.length - 1] !== opcodes.OP_CHECKSIG) {
break
}

dataSize = readDataSize(buf, 2, strict)
if (dataSize === null || dataSize.size !== 20) {
break
}

buf = buf.slice(2 + dataSize.bytes, buf.length - 2)
return {
type: 'pubkeyhash',
addresses: [
createAddress(network[Address.PayToPublicKeyHash], sChunks[2].buf)
]
addresses: [createAddress(network.pubkeyhash, buf)]
}

// scripthash
case Opcode.OP_HASH160:
if (!(sChunks.length === 3 &&
sChunks[1].buf &&
sChunks[1].buf.length === 20 &&
sChunks[2].opcodenum === Opcode.OP_EQUAL) ||
(strict && sChunks[1].opcodenum !== 20)) {
return {type: 'unknow'}
case opcodes.OP_HASH160:
if (buf.length < 23 ||
buf.length > (strict ? 23 : 27) ||
buf[buf.length - 1] !== opcodes.OP_EQUAL) {
break
}

dataSize = readDataSize(buf, 1, strict)
if (dataSize === null || dataSize.size !== 20) {
break
}

buf = buf.slice(1 + dataSize.bytes, buf.length - 1)
return {
type: 'scripthash',
addresses: [
createAddress(network[Address.PayToScriptHash], sChunks[1].buf)
]
addresses: [createAddress(network.scripthash, buf)]
}

// nulldata
case Opcode.OP_RETURN:
return {type: 'nulldata'}
case opcodes.OP_RETURN:
return {type: 'nulldata', addresses: []}

// pubkey & multisig
default:
// pubkey
if ((sChunks.length === 2 &&
isPublicKey(sChunks[0].buf, strict) &&
sChunks[1].opcodenum === Opcode.OP_CHECKSIG) &&
!(strict &&
sChunks[0].opcodenum !== 33 &&
sChunks[0].opcodenum !== 65)) {
let hashBuffer = sha256ripemd160(sChunks[0].buf)
if (buf[buf.length - 1] === opcodes.OP_CHECKSIG) {
dataSize = readDataSize(buf, 0, strict)
if (dataSize === null) {
break
}

buf = buf.slice(dataSize.bytes, buf.length - 1)
if (!isPublicKey(buf, strict)) {
break
}

return {
type: 'pubkey',
addresses: [
createAddress(network[Address.PayToPublicKeyHash], hashBuffer)
]
addresses: [createAddress(network.pubkeyhash, sha256ripemd160(buf))]
}
}

// multisig
let mOp = sChunks[0].opcodenum
let nOp = sChunks[Math.max(sChunks.length - 2, 0)].opcodenum
if (sChunks.length >= 4 &&
sChunks[sChunks.length - 1].opcodenum === Opcode.OP_CHECKMULTISIG &&
mOp >= Opcode.OP_1 &&
mOp <= Opcode.OP_16 &&
nOp >= Opcode.OP_1 &&
nOp <= Opcode.OP_16 &&
nOp >= mOp &&
nOp - Opcode.OP_1 === sChunks.length - 4 &&
sChunks.slice(1, -2).every((o) => isPublicKey(o.buf, strict))) {
let addresses = sChunks.slice(1, -2).map((o) => {
let hashBuffer = sha256ripemd160(o.buf)
return createAddress(network[Address.PayToPublicKeyHash], hashBuffer)
let mOp = buf[0]
let nOp = buf[buf.length - 2]
let isMultisig = (buf[buf.length - 1] === opcodes.OP_CHECKMULTISIG &&
mOp >= opcodes.OP_1 &&
nOp <= opcodes.OP_16 &&
nOp >= mOp)

let pubKeys = []
for (let offset = 1, stop = buf.length - 2; isMultisig && offset < stop;) {
dataSize = readDataSize(buf, offset, strict)
if (dataSize === null) {
isMultisig = false
} else {
pubKeys.push(buf.slice(offset + dataSize.bytes, offset + dataSize.bytes + dataSize.size))
isMultisig = isPublicKey(pubKeys[pubKeys.length - 1], strict)
offset += dataSize.bytes + dataSize.size
}
}

if (isMultisig && pubKeys.length === nOp - opcodes.OP_1 + 1) {
let addresses = pubKeys.map((pubKey) => {
return createAddress(network.pubkeyhash, sha256ripemd160(pubKey))
})
return {
type: 'multisig',
Expand All @@ -143,8 +192,8 @@ export default function (script, network, strict) {
})
}
}

// unknow output script type
return {type: 'unknow'}
}

// unknow output script type
return {type: 'unknow', addresses: []}
}
14 changes: 14 additions & 0 deletions src/networks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"mainnet": {
"pubkeyhash": 0,
"scripthash": 5
},
"livenet": {
"pubkeyhash": 0,
"scripthash": 5
},
"testnet": {
"pubkeyhash": 111,
"scripthash": 196
}
}
15 changes: 15 additions & 0 deletions src/opcodes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"OP_0": 0,
"OP_PUSHDATA1": 76,
"OP_PUSHDATA2": 77,
"OP_PUSHDATA4": 78,
"OP_1": 81,
"OP_16": 96,
"OP_RETURN": 106,
"OP_DUP": 118,
"OP_EQUAL": 135,
"OP_EQUALVERIFY": 136,
"OP_HASH160": 169,
"OP_CHECKSIG": 172,
"OP_CHECKMULTISIG": 174
}
Loading

0 comments on commit e996a34

Please sign in to comment.