blockstack
Version:
The Blockstack Javascript library for authentication, identity, and storage.
382 lines (352 loc) • 15.6 kB
JavaScript
import { exec } from 'child_process'
import test from 'tape'
import {
transactions, config, network, hexStringToECPair
} from '../../../lib'
import { hash160 } from '../../../lib/operations/utils'
const BLOCKSTACK_TEST = !!process.env.BLOCKSTACK_TEST
function pExec(cmd) {
return new Promise(
(resolve, reject) => {
exec(cmd, (err, stdout, stderr) => {
if (err) {
reject(err)
} else {
resolve(stdout, stderr)
}
})
}
)
}
function initializeBlockstackCore() {
if (BLOCKSTACK_TEST) {
// running with an external test suite
return Promise.resolve()
} else {
return pExec('docker pull quay.io/blockstack/integrationtests:develop')
.then(() => {
console.log('Pulled latest docker image')
return pExec(`docker stop test-bsk-core ;
docker rm test-bsk-core ;
rm -rf /tmp/.blockstack_int_test`)
.catch(() => true)
})
.then(() => pExec('docker run --name test-bsk-core -dt '
+ '-p 16268:16268 -p 18332:18332 -p 30001:30001 '
+ '-e BLOCKSTACK_TEST_CLIENT_RPC_PORT=16268 '
+ '-e BLOCKSTACK_TEST_CLIENT_BIND=0.0.0.0 '
+ '-e BLOCKSTACK_TEST_BITCOIND_ALLOWIP=172.17.0.0/16 '
+ '-e BLOCKSTACK_WEB_TEST_BIND=0.0.0.0 '
+ 'quay.io/blockstack/integrationtests:develop '
+ 'blockstack-test-scenario --interactive 2 '
+ 'blockstack_integration_tests.scenarios.portal_test_env'))
.then(() => {
console.log('Started regtest container, waiting until initialized')
return pExec('docker logs -f test-bsk-core | grep -q \'Test finished\'')
})
.then(() => {
// try to avoid race with nextBlock()
console.log('Wait 10 seconds for test server to bind')
return new Promise(resolve => setTimeout(resolve, 10000))
})
}
}
function nextBlock(numBlocks) {
if (BLOCKSTACK_TEST) {
const options = {
method: 'POST'
}
if (!!numBlocks) {
options.body = `numblocks=${numBlocks}`
}
const url = 'http://localhost:30001/nextblock'
return fetch(url, options)
.then((resp) => {
if (resp.status >= 400) {
throw new Error(`Bad test framework status: ${resp.status}`)
} else {
return true
}
})
} else {
return new Promise(resolve => setTimeout(resolve, 30000))
}
}
function shutdownBlockstackCore() {
if (BLOCKSTACK_TEST) {
return Promise.resolve()
} else {
return pExec('docker stop test-bsk-core')
}
}
export function runIntegrationTests() {
test('registerIdName', (t) => {
t.plan(8)
config.network = network.defaults.LOCAL_REGTEST
const myNet = config.network
const dest = '19238846ac60fa62f8f8bb8898b03df79bc6112600181f36061835ad8934086001'
const destAddress = hexStringToECPair(dest).getAddress()
const btcDest = '897f1b92041b798580f96b8be379053f6276f04eb7590a9042a62059d46d6fc301'
const btcDestAddress = hexStringToECPair(btcDest).getAddress()
const payer = 'bb68eda988e768132bc6c7ca73a87fb9b0918e9a38d3618b74099be25f7cab7d01'
const secondOwner = '54164693e3803223f7fa9a004997bfbf1475f5c44f65593fa45c6783086dafec01'
const transferDestination = hexStringToECPair(secondOwner).getAddress()
const renewalDestination = 'myPgwEX2ddQxPPqWBRkXNqL3TwuWbY29DJ'
const zfTest = '$ORIGIN aaron.id\n$TTL 3600\n_http._tcp URI 10 1 '
+ `"https://gaia.blockstacktest.org/hub/${destAddress}/0/profile.json"`
const zfTest2 = '$ORIGIN aaron.id\n$TTL 3600\n_http._tcp URI 10 1 '
+ `"https://gaia.blockstacktest.org/hub/${destAddress}/3/profile.json"`
const renewalZF = '$ORIGIN aaron.id\n$TTL 3600\n_http._tcp URI 10 1 '
+ `"https://gaia.blockstacktest.org/hub/${destAddress}/4/profile.json"`
initializeBlockstackCore()
.then(() => {
console.log('Blockstack Core initialized.')
return transactions.makePreorder('aaron.id', destAddress, payer)
})
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('PREORDER broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => transactions.makeRegister('aaron.id', destAddress, payer, zfTest))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('REGISTER broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => myNet.broadcastZoneFile(zfTest))
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.id`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(myNet.coerceAddress(nameInfo.address), destAddress,
`aaron.id should be owned by ${destAddress}`)
t.equal(nameInfo.zonefile, zfTest, 'zonefile should be properly set')
})
.then(() => transactions.makeUpdate('aaron.id', dest, payer, zfTest2))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('UPDATE broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => myNet.broadcastZoneFile(zfTest2))
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.id`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(nameInfo.zonefile, zfTest2, 'zonefile should be updated')
})
.then(() => transactions.makeTransfer('aaron.id', transferDestination, dest, payer))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('TRANSFER broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.id`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(myNet.coerceAddress(nameInfo.address), transferDestination,
`aaron.id should be owned by ${transferDestination}`)
})
.then(() => transactions.makeRenewal('aaron.id', renewalDestination,
secondOwner, payer, renewalZF))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('RENEWAL broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => myNet.broadcastZoneFile(renewalZF))
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.id`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(nameInfo.zonefile, renewalZF, 'zonefile should be updated')
t.equal(myNet.coerceAddress(nameInfo.address), renewalDestination,
`aaron.id should be owned by ${renewalDestination}`)
})
.then(() => transactions.makeBitcoinSpend(btcDestAddress, payer, 500000))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('broadcasted SPEND, waiting 10 seconds.')
return nextBlock()
})
.then(() => myNet.getUTXOs(btcDestAddress))
.then((utxos) => {
t.equal(utxos.length, 1, `Destination address ${btcDestAddress} should have 1 UTXO`)
const satoshis = utxos.reduce((agg, utxo) => agg + utxo.value, 0)
console.log(`${btcDestAddress} has ${satoshis} satoshis`)
})
.then(() => shutdownBlockstackCore())
.then(() => t.pass('Finished test'))
})
test('helloNamespace', (t) => {
t.plan(11)
config.network = network.defaults.LOCAL_REGTEST
const myNet = config.network
const nsPay = '6e50431b955fe73f079469b24f06480aee44e4519282686433195b3c4b5336ef01'
const nsReveal = 'c244642ce0b4eb68da8e098facfcad889e3063c36a68b7951fb4c085de49df1b01'
const nsRevealAddress = hexStringToECPair(nsReveal).getAddress()
const dest = '19238846ac60fa62f8f8bb8898b03df79bc6112600181f36061835ad8934086001'
const destAddress = hexStringToECPair(dest).getAddress()
const btcDest = '3ad9f690cc7694572fe7574526ad260ff2e711d608d3224895efd932b1d47c7201'
const btcDestAddress = hexStringToECPair(btcDest).getAddress()
const payer = 'bb68eda988e768132bc6c7ca73a87fb9b0918e9a38d3618b74099be25f7cab7d01'
const secondOwner = '54164693e3803223f7fa9a004997bfbf1475f5c44f65593fa45c6783086dafec01'
const transferDestination = hexStringToECPair(secondOwner).getAddress()
const renewalKey = 'bb68eda988e768132bc6c7ca73a87fb9b0918e9a38d3618b74099be25f7cab7d'
const renewalDestination = hexStringToECPair(renewalKey).getAddress()
const zfTest = '$ORIGIN aaron.hello\n$TTL 3600\n_http._tcp URI 10 1 '
+ `"https://gaia.blockstacktest.org/hub/${destAddress}/0/profile.json"`
const zfTest2 = '$ORIGIN aaron.hello\n$TTL 3600\n_http._tcp URI 10 1 '
+ `"https://gaia.blockstacktest.org/hub/${destAddress}/3/profile.json"`
const renewalZF = '$ORIGIN aaron.hello\n$TTL 3600\n_http._tcp URI 10 1 '
+ `"https://gaia.blockstacktest.org/hub/${destAddress}/4/profile.json"`
const importZF = '$ORIGIN import.hello\n$TTL 3600\n_http._tcp URI 10 1 '
+ `"https://gaia.blockstacktest.org/hub/${destAddress}/0/profile.json"`
initializeBlockstackCore()
.then(() => {
console.log('Blockstack Core initialized.')
console.log(`Preorder namespace "hello" to ${nsRevealAddress}`)
return transactions.makeNamespacePreorder('hello', nsRevealAddress, nsPay)
})
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('NAMESPACE_PREORDER broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => {
const ns = new transactions.BlockstackNamespace('hello')
ns.setVersion(1)
ns.setLifetime(52595)
ns.setCoeff(4)
ns.setBase(4)
ns.setBuckets([6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
ns.setNonalphaDiscount(10)
ns.setNoVowelDiscount(10)
console.log(`Reveal namespace "hello" to ${nsRevealAddress}`)
return transactions.makeNamespaceReveal(ns, nsRevealAddress, nsPay)
})
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('NAMESPACE_REVEAL broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => {
console.log('NAME_IMPORT import.hello')
const zfHash = hash160(Buffer.from(importZF)).toString('hex')
return transactions.makeNameImport(
'import.hello', renewalDestination, zfHash, nsReveal
)
})
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('NAME_IMPORT broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => myNet.broadcastZoneFile(importZF))
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/import.hello`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(myNet.coerceAddress(nameInfo.address), renewalDestination,
`import.hello should be owned by ${renewalDestination}`)
t.equal(nameInfo.zonefile, importZF, 'zonefile should be properly set for import.hello')
})
.then(() => {
console.log('Launch namespace "hello"')
return transactions.makeNamespaceReady('hello', nsReveal)
})
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('NAMESPACE_READY broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => {
console.log('Namespace initialized. Preordering aaron.hello')
return transactions.makePreorder('aaron.hello', destAddress, payer)
})
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('PREORDER broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => transactions.makeRegister('aaron.hello', destAddress, payer, zfTest))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('REGISTER broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => myNet.broadcastZoneFile(zfTest))
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.hello`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(myNet.coerceAddress(nameInfo.address), destAddress,
`aaron.hello should be owned by ${destAddress}`)
t.equal(nameInfo.zonefile, zfTest, 'zonefile should be properly set')
})
.then(() => transactions.makeUpdate('aaron.hello', dest, payer, zfTest2))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('UPDATE broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => myNet.broadcastZoneFile(zfTest2))
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.hello`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(nameInfo.zonefile, zfTest2, 'zonefile should be updated')
})
.then(() => transactions.makeTransfer('aaron.hello', transferDestination, dest, payer))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('TRANSFER broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.hello`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(myNet.coerceAddress(nameInfo.address), transferDestination,
`aaron.hello should be owned by ${transferDestination}`)
})
.then(() => transactions.makeRenewal('aaron.hello', renewalDestination,
secondOwner, payer, renewalZF))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('RENEWAL broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => myNet.broadcastZoneFile(renewalZF))
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.hello`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(nameInfo.zonefile, renewalZF, 'zonefile should be updated')
t.equal(myNet.coerceAddress(nameInfo.address), renewalDestination,
`aaron.hello should be owned by ${renewalDestination}`)
})
.then(() => transactions.makeRevoke('aaron.hello', renewalKey, payer))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('REVOKE broadcasted, waiting 30 seconds.')
return nextBlock()
})
.then(() => fetch(`${myNet.blockstackAPIUrl}/v1/names/aaron.hello`))
.then(resp => resp.json())
.then((nameInfo) => {
t.equal(nameInfo.status, 'revoked', 'Name should be revoked')
})
.then(() => transactions.makeBitcoinSpend(btcDestAddress, payer, 500000))
.then(rawtx => myNet.broadcastTransaction(rawtx))
.then(() => {
console.log('broadcasted SPEND, waiting 10 seconds.')
return nextBlock(6)
})
.then(() => myNet.getUTXOs(btcDestAddress))
.then((utxos) => {
t.equal(utxos.length, 1, `Destination address ${btcDestAddress} should have 1 UTXO`)
const satoshis = utxos.reduce((agg, utxo) => agg + utxo.value, 0)
console.log(`${btcDestAddress} has ${satoshis} satoshis`)
})
.then(() => shutdownBlockstackCore())
.then(() => t.pass('Finished test'))
.catch((err) => {
console.log(err.stack)
console.log(err)
})
})
}