Last active
August 20, 2019 08:34
-
-
Save WilfredTA/9f822b3ef598faa612289adff794aafd to your computer and use it in GitHub Desktop.
Some generator code extending js sdk - experiences errors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs') | |
const Core = require('@nervosnetwork/ckb-sdk-core').default | |
class CKBInterface { | |
constructor(core, nodeUrl, privKey) { | |
this.core = new Core(nodeUrl) | |
this.privKey = privKey | |
} | |
getAddressObject() { | |
if (!this.address) { | |
this.address = this.core.generateAddress(this.privKey) | |
} | |
return this.address | |
} | |
getLockScriptOf(target=null) { | |
if (!this.systemCell) { | |
throw "Must cache system cell info prior to using lock scripts" | |
} | |
let identifier = this.getAddressObject().identifier | |
if (target) { | |
identifier = target | |
} | |
return { | |
codeHash: this.systemCodeHash, | |
args: [`0x${identifier}`] | |
} | |
} | |
getLockHashOf(lockScript=null) { | |
if (!lockScript) { | |
lockScript = this.getLockScriptOf() | |
} | |
return this.core.utils.lockScriptToHash(lockScript) | |
} | |
async cacheSystemCell() { | |
if (!this.systemCell) { | |
this.systemCell = await this.core.loadSystemCell() | |
this.systemCodeHash = this.core.rpc.paramsFormatter.toHash(this.systemCell.codeHash) | |
this.systemCellOutPoint = { | |
blockHash: this.core.rpc.paramsFormatter.toHash(this.systemCell.outPoint.blockHash), | |
cell: { | |
txHash: this.core.rpc.paramsFormatter.toHash(this.systemCell.outPoint.cell.txHash), | |
index: this.systemCell.outPoint.cell.index, | |
}, | |
} | |
} | |
console.log(this.systemCell, " SYS CELL") | |
return this.systemCell | |
} | |
// bytes must be in BigInt | |
bytesToShannons(bytes) { | |
return bytes * BigInt((10**8)) | |
} | |
async installDuktape(address) { | |
try { | |
const duktapeBuildLocation = process.env.DUKTAPE_BUILD_LOCATION || '../ckb-duktape/build/duktape' | |
let duktapeData = fs.readFileSync(duktapeBuildLocation) | |
const duktapeDataSize = duktapeData.byteLength | |
let duktapeDataHash = this.core.utils.blake2b(32, null, null, this.core.utils.PERSONAL); | |
duktapeDataHash = duktapeDataHash.update(duktapeData).digest('hex'); | |
duktapeDataHash = this.core.rpc.paramsFormatter.toHash(duktapeDataHash) | |
duktapeData = this.core.rpc.paramsFormatter.toHash(this.core.utils.bytesToHex(duktapeData)) | |
console.log(duktapeDataSize, 'duktape size') | |
let duktapeCell = { | |
capacity: this.bytesToShannons(BigInt(duktapeDataSize) + 100n), | |
lock: this.getLockScriptOf(), | |
data: duktapeData | |
} | |
console.log(duktapeCell.capacity, "<< REQ cAPACITY") | |
const tx = await this.generateTransaction([duktapeCell]) | |
// const identifier = this.core.rpc.paramsFormatter.toHash(this.getAddressObject().identifier) | |
const signedTx = await this.core.signTransaction(this.getAddressObject())(tx) | |
const duktapeTxHash = await this.core.rpc.sendTransaction(signedTx) | |
this.duktapeDataHash = duktapeDataHash | |
this.duktapeOutPoint = { | |
cell: { | |
txHash: this.core.rpc.paramsFormatter.toHash(duktapeTxHash), | |
index: '0' | |
} | |
} | |
} catch(error) { | |
console.warn(error) | |
} | |
} | |
async getUnspentCells(_lockHash, from, to) { | |
const cellsGroup = [] | |
const STEP = 100 | |
const slidingWindow = async (_lockhash, from, to) => { | |
try { | |
if (from + STEP < to) { | |
const subsetOfCells = await this.core.rpc.getCellsByLockHash(_lockHash, from, from + STEP) | |
cellsGroup.push(subsetOfCells) | |
await slidingWindow(_lockHash, from + STEP, to) | |
} else { | |
const finalSetOfCells = await this.core.rpc | |
.getCellsByLockHash(_lockHash, from, to) | |
cellsGroup.push(finalSetOfCells) | |
} | |
} catch(error) { | |
console.warn(error) | |
} | |
} | |
await slidingWindow(_lockHash, from, to) | |
return cellsGroup.flat() | |
} | |
async gatherAllCells() { | |
try { | |
const lockHash = this.getLockHashOf(); | |
const tipBlockNumber = await this.core.rpc.getTipBlockNumber() | |
const cells = await this.getUnspentCells(lockHash, 0, tipBlockNumber) | |
return cells | |
} catch (error) { | |
console.warn(error); | |
} | |
} | |
// Outputs capacity is expected to be in shannons already, so we do not convert them to shannons | |
// During needed capacity calculation | |
async generateTransaction(outputs, additionalDeps=null) { | |
try { | |
let neededCapacity = 0n | |
let inputs = [] | |
let inputCapacity = 0n | |
let witnesses = [] | |
let cells = await this.gatherAllCells() | |
let deps = [this.systemCellOutPoint] | |
if (additionalDeps) { | |
deps = deps.concat(additionalDeps) | |
} | |
const changeOutput = { | |
capacity: 0n, | |
lock: this.getLockScriptOf(), | |
data: '0x' | |
} | |
// Collect needed capacity for each output | |
outputs.forEach((output) => { | |
neededCapacity += output.capacity | |
}) | |
for (let i = 0; i < cells.length; i++) { | |
const currCell = cells[i] | |
inputs.push({ | |
previousOutput: currCell.outPoint, | |
since: '0', | |
args: [] | |
}) | |
console.log(this.bytesToShannons(BigInt(currCell.capacity)), "<< PUSHED TO INPUTS") | |
inputCapacity += this.bytesToShannons(BigInt(currCell.capacity)) | |
if (inputCapacity >= neededCapacity) { | |
break | |
} | |
} | |
// Check if enough capacity exists | |
if (inputCapacity < neededCapacity) { | |
throw new Error('Not enough capacity to make this transaction') | |
} | |
// Make change | |
if (inputCapacity > neededCapacity) { | |
changeOutput.capacity = inputCapacity - neededCapacity | |
} | |
if (changeOutput.capacity > 0n) { | |
//outputs.push(changeOutput) | |
} | |
console.log(this.bytesToShannons(changeOutput.capacity, "CAPACITY OF CHANGE OUTPUT")) | |
// Format capacities to strings for rpc | |
outputs.forEach((out) => { | |
out.capacity = out.capacity.toString() | |
}) | |
// Add witnesses for every input | |
witnesses = inputs.map((input) => { | |
return { data: [] } | |
}) | |
const tx = { | |
version: '0', | |
deps, | |
inputs, | |
outputs, | |
witnesses | |
} | |
console.log(outputs.length, " NUM OF OUTPUTS") | |
return tx | |
} catch(error) { | |
console.warn(error) | |
} | |
} | |
async sendCapacity(target, capacity) { | |
try { | |
const output = { | |
capacity: BigInt(capacity), | |
lock: { | |
codeHash: this.systemCodeHash, | |
args: [`0x${target}`] | |
}, | |
data: '0x' | |
} | |
const tx = await this.generateTransaction([output]) | |
const signedTx = await this.core.signTransaction(this.getAddressObject())(tx) | |
console.log(signedTx) | |
const capacityTransferTxHash = await this.core.rpc.sendTransaction(signedTx) | |
console.log(capacityTransferTxHash) | |
} catch(error) { | |
console.warn(error) | |
} | |
} | |
} | |
const bootstrap = async () => { | |
const nodeUrl = process.env.NODE_URL || 'http://localhost:8114' // example node url | |
const privateKey = process.env.PRIV_KEY || '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' // example private key | |
let ckbGateway = new CKBInterface(Core, nodeUrl, privateKey) | |
await ckbGateway.cacheSystemCell() | |
const targetId = ckbGateway.getAddressObject().identifier | |
//const unspentCells = await ckbGateway.gatherAllCells() | |
await ckbGateway.installDuktape() // This results in Error: {"code":-3,"message":"InvalidTx(InsufficientCellCapacity)"} Sometimes | |
// Other times, it results in Error: {"code":-3,"message":"InvalidTx(OutputsSumOverflow)"} | |
//await ckbGateway.sendCapacity(targetId, 600000000) // this works | |
} | |
bootstrap() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment