-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(rgbpp-ckb): build btc transfer virtual ckb tx #13
Merged
Merged
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
5df87f5
add rgbpp transfer type
duanyytop d49c0b5
add rgbpp types
duanyytop 8fbe2fa
add collector methods
duanyytop 7f9c9ad
update secp256k1 constants
duanyytop d429b07
add ckb tx and rpbpp utils
duanyytop 70a15f6
add custom errors
duanyytop aa04831
feat: add L1 transfer constructor
duanyytop 2905812
feat: add appendPaymasterCell for sevice
duanyytop 80bd6c4
feat: add signRgbppTx for service
duanyytop 19efef4
refactor: update appendPaymasterCellAndSignTx
duanyytop 5c7ceb3
refacotr: update builder types
duanyytop dc35769
add sendCkbTx for service
duanyytop e1f581b
chore: update github actions
duanyytop 39fc5b9
add github actions lint
duanyytop ffc7a21
fix main rebase conflict
duanyytop bc7886e
Update packages/ckb/src/utils/rgbpp.ts
duanyytop 0d18e28
refactor: update l1/l2 to btc/ckb
duanyytop ec07a01
refactor: Update collector types
duanyytop d864f35
update multi input locks networks
duanyytop e586c4f
update calculateCommitment
duanyytop 0057411
add calculateCommitment test
duanyytop dabbb8a
add hex utils tests
duanyytop File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
import axios from 'axios'; | ||
import CKB from '@nervosnetwork/ckb-sdk-core'; | ||
import { toCamelcase } from '../utils/case-parser'; | ||
import { CollectResult, IndexerCell } from '../types/collector'; | ||
import { CollectResult, CollectUdtResult, IndexerCell } from '../types/collector'; | ||
import { MIN_CAPACITY } from '../constants'; | ||
import { CapacityNotEnoughError, IndexerError } from '../error'; | ||
import { CapacityNotEnoughError, IndexerError, UdtAmountNotEnoughError } from '../error'; | ||
import { leToU128 } from '../utils'; | ||
|
||
const parseScript = (script: CKBComponents.Script) => ({ | ||
code_hash: script.codeHash, | ||
|
@@ -111,4 +112,34 @@ export class Collector { | |
} | ||
return { inputs, capacity: sum }; | ||
} | ||
|
||
collectUdtInputs(liveCells: IndexerCell[], needAmount: bigint): CollectUdtResult { | ||
let inputs: CKBComponents.CellInput[] = []; | ||
let sumCapacity = BigInt(0); | ||
let sumAmount = BigInt(0); | ||
for (let cell of liveCells) { | ||
inputs.push({ | ||
previousOutput: { | ||
txHash: cell.outPoint.txHash, | ||
index: cell.outPoint.index, | ||
}, | ||
since: '0x0', | ||
}); | ||
sumCapacity = sumCapacity + BigInt(cell.output.capacity); | ||
sumAmount += leToU128(cell.outputData); | ||
if (sumAmount >= needAmount) { | ||
break; | ||
} | ||
} | ||
if (sumAmount < needAmount) { | ||
throw new UdtAmountNotEnoughError('Insufficient UDT balance'); | ||
} | ||
return { inputs, capacity: sumCapacity, amount: sumAmount }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated ec07a01 |
||
} | ||
|
||
async getLiveCell(outPoint: CKBComponents.OutPoint): Promise<CKBComponents.LiveCell> { | ||
const ckb = new CKB(this.ckbNodeUrl); | ||
const { cell } = await ckb.rpc.getLiveCell(outPoint, true); | ||
return cell; | ||
} | ||
} |
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { | ||
getTransactionSize, | ||
rawTransactionToHash, | ||
scriptToHash, | ||
serializeWitnessArgs, | ||
} from '@nervosnetwork/ckb-sdk-utils'; | ||
import { AppendPaymasterCellAndSignTxParams, AppendWitnessesParams, SendTxParams } from '../types'; | ||
import { SECP256K1_WITNESS_LOCK_LEN, getRgbppLockScript } from '../constants'; | ||
import { append0x, calculateTransactionFee } from '../utils'; | ||
import { InputsCapacityNotEnoughError } from '../error'; | ||
import signWitnesses from '@nervosnetwork/ckb-sdk-core/lib/signWitnesses'; | ||
|
||
//TODO: waiting for SPV proof | ||
export const appendWitnesses = ({ ckbRawTx, sumInputsCapacity, needPaymasterCell }: AppendWitnessesParams) => { | ||
if (!needPaymasterCell) { | ||
let rawTx = ckbRawTx; | ||
const partialOutputsCapacity = rawTx.outputs | ||
.slice(0, rawTx.outputs.length - 1) | ||
.map((output) => BigInt(output.capacity)) | ||
.reduce((prev, current) => prev + current, BigInt(0)); | ||
|
||
if (sumInputsCapacity <= partialOutputsCapacity) { | ||
throw new InputsCapacityNotEnoughError('The sum of inputs capacity is not enough'); | ||
} | ||
|
||
const txSize = getTransactionSize(rawTx) + SECP256K1_WITNESS_LOCK_LEN; | ||
const estimatedTxFee = calculateTransactionFee(txSize); | ||
|
||
const changeCapacity = sumInputsCapacity - partialOutputsCapacity - estimatedTxFee; | ||
rawTx.outputs[rawTx.outputs.length - 1].capacity = append0x(changeCapacity.toString(16)); | ||
return rawTx; | ||
} | ||
}; | ||
|
||
export const appendPaymasterCellAndSignTx = async ({ | ||
secp256k1PrivateKey, | ||
ckbRawTx, | ||
sumInputsCapacity, | ||
paymasterCell, | ||
}: AppendPaymasterCellAndSignTxParams) => { | ||
let rawTx = ckbRawTx; | ||
const paymasterInput = { previousOutput: paymasterCell.outPoint, since: '0x0' }; | ||
rawTx.inputs = [paymasterInput, ...rawTx.inputs]; | ||
const inputsCapacity = sumInputsCapacity + BigInt(paymasterCell.output.capacity); | ||
|
||
const sumOutputsCapacity = rawTx.outputs | ||
.map((output) => BigInt(output.capacity)) | ||
.reduce((prev, current) => prev + current, BigInt(0)); | ||
|
||
const txSize = getTransactionSize(rawTx) + SECP256K1_WITNESS_LOCK_LEN; | ||
const estimatedTxFee = calculateTransactionFee(txSize); | ||
|
||
if (inputsCapacity <= sumOutputsCapacity) { | ||
throw new InputsCapacityNotEnoughError('The sum of inputs capacity is not enough'); | ||
} | ||
const changeCapacity = inputsCapacity - sumOutputsCapacity - estimatedTxFee; | ||
rawTx.outputs[rawTx.outputs.length - 1].capacity = append0x(changeCapacity.toString(16)); | ||
|
||
let keyMap = new Map<string, string>(); | ||
keyMap.set(scriptToHash(paymasterCell.output.lock), secp256k1PrivateKey); | ||
// The 0x00 is placeholder for rpbpp cell and it has no effect on transaction signatures | ||
keyMap.set('0x00', ''); | ||
|
||
const cells = ckbRawTx.inputs.map((input, index) => ({ | ||
outPoint: input.previousOutput, | ||
lock: index === 0 ? paymasterCell.output.lock : getRgbppLockScript(false), | ||
})); | ||
|
||
const transactionHash = rawTransactionToHash(rawTx); | ||
const signedWitnesses = signWitnesses(keyMap)({ | ||
transactionHash, | ||
witnesses: rawTx.witnesses, | ||
inputCells: cells, | ||
skipMissingKeys: true, | ||
}); | ||
const emptyWitness = { lock: '', inputType: '', outputType: '' }; | ||
const signedTx = { | ||
...rawTx, | ||
witnesses: signedWitnesses.map((witness, index) => (index === 0 ? serializeWitnessArgs(emptyWitness) : witness)), | ||
}; | ||
return signedTx; | ||
}; | ||
|
||
export const sendCkbTx = async ({ collector, signedTx }: SendTxParams) => { | ||
const txHash = await collector.getCkb().rpc.sendTransaction(signedTx, 'passthrough'); | ||
return txHash; | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './l1-transfer'; | ||
export * from './builder'; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { RgbppL1TransferVirtualParams, RgbppL1TransferVirtualResult, RgbppL1TransferVirtualTx } from '../types/rgbpp'; | ||
import { blockchain } from '@ckb-lumos/base'; | ||
import { NoRgbppLiveCellError } from '../error'; | ||
import { append0x, calculateRgbppCellCapacity, u128ToLe, u32ToLe } from '../utils'; | ||
import { calculateCommitment, genRgbppLockScript } from '../utils/rgbpp'; | ||
import { IndexerCell } from '../types'; | ||
import { getRgbppLockDep, getSecp256k1CellDep, getXudtDep } from '../constants'; | ||
|
||
export const genTransferL1VirtualTx = async ({ | ||
collector, | ||
xudtTypeBytes, | ||
rgbppLockArgsList, | ||
transferAmount, | ||
isMainnet, | ||
}: RgbppL1TransferVirtualParams): Promise<RgbppL1TransferVirtualResult> => { | ||
const xudtType = blockchain.Script.unpack(xudtTypeBytes) as CKBComponents.Script; | ||
|
||
const rgbppLocks = rgbppLockArgsList.map((args) => genRgbppLockScript(args, isMainnet)); | ||
let rgbppCells: IndexerCell[] = []; | ||
for await (const rgbppLock of rgbppLocks) { | ||
const cells = await collector.getCells({ lock: rgbppLock, type: xudtType }); | ||
if (!cells || cells.length === 0) { | ||
throw new NoRgbppLiveCellError('No rgb++ cells found with the xudt type script and the rgbpp lock args'); | ||
} | ||
rgbppCells = [...rgbppCells, ...cells]; | ||
} | ||
|
||
const { | ||
inputs, | ||
capacity: sumInputsCapacity, | ||
amount: sumAmount, | ||
} = collector.collectUdtInputs(rgbppCells, transferAmount); | ||
|
||
const rpbppCellCapacity = calculateRgbppCellCapacity(xudtType); | ||
const outputsData = [append0x(u128ToLe(transferAmount))]; | ||
const outputs: CKBComponents.CellOutput[] = [ | ||
{ | ||
lock: genRgbppLockScript(u32ToLe(1)), | ||
type: xudtType, | ||
capacity: append0x(rpbppCellCapacity.toString(16)), | ||
}, | ||
]; | ||
|
||
if (sumAmount > transferAmount) { | ||
outputs.push({ | ||
lock: genRgbppLockScript(u32ToLe(2)), | ||
type: xudtType, | ||
capacity: append0x(rpbppCellCapacity.toString(16)), | ||
}); | ||
outputsData.push(append0x(u128ToLe(sumAmount - transferAmount))); | ||
} | ||
|
||
const cellDeps = [getRgbppLockDep(isMainnet), getXudtDep(isMainnet)]; | ||
const needPaymasterCell = inputs.length < outputs.length; | ||
if (needPaymasterCell) { | ||
cellDeps.push(getSecp256k1CellDep(isMainnet)); | ||
} | ||
const witnesses = inputs.map((_) => '0x'); | ||
|
||
const ckbRawTx: CKBComponents.RawTransaction = { | ||
version: '0x0', | ||
cellDeps, | ||
headerDeps: [], | ||
inputs, | ||
outputs, | ||
outputsData, | ||
witnesses, | ||
}; | ||
|
||
const virtualTx: RgbppL1TransferVirtualTx = { | ||
inputs, | ||
outputs, | ||
outputsData, | ||
}; | ||
const commitment = calculateCommitment(virtualTx); | ||
|
||
return { | ||
ckbRawTx, | ||
commitment, | ||
needPaymasterCell, | ||
sumInputsCapacity, | ||
}; | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
export * from './common'; | ||
export * from './collector'; | ||
export * from './transfer'; | ||
export * from './rgbpp'; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test workflow doesn't seem to have any significant changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding an empty line will be more clear and I don't think it's a bad thing. Do you think so ?
If you think it's necessary to remove the empty line, I will do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree with you. I think breaking steps with an empty line is clearer than collapsing them together. However, I noticed that the only change in the
test.yaml
file in the PR is the addition of more spaces on line 45, rather than adding more line breaks as you suggested.test.yaml
file in the main branch: https://github.com/ckb-cell/rgbpp-sdk/blob/f67460f3e4a2cc1620ea1a59baaa14bc78b51d32/.github/workflows/test.yaml#L43-L47test.yaml
in the current PR: https://github.com/ckb-cell/rgbpp-sdk/blob/dabbb8a4346c30b1416437a13ac3f412cebfce04/.github/workflows/test.yaml#L43-L47