Skip to content

Commit

Permalink
hotfix: Staker and finality provider public keys should not be the same
Browse files Browse the repository at this point in the history
  • Loading branch information
vitsalis committed Jun 28, 2024
1 parent f8f9f78 commit 06afb58
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,5 @@ $RECYCLE.BIN/
*.lnk

# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows
*.swp
*.swo
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "btc-staking-ts",
"version": "0.2.5",
"version": "0.2.6",
"description": "Library exposing methods for the creation and consumption of Bitcoin transactions pertaining to Babylon's Bitcoin Staking protocol. Experimental version, should not be used for production purposes or with real funds.",
"module": "dist/index.js",
"main": "dist/index.cjs",
Expand Down Expand Up @@ -60,4 +60,4 @@
"@bitcoin-js/tiny-secp256k1-asmjs": "^2.2.3",
"bitcoinjs-lib": "^6.1.5"
}
}
}
30 changes: 28 additions & 2 deletions src/utils/stakingScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { StakingScripts } from "../types/StakingScripts";

// PK_LENGTH denotes the length of a public key in bytes
export const PK_LENGTH = 32;
export const MAGIC_BYTES_LEN = 4;

// StakingScriptData is a class that holds the data required for the BTC Staking Script
// and exposes methods for converting it into useful formats
Expand Down Expand Up @@ -90,10 +91,35 @@ export class StakingScriptData {
) {
return false;
}
// check that maximum value for staking time is not greater than uint16
if (this.#stakingTimeLock > 65535) {

// Check whether we have any duplicate keys
const allPks = [this.#stakerKey, ...this.#finalityProviderKeys, ...this.#covenantKeys];
const allPksSet = new Set(allPks);
if (allPks.length !== allPksSet.size) {
return false;
}

// check that the threshold is above 0 and less than or equal to
// the size of the covenant emulators set
if (this.#covenantThreshold == 0 || this.#covenantThreshold > this.#covenantKeys.length) {
return false;
}

// check that maximum value for staking time is not greater than uint16 and above 0
if (this.#stakingTimeLock == 0 || this.#stakingTimeLock > 65535) {
return false;
}

// check that maximum value for unbonding time is not greater than uint16 and above 0
if (this.#unbondingTimeLock == 0 || this.#unbondingTimeLock > 65535) {
return false;
}

// check that the magic bytes are 4 in length
if (this.#magicBytes.length != MAGIC_BYTES_LEN) {
return false;
}

return true;
}

Expand Down
198 changes: 198 additions & 0 deletions tests/utils/stakingScript.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { StakingScriptData } from "../../src/utils/stakingScript";

describe("stakingScript", () => {
describe("Input Fields Validation", () => {
const pk1 = Buffer.from("6f13a6d104446520d1757caec13eaf6fbcf29f488c31e0107e7351d4994cd068", "hex");
const pk2 = Buffer.from("f5199efae3f28bb82476163a7e458c7ad445d9bffb0682d10d3bdb2cb41f8e8e", "hex");
const pk3 = Buffer.from("17921cf156ccb4e73d428f996ed11b245313e37e27c978ac4d2cc21eca4672e4", "hex");
const pk4 = Buffer.from("76d1ae01f8fb6bf30108731c884cddcf57ef6eef2d9d9559e130894e0e40c62c", "hex");
const pk5 = Buffer.from("49766ccd9e3cd94343e2040474a77fb37cdfd30530d05f9f1e96ae1e2102c86e", "hex");
const invalidPk = Buffer.from("6f13a6d104446520d1757caec13eaf6fbcf29f488c31e0107e7351d4994cd0", "hex");
const emptyBuffer = Buffer.from("", "hex");
const stakingTimeLock = 65535;
const unbondingTimeLock = 1000;
const magicBytes = Buffer.from("62626234", "hex");
it("should fail if the staker key is not 32 bytes", () => {
expect(() =>
new StakingScriptData(
invalidPk, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
unbondingTimeLock,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if a finality provider key is not 32 bytes", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2, invalidPk], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
unbondingTimeLock,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if a covenant emulator key is not 32 bytes", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2, pk3], // Finality Provider Pks
[pk4, invalidPk, pk5], // covenant Pks
2,
stakingTimeLock,
unbondingTimeLock,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if the covenant emulators threshold is 0", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
0,
stakingTimeLock,
unbondingTimeLock,
magicBytes
)
).toThrow("Missing required input values");
});
it("should fail if the covenant emulators threshold is larger than the covenant emulators", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
4,
stakingTimeLock,
unbondingTimeLock,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if the staking timelock is 0", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
0,
unbondingTimeLock,
magicBytes
)
).toThrow("Missing required input values");
});
it("should fail if the staking timelock is above the maximum", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
65536,
unbondingTimeLock,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if the unbonding timelock is 0", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
0,
magicBytes
)
).toThrow("Missing required input values");
});
it("should fail if the unbonding timelock is above the maximum", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
65536,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if the staker pk is in the finality providers list", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2, pk1], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
unbondingTimeLock,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if the staker pk is in the covenants list", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk1, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
unbondingTimeLock,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if a finality provider pk is in the covenants list", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk2, pk3, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
unbondingTimeLock,
magicBytes
)
).toThrow("Invalid script data provided");
});
it("should fail if the magic bytes are below 4 in length", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
unbondingTimeLock,
Buffer.from("aaaaaa", "hex")
)
).toThrow("Invalid script data provided");
});
it("should fail if the magic bytes are above 4 in length", () => {
expect(() =>
new StakingScriptData(
pk1, // Staker Pk
[pk2], // Finality Provider Pks
[pk3, pk4, pk5], // covenant Pks
2,
stakingTimeLock,
unbondingTimeLock,
Buffer.from("aaaaaaaaaa", "hex")
)
).toThrow("Invalid script data provided");
});
});
});

0 comments on commit 06afb58

Please sign in to comment.