Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c3b8f7f
setup initial BlsPublicKeyRegistration transaction type
oXtxNt9U Apr 21, 2025
2521708
create BlsPublicKeyRegistration handler and index
oXtxNt9U Apr 21, 2025
c977b8c
Set bls public key on bootstrap
sebastijankuzner Apr 23, 2025
ccfb52b
Fix data type
sebastijankuzner Apr 23, 2025
7ec6394
Rename index
sebastijankuzner Apr 23, 2025
fd8fa93
Remove NotSupportedForSecondSignatureWalletError
sebastijankuzner Apr 23, 2025
ad95b01
Remove NotSupportedForMultiSignatureWalletError
sebastijankuzner Apr 23, 2025
00bf8a9
Remove TODO
sebastijankuzner Apr 25, 2025
7f352e0
Add blsPublicKey schema
sebastijankuzner Apr 25, 2025
3bdce23
Use reference in transaction schema
sebastijankuzner Apr 25, 2025
c33e513
Serialize
sebastijankuzner Apr 25, 2025
8f0937c
Add initial test for bls-public-key-registartion
sebastijankuzner Apr 28, 2025
768f890
Test throwIfCannotBeApplied
sebastijankuzner Apr 28, 2025
61a1937
Test throwIfCannotEnterPool
sebastijankuzner Apr 28, 2025
319d6f2
onPoolEnter/Leave
sebastijankuzner Apr 28, 2025
7b08db5
Apply and revert test
sebastijankuzner Apr 28, 2025
44fe129
Directly use indexes
sebastijankuzner Apr 28, 2025
354b26d
Support bls key swap
sebastijankuzner Apr 28, 2025
c49701c
Fix existing tests
sebastijankuzner Apr 29, 2025
65adaa1
Test swap scenarios
sebastijankuzner Apr 29, 2025
b9ca132
Support event emits
sebastijankuzner Apr 29, 2025
b4f0322
Enable on blsPublicKeyRegistrationEnabled
sebastijankuzner Apr 29, 2025
4ca2346
Typo
sebastijankuzner Apr 29, 2025
858d9d9
Builder tests
sebastijankuzner Apr 30, 2025
c57f13b
Merge branch 'develop' into feat/mainsail/bls2
sebastijankuzner Apr 30, 2025
021af3c
Fix unit tests
sebastijankuzner May 5, 2025
6f75af7
Add basic functional test
sebastijankuzner May 6, 2025
6e63160
Setup CI
sebastijankuzner May 6, 2025
f197efa
Update @arkecosystem/crypto-networks
sebastijankuzner May 6, 2025
eed6806
Check bls public key
sebastijankuzner May 6, 2025
83238cc
Add extra tests
sebastijankuzner May 6, 2025
4819258
Rename attribute to delegate.blsPublicKey
sebastijankuzner May 7, 2025
82e42ee
Update @arkecosystem/crypto-networks
sebastijankuzner May 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/functional.yml
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,61 @@ jobs:
name: ${{ github.job }}-coverage
path: .coverage/functional/${{ github.job }}/lcov.info

bls-public-key-registration-resignation:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:12
env:
POSTGRES_USER: ark
POSTGRES_PASSWORD: password
POSTGRES_DB: ark_unitnet
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

strategy:
matrix:
node-version: [16.x]

steps:
- uses: actions/checkout@v4
- name: Cache node modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-node-
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

- name: Update system
run: sudo apt-get update -y

- name: Install xsel & postgresql-client
run: sudo apt-get install -q xsel postgresql-client

- name: Install and build packages
run: yarn setup

- name: Create .core/database directory
run: mkdir -p $HOME/.core/database

- name: Functional tests
run: yarn test __tests__/functional/transaction-forging/bls-public-key-registration.test.ts --coverage --coverageDirectory .coverage/functional/${{ github.job }}
env:
CORE_DB_DATABASE: ark_unitnet
CORE_DB_USERNAME: ark

- name: Archive code coverage
uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-coverage
path: .coverage/functional/${{ github.job }}/lcov.info

htlc-claim:
runs-on: ubuntu-latest

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ export const setUp = async (): Promise<Contracts.Kernel.Application> => {

Managers.configManager.getMilestone().aip11 = false;
Managers.configManager.getMilestone().htlcEnabled = false;
Managers.configManager.getMilestone().blsPublicKeyRegistrationEnabled = false;

await app.boot();

Managers.configManager.getMilestone().aip11 = true;
Managers.configManager.getMilestone().htlcEnabled = true;
Managers.configManager.getMilestone().blsPublicKeyRegistrationEnabled = true;
});

return sandbox.app;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import "@packages/core-test-framework/src/matchers";

import { Contracts } from "@arkecosystem/core-kernel";
import { Identities } from "@arkecosystem/crypto";
import secrets from "@packages/core-test-framework/src/internal/passphrases.json";
import { snoozeForBlock, TransactionFactory } from "@packages/core-test-framework/src/utils";
import { generateMnemonic } from "bip39";

import * as support from "./__support__";

const genesisPassphrase: string = secrets[0];

let app: Contracts.Kernel.Application;
beforeAll(async () => (app = await support.setUp()));
afterAll(async () => await support.tearDown());

describe("Transaction Forging - Bls Public Key Registration", () => {
describe("Signed with 1 Passphase", () => {
it("should broadcast, accept and forge it", async () => {
// Prepare a fresh wallet for the tests
const passphrase = generateMnemonic();

// Initial Funds
const initialFunds = TransactionFactory.initialize(app)
.transfer(Identities.Address.fromPassphrase(passphrase), 100 * 1e8)
.withPassphrase(genesisPassphrase)
.createOne();

await expect(initialFunds).toBeAccepted();
await snoozeForBlock(1);
await expect(initialFunds.id).toBeForged();

// Register a delegate
const transactionsRegister = TransactionFactory.initialize(app)
.delegateRegistration()
.withPassphrase(passphrase)
.createOne();

await expect(transactionsRegister).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsRegister.id).toBeForged();

// Register bls public key
const transactionsRegisterFirst = TransactionFactory.initialize(app)
.blsPublicKeyRegistration({
newBlsPublicKey: "a".repeat(96),
})
.withPassphrase(passphrase)
.createOne();

await expect(transactionsRegisterFirst).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsRegisterFirst.id).toBeForged();
await expect(transactionsRegisterFirst).blsPublicKeyRegistered();

// Overwrite bls public key
const transactionsRegisterSecond = TransactionFactory.initialize(app)
.blsPublicKeyRegistration({
oldBlsPublicKey: "a".repeat(96),
newBlsPublicKey: "b".repeat(96),
})
.withPassphrase(passphrase)
.createOne();

await expect(transactionsRegisterSecond).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsRegisterSecond.id).toBeForged();
await expect(transactionsRegisterSecond).blsPublicKeyRegistered();
});

it("should reject if bls key is already registered", async () => {
// Prepare a fresh wallet for the tests
const passphrase = generateMnemonic();

// Initial Funds
const initialFunds = TransactionFactory.initialize(app)
.transfer(Identities.Address.fromPassphrase(passphrase), 100 * 1e8)
.withPassphrase(genesisPassphrase)
.createOne();

await expect(initialFunds).toBeAccepted();
await snoozeForBlock(1);
await expect(initialFunds.id).toBeForged();

// Register a delegate
const transactionsRegister = TransactionFactory.initialize(app)
.delegateRegistration()
.withPassphrase(passphrase)
.createOne();

await expect(transactionsRegister).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsRegister.id).toBeForged();

// Register bls public key
const transactionsRegisterFirst = TransactionFactory.initialize(app)
.blsPublicKeyRegistration({
newBlsPublicKey: "b".repeat(96),
})
.withPassphrase(passphrase)
.createOne();

await expect(transactionsRegisterFirst).toBeRejected();
await snoozeForBlock(1);
await expect(transactionsRegisterFirst.id).not.toBeForged();
await expect(transactionsRegisterFirst).not.blsPublicKeyRegistered();
});

