Skip to content

Commit

Permalink
Merge pull request #24 from ckb-cell/schemas-test
Browse files Browse the repository at this point in the history
feat(rgbpp-sdk/ckb): Update schemas build methods and add tests
  • Loading branch information
duanyytop authored Mar 16, 2024
2 parents 9e9a055 + 7622674 commit 82e33e7
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 40 deletions.
1 change: 1 addition & 0 deletions packages/ckb/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const SECP256K1_WITNESS_LOCK_SIZE = 65;
export const BTC_JUMP_CONFIRMATION_BLOCKS = 6;

export const RGBPP_WITNESS_PLACEHOLDER = '0xFF';
export const RGBPP_TX_ID_PLACEHOLDER = '0000000000000000000000000000000000000000000000000000000000000000';

const TestnetInfo = {
Secp256k1LockDep: {
Expand Down
12 changes: 9 additions & 3 deletions packages/ckb/src/rgbpp/btc-jump-ckb.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { RgbppCkbVirtualTx, BtcJumpCkbVirtualTxParams, BtcJumpCkbVirtualTxResult } from '../types/rgbpp';
import { blockchain } from '@ckb-lumos/base';
import { NoRgbppLiveCellError } from '../error';
import { append0x, calculateRgbppCellCapacity, u128ToLe, u32ToLe } from '../utils';
import { calculateCommitment, compareInputs, genBtcTimeLockScript, genRgbppLockScript } from '../utils/rgbpp';
import { append0x, calculateRgbppCellCapacity, u128ToLe } from '../utils';
import {
buildPreLockArgs,
calculateCommitment,
compareInputs,
genBtcTimeLockScript,
genRgbppLockScript,
} from '../utils/rgbpp';
import { Hex, IndexerCell } from '../types';
import { RGBPP_WITNESS_PLACEHOLDER, getRgbppLockDep, getSecp256k1CellDep, getXudtDep } from '../constants';
import { addressToScript } from '@nervosnetwork/ckb-sdk-utils';
Expand Down Expand Up @@ -52,7 +58,7 @@ export const genBtcJumpCkbVirtualTx = async ({

if (sumAmount > transferAmount) {
outputs.push({
lock: genRgbppLockScript(u32ToLe(2), isMainnet),
lock: genRgbppLockScript(buildPreLockArgs(2), isMainnet),
type: xudtType,
capacity: append0x(rpbppCellCapacity.toString(16)),
});
Expand Down
3 changes: 2 additions & 1 deletion packages/ckb/src/rgbpp/btc-time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import {
lockScriptFromBtcTimeLockArgs,
} from '../utils';
import { buildSpvClientCellDep } from '../spv';
import { Bytes } from '../schemas/generated/blockchain';

export const buildBtcTimeUnlockWitness = (btcTxProof: Hex): Hex => {
const btcTimeUnlock = BTCTimeUnlock.pack({ btcTxProof });
const btcTimeUnlock = BTCTimeUnlock.pack({ btcTxProof: Bytes.pack(btcTxProof) });
return append0x(bytesToHex(btcTimeUnlock));
};

Expand Down
6 changes: 3 additions & 3 deletions packages/ckb/src/rgbpp/btc-transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BtcTransferVirtualTxParams, BtcTransferVirtualTxResult, RgbppCkbVirtual
import { blockchain } from '@ckb-lumos/base';
import { NoRgbppLiveCellError } from '../error';
import { append0x, calculateRgbppCellCapacity, u128ToLe, u32ToLe } from '../utils';
import { calculateCommitment, compareInputs, genRgbppLockScript } from '../utils/rgbpp';
import { buildPreLockArgs, calculateCommitment, compareInputs, genRgbppLockScript } from '../utils/rgbpp';
import { Hex, IndexerCell } from '../types';
import { RGBPP_WITNESS_PLACEHOLDER, getRgbppLockDep, getSecp256k1CellDep, getXudtDep } from '../constants';

Expand Down Expand Up @@ -42,15 +42,15 @@ export const genBtcTransferCkbVirtualTx = async ({
// The Vouts[0] for OP_RETURN and Vouts[1], Vouts[2] for RGBPP assets
const outputs: CKBComponents.CellOutput[] = [
{
lock: genRgbppLockScript(u32ToLe(1), isMainnet),
lock: genRgbppLockScript(buildPreLockArgs(1), isMainnet),
type: xudtType,
capacity: append0x(rpbppCellCapacity.toString(16)),
},
];

if (sumAmount > transferAmount) {
outputs.push({
lock: genRgbppLockScript(u32ToLe(2), isMainnet),
lock: genRgbppLockScript(buildPreLockArgs(2), isMainnet),
type: xudtType,
capacity: append0x(rpbppCellCapacity.toString(16)),
});
Expand Down
19 changes: 14 additions & 5 deletions packages/ckb/src/rgbpp/ckb-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,26 @@ import { InputsCapacityNotEnoughError } from '../error';
import signWitnesses from '@nervosnetwork/ckb-sdk-core/lib/signWitnesses';
import { buildSpvClientCellDep } from '../spv';
import { RGBPPUnlock, Uint16 } from '../schemas/generated/rgbpp';
import { Bytes } from '@ckb-lumos/base/lib/blockchain';

export const buildRgbppUnlockWitness = (
btcTxBytes: Hex,
btcTxProof: Hex,
ckbRawTx: CKBComponents.RawTransaction,
inputsLen: number,
outputsLen: number,
): Hex => {
const inputLen = append0x(u8ToHex(ckbRawTx.inputs.length));
const outputLen = append0x(u8ToHex(ckbRawTx.outputs.length));
const inputLen = append0x(u8ToHex(inputsLen));
const outputLen = append0x(u8ToHex(outputsLen));

const btcTx = Bytes.pack(btcTxBytes);

const version = Uint16.pack([0, 0]);
const rgbppUnlock = RGBPPUnlock.pack({ version, extraData: { inputLen, outputLen }, btcTx: btcTxBytes, btcTxProof });
const rgbppUnlock = RGBPPUnlock.pack({
version,
extraData: { inputLen, outputLen },
btcTx,
btcTxProof: Bytes.pack(btcTxProof),
});
return append0x(bytesToHex(rgbppUnlock));
};

Expand All @@ -55,7 +64,7 @@ export const appendCkbTxWitnesses = async ({
const { spvClient, proof } = await spvService.fetchSpvClientCellAndTxProof({ btcTxId, confirmBlocks: 0 });
rawTx.cellDeps.push(buildSpvClientCellDep(spvClient));

const rgbppUnlock = buildRgbppUnlockWitness(btcTxBytes, proof, ckbRawTx);
const rgbppUnlock = buildRgbppUnlockWitness(btcTxBytes, proof, ckbRawTx.inputs.length, ckbRawTx.outputs.length);
rawTx.witnesses = rawTx.witnesses.map((witness) => (witness === RGBPP_WITNESS_PLACEHOLDER ? rgbppUnlock : witness));

if (!needPaymasterCell) {
Expand Down
8 changes: 4 additions & 4 deletions packages/ckb/src/rgbpp/ckb-jump-btc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CkbJumpBtcVirtualTxParams, RgbppCkbVirtualTx } from '../types/rgbpp';
import { CkbJumpBtcVirtualTxParams } from '../types/rgbpp';
import { blockchain } from '@ckb-lumos/base';
import { NoLiveCellError, NoXudtLiveCellError } from '../error';
import {
Expand All @@ -9,8 +9,8 @@ import {
remove0x,
u128ToLe,
} from '../utils';
import { genRgbppLockScript } from '../utils/rgbpp';
import { MAX_FEE, MIN_CAPACITY, SECP256K1_WITNESS_LOCK_SIZE, getXudtDep } from '../constants';
import { buildPreLockArgs, genRgbppLockScript } from '../utils/rgbpp';
import { MAX_FEE, SECP256K1_WITNESS_LOCK_SIZE, getXudtDep } from '../constants';
import { addressToScript, getTransactionSize } from '@nervosnetwork/ckb-sdk-utils';

/**
Expand Down Expand Up @@ -48,7 +48,7 @@ export const genCkbJumpBtcVirtualTx = async ({
const outIndex = remove0x(toRgbppLockArgs).substring(0, 8);
const outputs: CKBComponents.CellOutput[] = [
{
lock: genRgbppLockScript(outIndex, isMainnet),
lock: genRgbppLockScript(buildPreLockArgs(outIndex), isMainnet),
type: xudtType,
capacity: append0x(rpbppCellCapacity.toString(16)),
},
Expand Down
24 changes: 24 additions & 0 deletions packages/ckb/src/rgbpp/schemas.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, it, expect } from 'vitest';
import { buildRgbppUnlockWitness } from './ckb-builder';
import { buildBtcTimeUnlockWitness } from './btc-time';

describe('RGBPP schemas', () => {
// The test data is from RGBPP lock contract test
it('buildRgbppUnlockWitness', async () => {
const btcTx =
'0x000000000156e156abc593f2d07b496c52f767c48e85c9a0e7f2707acee20cb4feca8fb3a5000000002094ee8a1d82d77738655e09affc6903021a97a143afcc5cd02afb052150c39537000000000200000000000001f4226a201bd6b07a2e10e6d0e8c80ef629ed919795d80860b7bf48a7fe54c1d32291f4d800000000000001f4202f1275473ae8a84d5ba4ec48d4a9cf9aeee4a62d711c620a7d63ad87e9d300ac00000000';
const txProof = '0x';
const inputsLen = 1;
const outputsLen = 2;
const rgbppUnlock = buildRgbppUnlockWitness(btcTx, txProof, inputsLen, outputsLen);
expect(
'0xc7000000140000001600000018000000c300000000000102a7000000000000000156e156abc593f2d07b496c52f767c48e85c9a0e7f2707acee20cb4feca8fb3a5000000002094ee8a1d82d77738655e09affc6903021a97a143afcc5cd02afb052150c39537000000000200000000000001f4226a201bd6b07a2e10e6d0e8c80ef629ed919795d80860b7bf48a7fe54c1d32291f4d800000000000001f4202f1275473ae8a84d5ba4ec48d4a9cf9aeee4a62d711c620a7d63ad87e9d300ac0000000000000000',
).toBe(rgbppUnlock);
});

it('buildBtcTimeUnlockWitness', async () => {
const txProof = '0xb93dc09afbb463577ca0149726840f204f4cb29490d115a1cf8c1018f05f5296';
const btcTimeUnlock = buildBtcTimeUnlockWitness(txProof);
expect('0x2800000008000000b93dc09afbb463577ca0149726840f204f4cb29490d115a1cf8c1018f05f5296').toBe(btcTimeUnlock);
});
});
52 changes: 36 additions & 16 deletions packages/ckb/src/utils/rgbpp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { sha256 } from 'js-sha256';
import { hexToBytes } from '@nervosnetwork/ckb-sdk-utils';
import {
btcTxIdFromBtcTimeLockArgs,
buildPreLockArgs,
calculateCommitment,
genBtcTimeLockScript,
lockScriptFromBtcTimeLockArgs,
Expand All @@ -17,37 +18,49 @@ describe('rgbpp tests', () => {
expect(hash).toBe('c0a45d9d7c024adcc8076c18b3f07c08de7c42120cdb7e6cbc05a28266b15b5f');
});

// TODO: Check the hash result with the data from the rgbpp lock script test case
it('calculateCommitment', async () => {
const virtualTx: RgbppCkbVirtualTx = {
it('calculateCommitment with the test data which is from RGBPP lock contract test cases', async () => {
const rgbppVirtualTx: RgbppCkbVirtualTx = {
inputs: [
{
previousOutput: {
index: '0xffffffff',
txHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
txHash: '0xb93dc09afbb463577ca0149726840f204f4cb29490d115a1cf8c1018f05f5296',
index: '0x0',
},
since: '0x400',
since: '0x0',
},
],
outputs: [
{
capacity: '0x18e64b61cf',
lock: {
args: '0x',
codeHash: '0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5',
hashType: 'data',
codeHash: '0x6baadd6f224432eec24b4031b66e1bcef690963205ec3edf6aa0b35570c429f6',
hashType: 'type',
args: '0x010000000000000000000000000000000000000000000000000000000000000000000000',
},
capacity: '0x0000000000000000',
type: {
args: '0x',
codeHash: '0x28e83a1277d48add8e72fadaa9248559e1b632bab2bd60b27955ebc4c03800a5',
hashType: 'data',
codeHash: '0xdb9b4c309f25738b21e7278c803e00a7dcf81115f448ae87b42463f4136821e7',
hashType: 'type',
args: '0x385c913dd83f7255922df8f5126511652a3ecf7e015849d9cb90558d9b81c5c2',
},
},
{
lock: {
codeHash: '0x6baadd6f224432eec24b4031b66e1bcef690963205ec3edf6aa0b35570c429f6',
hashType: 'type',
args: '0x010000000000000000000000000000000000000000000000000000000000000000000000',
},
capacity: '0x0000000000000000',
type: {
codeHash: '0xdb9b4c309f25738b21e7278c803e00a7dcf81115f448ae87b42463f4136821e7',
hashType: 'type',
args: '0x385c913dd83f7255922df8f5126511652a3ecf7e015849d9cb90558d9b81c5c2',
},
},
],
outputsData: ['0xc0a45d9d7c024adcc8076c18b3f07c08de7c42120cdb7e6cbc05a28266b15b5f'],
outputsData: ['0x2c010000000000000000000000000000', '0xbc020000000000000000000000000000'],
};
const hash = calculateCommitment(virtualTx);
expect(hash).toBe('6c6077f12059b6d8534a3a6e893a2268fe12d3456d1bf5c029fe66615e016c21');
const commitment = calculateCommitment(rgbppVirtualTx);
expect('150c5f0856ba6d9148bd2588d8a3d9a1f45eec8a34fe0ca426a1b5193d5a701c').toBe(commitment);
});

it('genBtcTimeLockScript', async () => {
Expand Down Expand Up @@ -91,4 +104,11 @@ describe('rgbpp tests', () => {
const capacity = calculateUdtCellCapacity(joyIDLock, xudtType);
expect(BigInt(145_0000_0000)).toBe(capacity);
});

it('buildPreLockArgs', async () => {
expect('020000000000000000000000000000000000000000000000000000000000000000000000').toBe(buildPreLockArgs(2));

const lockArgs = buildPreLockArgs('02000000');
expect('020000000000000000000000000000000000000000000000000000000000000000000000').toBe(lockArgs);
});
});
33 changes: 25 additions & 8 deletions packages/ckb/src/utils/rgbpp.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { sha256 } from 'js-sha256';
import { Hex, IndexerCell, RgbppCkbVirtualTx } from '../types';
import { append0x, remove0x, u16ToLe, u32ToLe, u8ToHex, utf8ToHex } from './hex';
import { BTC_JUMP_CONFIRMATION_BLOCKS, getBtcTimeLockScript, getRgbppLockScript } from '../constants';
import { hexToBytes, serializeOutPoint, serializeOutputs, serializeScript } from '@nervosnetwork/ckb-sdk-utils';
import { append0x, remove0x, u32ToLe, utf8ToHex } from './hex';
import {
BTC_JUMP_CONFIRMATION_BLOCKS,
RGBPP_TX_ID_PLACEHOLDER,
getBtcTimeLockScript,
getRgbppLockScript,
} from '../constants';
import { hexToBytes, serializeOutPoint, serializeOutput, serializeScript } from '@nervosnetwork/ckb-sdk-utils';
import { blockchain } from '@ckb-lumos/base';

export const genRgbppLockScript = (rgbppLockArgs: Hex, isMainnet: boolean) => {
Expand All @@ -24,14 +29,19 @@ export const genBtcTimeLockScript = (toLock: CKBComponents.Script, isMainnet: bo
export const calculateCommitment = (rgbppVirtualTx: RgbppCkbVirtualTx | CKBComponents.RawTransaction): Hex => {
var hash = sha256.create();
hash.update(hexToBytes(utf8ToHex('RGB++')));
const version = u16ToLe(0);
const inputsLen = u8ToHex(rgbppVirtualTx.inputs.length);
const outputsLen = u8ToHex(rgbppVirtualTx.outputs.length);
hash.update(hexToBytes(`0x${version}${inputsLen}${outputsLen}`));
const version = [0, 0];
hash.update(version);
hash.update([rgbppVirtualTx.inputs.length, rgbppVirtualTx.outputs.length]);

for (const input of rgbppVirtualTx.inputs) {
hash.update(hexToBytes(serializeOutPoint(input.previousOutput)));
}
hash.update(hexToBytes(serializeOutputs(rgbppVirtualTx.outputs)));
for (let index = 0; index < rgbppVirtualTx.outputs.length; index++) {
const output = rgbppVirtualTx.outputs[index];
const outputData = rgbppVirtualTx.outputsData[index];
hash.update(hexToBytes(serializeOutput(output)));
hash.update(hexToBytes(outputData));
}
// double sha256
return sha256(hash.array());
};
Expand Down Expand Up @@ -69,6 +79,13 @@ export const isRgbppLockOrBtcTimeLock = (lock: CKBComponents.Script, isMainnet:
return isRgbppLock || isBtcTimeLock;
};

export const buildPreLockArgs = (outIndex: number | string) => {
if (typeof outIndex === 'number') {
return `${u32ToLe(outIndex)}${RGBPP_TX_ID_PLACEHOLDER}`;
}
return `${outIndex}${RGBPP_TX_ID_PLACEHOLDER}`;
};

export const compareInputs = (a: IndexerCell, b: IndexerCell) => {
if (a.output.lock.args < b.output.lock.args) {
return -1;
Expand Down

1 comment on commit 82e33e7

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[{"name":"@rgbpp-sdk/btc","version":"0.0.0-snap-20240316021926"},{"name":"@rgbpp-sdk/ckb","version":"0.0.0-snap-20240316021926"}]

Please sign in to comment.