Skip to content

Commit

Permalink
refactor(transaction, signer): add WitnessArgs to transaction
Browse files Browse the repository at this point in the history
with WitnessArgs, developers can get more flexibility in transaction design.

BREAKING CHANGE: required parameter 'witnessArgs' is added to Transaction's contructor. the default
values can be found in static attribute WITNESS_ARGS of Builder.
  • Loading branch information
louzhixian committed Jun 27, 2020
1 parent e7ee1d8 commit e42d02d
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 35 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lay2/pw-core",
"version": "0.2.5",
"version": "0.2.6",
"description": "the javascript sdk of pw-sdk",
"main": "build/main/index.js",
"typings": "build/main/index.d.ts",
Expand Down
12 changes: 12 additions & 0 deletions src/builders/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ const FEE_BASE = 1000;
export abstract class Builder {
static readonly MIN_FEE_RATE = 1000;
static readonly MIN_CHANGE = new Amount('61', AmountUnit.ckb);
static readonly WITNESS_ARGS = {
Secp256k1: {
lock: '0x' + '0'.repeat(130),
input_type: '',
output_type: '',
},
Secp256r1: {
lock: '0x' + '0'.repeat(600),
input_type: '',
output_type: '',
},
};

static calcFee(
tx: Transaction,
Expand Down
27 changes: 10 additions & 17 deletions src/builders/simple-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export class SimpleBuilder extends Builder {
super(feeRate, collector);
}

async build(): Promise<Transaction> {
async build(fee: Amount = new Amount('0')): Promise<Transaction> {
const outputCell = new Cell(this.amount, this.address.toLockScript());
const neededAmount = this.amount.add(Builder.MIN_CHANGE);
const neededAmount = this.amount.add(Builder.MIN_CHANGE).add(fee);
let inputSum = new Amount('0');
const inputCells: Cell[] = [];

Expand Down Expand Up @@ -51,27 +51,20 @@ export class SimpleBuilder extends Builder {
);

const tx = new Transaction(
new RawTransaction(inputCells, [outputCell, changeCell])
new RawTransaction(inputCells, [outputCell, changeCell]),
[Builder.WITNESS_ARGS.Secp256k1]
);

this.fee = Builder.calcFee(tx);

if (this.fee.add(Builder.MIN_CHANGE).gt(changeCell.capacity)) {
// TODO: collect more cells and recalculate fee, until input capacity is
// enough or no more available unspent cells.
throw new Error(
`input capacity not enough, need ${outputCell.capacity
.add(this.fee)
.toString(AmountUnit.ckb)}, got ${inputSum.toString(AmountUnit.ckb)}`
);
if (changeCell.capacity.gte(Builder.MIN_CHANGE.add(this.fee))) {
changeCell.capacity = changeCell.capacity.sub(this.fee);
tx.raw.outputs.pop();
tx.raw.outputs.push(changeCell);
return tx;
}

// sub fee from changeCell
changeCell.capacity = changeCell.capacity.sub(this.fee);
tx.raw.outputs.pop();
tx.raw.outputs.push(changeCell);

return tx;
return this.build(this.fee);
}

getCollector() {
Expand Down
2 changes: 1 addition & 1 deletion src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export default class PWCore {
feeRate?: number
): Promise<string> {
const simpleBuilder = new SimpleBuilder(address, amount, feeRate);
const ethSigner = new EthSigner(address.addressString);
const ethSigner = new EthSigner(PWCore.provider.address.addressString);
return this.sendTransaction(simpleBuilder, ethSigner);
}

Expand Down
6 changes: 5 additions & 1 deletion src/models/transaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import PWCore, {
} from '..';
import { DummyCollector } from '../collectors/dummy-collector';
import { DummyProvider } from '../providers/dummy-provider';
import { Builder } from '../builders';

const test = anyTest as TestInterface<{ tx: Transaction }>;

Expand Down Expand Up @@ -44,7 +45,10 @@ test.before(async (t) => {
const outputs = cells.slice(1);
const cellDeps = [new CellDep(DepType.depGroup, outPoint4)];

t.context.tx = new Transaction(new RawTransaction(inputs, outputs, cellDeps));
t.context.tx = new Transaction(
new RawTransaction(inputs, outputs, cellDeps),
[Builder.WITNESS_ARGS.Secp256k1]
);
});

test('validate', (t) => {
Expand Down
29 changes: 19 additions & 10 deletions src/models/transaction.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import { CKBModel } from '../interfaces';
import { CKBModel, WitnessArgs } from '../interfaces';
import { ECDSA_WITNESS_LEN } from '../constants';
import { validators, normalizers, transformers } from 'ckb-js-toolkit';
// import { SerializeTransaction } from 'ckb-js-toolkit-contrib/src/blockchain';
// import { signer } from 'ckb-js-toolkit-contrib/src';
import { SerializeTransaction } from '@ckb-lumos/types/lib/core';
import { validators, normalizers, transformers, Reader } from 'ckb-js-toolkit';
import {
SerializeTransaction,
SerializeWitnessArgs,
} from '@ckb-lumos/types/lib/core';
import { RawTransaction } from '.';

export class Transaction implements CKBModel {
// TODO: add WitnessArgs to adapt different transaction structures
public witnesses: string[];

constructor(
public raw: RawTransaction,
witnessesLength: number[] = [ECDSA_WITNESS_LEN]
public witnessArgs: WitnessArgs[],
witnessLengths: number[] = [ECDSA_WITNESS_LEN]
) {
// fill witnesses with actural length to make tx size accurate
this.witnesses = raw.inputs.map((_) => '0x');
for (let i = 0; i < witnessesLength.length; i++) {
this.witnesses[i] = '0x' + '0'.repeat(witnessesLength[i] - 2);
for (let i = 0; i < witnessLengths.length; i++) {
this.witnesses[i] = '0x' + '0'.repeat(witnessLengths[i] - 2);
}
if (!Array.isArray(witnessArgs))
throw new Error('[Transaction] - witnessArgs must be an Array!');
for (let i = 0; i < witnessArgs.length; i++) {
this.witnesses[i] = new Reader(
SerializeWitnessArgs(
normalizers.NormalizeWitnessArgs(this.witnessArgs[i])
)
).serializeJson();
}
}

Expand Down
1 change: 1 addition & 0 deletions src/signers/eth-signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class EthSigner extends Signer {
reject(result.error);
}
result = result.result;
let v = Number.parseInt(result.slice(-2), 16);
if (v >= 27) v -= 27;
result = result.slice(0, -2) + v.toString(16).padStart(2, '0');
resolve([result]);
Expand Down
12 changes: 7 additions & 5 deletions src/signers/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ export abstract class Signer {
protected abstract async signMessages(messages: Message[]): Promise<string[]>;

async sign(tx: Transaction): Promise<Transaction> {
tx.witnesses[0] = `0x5500000010000000550000005500000041000000${'0'.repeat(
130
)}`;
console.log('[signer] tx before: ', tx);
const messages = this.toMessages(tx);
const witnesses = await this.signMessages(messages);
witnesses[0] = new Reader(
SerializeWitnessArgs(
normalizers.NormalizeWitnessArgs({ lock: witnesses[0] })
normalizers.NormalizeWitnessArgs({
...tx.witnessArgs[0],
lock: witnesses[0],
})
)
).serializeJson();
FillSignedWitnesses(tx, messages, witnesses);
tx = FillSignedWitnesses(tx, messages, witnesses);
console.log('[signer] tx after: ', tx);

return tx;
}
Expand Down

0 comments on commit e42d02d

Please sign in to comment.