it("should reject if delegate is resiged", async () => {
// Prepare a fresh wallet for the tests
const passphrase = generateMnemonic();

// Initial Funds
const initialFunds = TransactionFactory.initialize(app)
.transfer(Identities.Address.fromPassphrase(passphrase), 100 * 1e8)
.withPassphrase(genesisPassphrase)
.createOne();

await expect(initialFunds).toBeAccepted();
await snoozeForBlock(1);
await expect(initialFunds.id).toBeForged();

// Register a delegate
const transactionsRegister = TransactionFactory.initialize(app)
.delegateRegistration()
.withPassphrase(passphrase)
.createOne();

await expect(transactionsRegister).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsRegister.id).toBeForged();

// Resign a delegate
const transactionsResign = TransactionFactory.initialize(app)
.delegateResignation()
.withPassphrase(passphrase)
.createOne();

await expect(transactionsResign).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsResign.id).toBeForged();

// Register bls public key
const transactionsRegisterFirst = TransactionFactory.initialize(app)
.blsPublicKeyRegistration({
newBlsPublicKey: "f".repeat(96),
})
.withPassphrase(passphrase)
.createOne();

await expect(transactionsRegisterFirst).toBeRejected();
await snoozeForBlock(1);
await expect(transactionsRegisterFirst.id).not.toBeForged();
await expect(transactionsRegisterFirst).not.blsPublicKeyRegistered();
});

it("should reject if not from delegate", async () => {
// Prepare a fresh wallet for the tests
const passphrase = generateMnemonic();

// Initial Funds
const initialFunds = TransactionFactory.initialize(app)
.transfer(Identities.Address.fromPassphrase(passphrase), 100 * 1e8)
.withPassphrase(genesisPassphrase)
.createOne();

await expect(initialFunds).toBeAccepted();
await snoozeForBlock(1);
await expect(initialFunds.id).toBeForged();

// Register bls public key
const transactionsRegisterFirst = TransactionFactory.initialize(app)
.blsPublicKeyRegistration({
newBlsPublicKey: "g".repeat(96),
})
.withPassphrase(passphrase)
.createOne();

await expect(transactionsRegisterFirst).toBeRejected();
await snoozeForBlock(1);
await expect(transactionsRegisterFirst.id).not.toBeForged();
await expect(transactionsRegisterFirst).not.blsPublicKeyRegistered();
});
});

