navio-blsct
Version:
TypeScript bindings for the `libblsct` library used by the [Navio](https://nav.io/) blockchain to construct confidential transactions based on the BLS12-381 curve.
273 lines (237 loc) • 8.18 kB
JavaScript
const fs = require('fs')
const os = require('os')
const path = require('path')
const { spawnSync } = require('child_process')
// TODO: turn this on for production builds
const IS_PROD = false
const DEV_BRANCH = 'add-missing-functionality'
// git ls-remote https://github.com/nav-io/navio-core.git refs/heads/master
const MASTER_SHA = '3f7805c30db897c787b9cae50a013f9c8cd20086'
const getCfg = (isProd) => {
baseDir = path.resolve(__dirname, '..')
swigDir = path.join(baseDir, 'swig')
navioCoreDir = path.join(baseDir, 'navio-core')
dependsDir = path.join(navioCoreDir, 'depends')
libsDir = path.join(baseDir, 'libs')
bakDir = path.join(os.homedir(), '.navio-tmp')
dependsBakDir = path.join(bakDir, 'depends')
srcPath = path.join(navioCoreDir, "src")
blsPath = path.join(srcPath, "bls")
blsLibPath = path.join(blsPath, "lib")
mclPath = path.join(blsPath, "mcl")
mclLibPath = path.join(mclPath, "lib")
const srcDotAFiles = [
path.join(srcPath, "libblsct.a"),
path.join(srcPath, "libunivalue_blsct.a"),
path.join(blsLibPath, "libbls384_256.a"),
path.join(mclLibPath, "libmcl.a"),
]
const destDotAFiles = [
path.join(libsDir, "libblsct.a"),
path.join(libsDir, "libunivalue_blsct.a"),
path.join(libsDir, "libbls384_256.a"),
path.join(libsDir, "libmcl.a"),
]
return {
swigDir,
stdCpp: '-std=c++20',
navioCoreRepo: isProd ? 'https://github.com/nav-io/navio-core' : 'https://github.com/gogoex/navio-core',
navioCoreMasterSha: isProd ? MASTER_SHA : '',
navioCoreBranch: isProd ? '' : DEV_BRANCH,
navioCoreDir,
dependsDir,
dependsBakDir,
srcDotAFiles,
destDotAFiles,
libsDir,
}
}
const exec = (cmd) => {
const isRoot = process.getuid && process.getuid() === 0
if (!isRoot) {
cmd = ['sudo', ...cmd]
}
const res = spawnSync(cmd[0], cmd.slice(1), { stdio: 'inherit' })
if (res.status !== 0) {
throw new Error(`Failed to execute ${cmd.join(' ')}: ${JSON.stringify(res)}`)
}
}
const detectPkgManager = () => {
const exists = (cmd) => {
const res = spawnSync('which', [cmd])
return res.status === 0
}
if (exists('apt-get')) {
return 'apt-get'
} else {
return undefined
}
}
const installSystemDeps = () => {
const platform = os.platform()
console.log(`Platform: ${platform}`)
if (platform === 'darwin') {
console.log('Installing system dependencies w/ brew...')
spawnSync('brew', ['install', 'swig', 'autoconf', 'automake', 'libtool', 'pkg-config', 'git'])
} else if (platform === 'linux') {
const pm = detectPkgManager()
if (pm !== undefined) {
console.log(`Installing system dependencies w/ ${pm}...`)
if (pm === 'apt-get') {
exec(['apt-get', 'update'])
exec(['apt-get', 'install', '-y', 'swig', 'autoconf', 'automake', 'libtool', 'pkg-config', 'git', 'python3', 'build-essential'])
} else {
// should not be reached
}
} else {
console.log('No supported package manager found')
}
} else {
console.log(`Unsupported platform: ${platform}`)
}
}
const getDepArchDir = (dependsDir) => {
if (!fs.existsSync(dependsDir)) {
throw new Error(`${dependsDir} not found`)
}
const arches = [
'x86_64', 'i686', 'mips', 'arm', 'aarch64',
'powerpc', 'riscv32', 'riscv64', 's390x'
]
const files = fs.readdirSync(dependsDir, { withFileTypes: true })
for (const file of files) {
if (
file.isDirectory() &&
arches.some(arch => file.name.startsWith(arch))
) {
console.log(`Found dependency arch dir: ${file.name}`)
return path.resolve(dependsDir, file.name)
}
}
throw new Error(`Failed to find dependency arch dir in ${dependsDir}`)
}
const gitCloneNavioCore = (cfg) => {
// Remove existing directory
if (fs.existsSync(cfg.navioCoreDir)) {
fs.rmSync(cfg.navioCoreDir, { recursive: true, force: true })
console.log(`Removed exisitng navio-core dir`)
} else {
console.log(`No existing navio-core dir found`)
}
const cmd = ['git', 'clone', '--depth', '1']
if (cfg.navioCoreBranch !== '') {
cmd.push('--branch', cfg.navioCoreBranch)
console.log(`Using navio-core ${cfg.navioCoreBranch} branch...`)
} else {
console.log(`Using navio-core master branch...`)
}
cmd.push(cfg.navioCoreRepo, cfg.navioCoreDir)
const res = spawnSync(cmd[0], cmd.slice(1))
if (res.status !== 0) {
throw new Error(`${cmd.join(' ')} failed: ${JSON.stringify(res)}`)
}
console.log(`Cloned navio-core`)
if (cfg.navioCoreMasterSha !== '') {
const cwd = cfg.navioCoreDir
{
const cmd = ['git', 'fetch', '--depth', '1', 'origin', cfg.navioCoreMasterSha]
const res = spawnSync(cmd[0], cmd.slice(1), { cwd })
if (res.status !== 0) {
throw new Error(`${cmd.join(' ')} failed: ${JSON.stringify(res)}`)
}
console.log(`Fetched navio-core commit ${cfg.navioCoreMasterSha}`)
}
{
const cmd = ['git', 'checkout', cfg.navioCoreMasterSha]
const res = spawnSync(cmd[0], cmd.slice(1), { cwd })
if (res.status !== 0) {
throw new Error(`${cmd.join(' ')} failed: ${JSON.stringify(res)}`)
}
console.log(`Checked out navio-core commit ${cfg.navioCoreMasterSha}`)
}
}
}
const buildDepends = (cfg, numCpus) => {
if (fs.existsSync(cfg.dependsBakDir)) {
console.log('Copying the backup of dependency dir...')
fs.cpSync(cfg.dependsBakDir, cfg.dependsDir, { recursive: true })
} else {
console.log('Building navio-core dependencies...')
const res = spawnSync('make', ['-j', numCpus], { cwd: cfg.dependsDir })
if (res.status !== 0) {
throw new Error(`Failed to build dependencies: ${JSON.stringify(res)}`)
}
console.log('Creating backup of depends dir...')
fs.cpSync(cfg.dependsDir, cfg.dependsBakDir, { recursive: true })
}
return getDepArchDir(cfg.dependsDir)
}
const buildLibBlsct = (cfg, numCpus, depArchDir) => {
// run autogen.sh
console.log('Running autogen.sh...')
const autogenRes = spawnSync('./autogen.sh', [], { cwd: cfg.navioCoreDir })
if (autogenRes.status !== 0) {
throw new Error(`autogen.sh failed: ${JSON.stringify(autogenRes)}`)
}
// run configure
console.log(`Running configure...`)
const configureRes = spawnSync('./configure', [
`--prefix=${depArchDir}`,
'--enable-build-libblsct-only'
], {
cwd: cfg.navioCoreDir,
})
if (configureRes.status !== 0) {
throw new Error(`Running configure failed: ${JSON.stringify(configureRes)}`)
}
// build libblsct
console.log('Building libblsct...')
const makeRes = spawnSync('make', ['-j', numCpus], {
cwd: cfg.navioCoreDir,
})
if (makeRes.status !== 0) {
throw new Error(`Building libblsct failed: ${JSON.stringify(makeRes)}`)
}
// prepare a fresh backup dir
if (fs.existsSync(cfg.libsDir)) {
fs.rmSync(cfg.libsDir, { recursive: true, force: true })
}
fs.mkdirSync(cfg.libsDir, { recursive: true })
// copy .a files to the libs dir
const src_dest = cfg.srcDotAFiles.map((src, i) => [src, cfg.destDotAFiles[i]]);
for(const [src, dest] of src_dest) {
console.log(`Copying ${src} to ${dest}...`)
fs.copyFileSync(src, dest)
}
}
const buildSwigWrapper = (cfg) => {
console.log('Building swig wrapper...')
const res = spawnSync(
'swig', ['-c++', '-javascript', '-node', 'blsct.i'],
{ cwd: cfg.swigDir }
)
if (res.status !== 0) {
throw new Error(`Failed to build swig wrapper: ${JSON.stringify(res)}`)
}
}
const main = () => {
const cfg = getCfg(IS_PROD)
const numCpus = os.cpus().length
installSystemDeps()
gitCloneNavioCore(cfg)
// if .a files have been built, copy them from the backup dir
if (cfg.destDotAFiles.every(file => fs.existsSync(file))) {
const src_dest = cfg.srcDotAFiles.map((src, i) => [src, cfg.destDotAFiles[i]]);
for(const [src, dest] of src_dest) {
console.log(`Copying ${dest} to ${src}...`)
fs.copyFileSync(dest, src)
}
}
// otherwise, build them and create backups
else {
depArchDir = buildDepends(cfg, numCpus)
buildLibBlsct(cfg, numCpus, depArchDir)
}
buildSwigWrapper(cfg)
}
main()