describe("Signed with 2 Passphases", () => {
it("should broadcast, accept and forge it", async () => {
// Prepare a fresh wallet for the tests
const passphrase = generateMnemonic();
const secondPassphrase = generateMnemonic();

// Initial Funds
const initialFunds = TransactionFactory.initialize(app)
.transfer(Identities.Address.fromPassphrase(passphrase), 100 * 1e8)
.withPassphrase(genesisPassphrase)
.createOne();

await expect(initialFunds).toBeAccepted();
await snoozeForBlock(1);
await expect(initialFunds.id).toBeForged();

// Register a second passphrase
const secondSignature = TransactionFactory.initialize(app)
.secondSignature(secondPassphrase)
.withPassphrase(passphrase)
.createOne();

await expect(secondSignature).toBeAccepted();
await snoozeForBlock(1);
await expect(secondSignature.id).toBeForged();

// Register a delegate
const transactionsRegister = TransactionFactory.initialize(app)
.delegateRegistration()
.withPassphrasePair({ passphrase, secondPassphrase })
.createOne();

await expect(transactionsRegister).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsRegister.id).toBeForged();

// Register bls public key
const transactionsRegisterFirst = TransactionFactory.initialize(app)
.blsPublicKeyRegistration({
newBlsPublicKey: "c".repeat(96),
})
.withPassphrasePair({ passphrase, secondPassphrase })
.createOne();

await expect(transactionsRegisterFirst).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsRegisterFirst.id).toBeForged();
await expect(transactionsRegisterFirst).blsPublicKeyRegistered();

// Register bls public key
const transactionsRegisterSecond = TransactionFactory.initialize(app)
.blsPublicKeyRegistration({
oldBlsPublicKey: "c".repeat(96),
newBlsPublicKey: "d".repeat(96),
})
.withPassphrasePair({ passphrase, secondPassphrase })
.createOne();

await expect(transactionsRegisterSecond).toBeAccepted();
await snoozeForBlock(1);
await expect(transactionsRegisterSecond.id).toBeForged();
await expect(transactionsRegisterSecond).blsPublicKeyRegistered();
});
});
});
1 change: 1 addition & 0 deletions __tests__/unit/core-api/__support__/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export const initApp = (): Application => {
app.bind(Identifiers.TransactionHandler).to(Two.HtlcLockTransactionHandler);
app.bind(Identifiers.TransactionHandler).to(Two.HtlcClaimTransactionHandler);
app.bind(Identifiers.TransactionHandler).to(Two.HtlcRefundTransactionHandler);
app.bind(Identifiers.TransactionHandler).to(Two.BlsPublicKeyRegistrationTransactionHandler);

app.bind(Identifiers.TransactionHandlerProvider).to(TransactionHandlerProvider).inSingletonScope();
app.bind(Identifiers.TransactionHandlerRegistry).to(TransactionHandlerRegistry).inSingletonScope();
Expand Down
2 changes: 1 addition & 1 deletion __tests__/unit/core-api/controllers/transactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ describe("TransactionsController", () => {
it("should return registered schemas", async () => {
const response = (await controller.schemas(undefined, undefined)) as ItemResponse;

const coreTransactionHandlersCount = 11;
const coreTransactionHandlersCount = 12;
expect(Object.keys(response.data["1"]).length).toBe(coreTransactionHandlersCount);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Contracts } from "@packages/core-kernel";
import { Wallet, WalletRepository, WalletRepositoryCopyOnWrite } from "@packages/core-state/src/wallets";
import {
addressesIndexer,
blsPublicKeysIndexer,
ipfsIndexer,
locksIndexer,
publicKeysIndexer,
Expand Down Expand Up @@ -36,14 +37,15 @@ describe("Wallet Repository Copy On Write", () => {
});

it("should be able to look up indexers", () => {
const expected = ["addresses", "publicKeys", "usernames", "resignations", "locks", "ipfs"];
const expected = ["addresses", "publicKeys", "usernames", "resignations", "locks", "ipfs", "blsPublicKeys"];
expect(walletRepoCopyOnWrite.getIndexNames()).toEqual(expected);
expect(walletRepoCopyOnWrite.getIndex("addresses").indexer).toEqual(addressesIndexer);
expect(walletRepoCopyOnWrite.getIndex("publicKeys").indexer).toEqual(publicKeysIndexer);
expect(walletRepoCopyOnWrite.getIndex("usernames").indexer).toEqual(usernamesIndexer);
expect(walletRepoCopyOnWrite.getIndex("resignations").indexer).toEqual(resignationsIndexer);
expect(walletRepoCopyOnWrite.getIndex("locks").indexer).toEqual(locksIndexer);
expect(walletRepoCopyOnWrite.getIndex("ipfs").indexer).toEqual(ipfsIndexer);
expect(walletRepoCopyOnWrite.getIndex("blsPublicKeys").indexer).toEqual(blsPublicKeysIndexer);
});

it("should find wallets by address", () => {
Expand Down
3 changes: 3 additions & 0 deletions __tests__/unit/core-state/wallets/wallet-repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { Wallet, WalletRepository } from "@packages/core-state/src/wallets";
import {
addressesIndexer,
blsPublicKeysIndexer,
ipfsIndexer,
locksIndexer,
publicKeysIndexer,
Expand Down Expand Up @@ -75,6 +76,7 @@ describe("Wallet Repository", () => {
"resignations",
"locks",
"ipfs",
"blsPublicKeys",
"businesses",
"bridgechains",
];
Expand All @@ -85,6 +87,7 @@ describe("Wallet Repository", () => {
expect(walletRepo.getIndex("resignations").indexer).toEqual(resignationsIndexer);
expect(walletRepo.getIndex("locks").indexer).toEqual(locksIndexer);
expect(walletRepo.getIndex("ipfs").indexer).toEqual(ipfsIndexer);
expect(walletRepo.getIndex("blsPublicKeys").indexer).toEqual(blsPublicKeysIndexer);
expect(() => walletRepo.getIndex("iDontExist")).toThrow();
});

Expand Down
Loading
Loading