Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
janniks committed Nov 14, 2024
1 parent 65b72ac commit 32c6341
Show file tree
Hide file tree
Showing 7 changed files with 684 additions and 55 deletions.
3 changes: 3 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { Wallet, generateNewAccount, generateWallet } from '@stacks/wallet-sdk';
import { Toxiproxy } from 'toxiproxy-node-client';
import { ENV } from './env';
import { withRetry, withTimeout } from './utils';
import { c32addressDecode } from 'c32check';

export function newSocketClient(): StacksApiSocketClient {
return new StacksApiSocketClient({
Expand Down Expand Up @@ -218,6 +219,7 @@ export async function getStackerSet(cycle: number) {
).stacker_set;
}

/** Uses the clarity map entries of the pox contract to get the reward set of a cycle */
export async function getTentativeStackerSet(cycle: number, poxInfo: PoxInfo) {
const [contractAddress, contractName] = poxInfo.contract_id.split('.');
const lenTuple = (await getContractMapEntry({
Expand Down Expand Up @@ -317,6 +319,7 @@ export function getAccount(key: string) {
network.isMainnet() ? NETWORK : TEST_NETWORK
) as string,
client: new StackingClient(address, network),
hashBytes: c32addressDecode(address)[1],
};
}

Expand Down
13 changes: 0 additions & 13 deletions src/tests/misc.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { waitForBurnBlockHeight, getStacksBlock, getStacksBlockRaw } from '../helpers';
import { regtestComposeDown, regtestComposeUp } from '../utils';

test('wip test', async () => {
await waitForBurnBlockHeight(132);
Expand All @@ -10,15 +9,3 @@ test('wip test', async () => {
const blockRaw = await getStacksBlockRaw(block.height);
console.log('stx block raw', blockRaw);
});

test('signer rollover', async () => {
await waitForBurnBlockHeight(110);
console.log(await regtestComposeDown('stacker'));
console.log(await regtestComposeUp('stacker', '--env-file .env-signers-5'));
// cycle 5 (reward phase)
// stacker script should have stacked until 6
// shut off stackers (in cycle 5)
// original signers can take on cycle 6
// power up new stackers (in cycle 6)
// new stackers take on cycle 7
});
2 changes: 1 addition & 1 deletion src/tests/regtest-multiminer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ test('multiple miners are active', async () => {
// get the pubkey hashes from the stacks blocks
// ensure there are EXACTLY TWO unique pubkeys mining

await waitForBurnBlockHeight(130);
await waitForBurnBlockHeight(300);

const height = await getStacksBlockHeight();
const range = Array.from({ length: height - 1 }, (_, i) => i + 1);
Expand Down
340 changes: 340 additions & 0 deletions src/tests/regtest-signer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
import { StackingClient } from '@stacks/stacking';
import {
bitcoindClient,
getPubKeyHashFromTx,
getStacksBlock,
getStacksBlockHeight,
isInPreparePhase,
stacksNetwork,
waitForBurnBlockHeight,
waitForNetwork,
waitForNextCycle,
} from '../helpers';
import { networkEnvDown, networkEnvUp, regtestComposeDown, regtestComposeUp } from '../utils';

const network = stacksNetwork();

beforeEach(async () => {
await networkEnvUp();
await waitForNetwork();
});

afterEach(async () => {
await networkEnvDown();
});

test('signer rollover', async () => {
// TEST CASE
// some signers are active 3/5 (stacking)
// we start two additional signers 5/5 (stacking)

const client = new StackingClient('', stacksNetwork());

// ===========================================================================
// CYCLE 8:
await waitForBurnBlockHeight(165);
expect((await client.getPoxInfo()).reward_cycle_id).toBe(8);

// we're still in the reward phase
let info = await client.getPoxInfo();
expect(isInPreparePhase(info.current_burnchain_block_height as number, info)).toBe(false);

// cycle 8 (reward phase)
// stacker script should have stacked until 9
const cycle8 = await getStacksBlock();
console.log('cycle: 8; stx height:', cycle8.height, cycle8.hash);

// shut off stackers (still in cycle 8)
console.log('shutting off stackers; stx height:', await getStacksBlockHeight());
await regtestComposeDown('stacker');
console.log('shut off all stackers; stx height:', await getStacksBlockHeight());

// ===========================================================================
// CYCLE 9: original signers can take on cycle 9
await waitForNextCycle(await client.getPoxInfo());
expect((await client.getPoxInfo()).reward_cycle_id).toBe(9);

// we're still in the reward phase
info = await client.getPoxInfo();
expect(isInPreparePhase(info.current_burnchain_block_height as number, info)).toBe(false);

// cycle 9 (reward phase)
const cycle9 = await getStacksBlock();
console.log('cycle: 9; stx height:', cycle9.height, cycle9.hash);

// wait for a few blocks to make sure signers can sign cycle 9
info = await client.getPoxInfo();
await waitForBurnBlockHeight((info.current_burnchain_block_height as number) + 5);
// todo: make sure signer set is working

// check pox is active
info = await client.getPoxInfo();
expect(info.current_cycle.is_pox_active).toBe(true);

// power up new stackers (in cycle 9)
console.log('powering up new stackers; stx height:', await getStacksBlockHeight());
console.log(await regtestComposeUp('stacker', '--env-file .env-signers-5'));
console.log('powered up new stackers; stx height:', await getStacksBlockHeight());

// ===========================================================================
// CYCLE 10: new stackers take on cycle 10
await waitForNextCycle(await client.getPoxInfo());
expect((await client.getPoxInfo()).reward_cycle_id).toBe(10);

// we're still in the reward phase
info = await client.getPoxInfo();
expect(isInPreparePhase(info.current_burnchain_block_height as number, info)).toBe(false);

// cycle 10 (reward phase)
const cycle10 = await getStacksBlock();
console.log('cycle: 10; stx height:', cycle10.height, cycle10.hash);

// check pox is active
info = await client.getPoxInfo();
expect(info.current_cycle.is_pox_active).toBe(true);

// ===========================================================================
// make sure 2 cycles from now everything still works
await waitForNextCycle(await client.getPoxInfo());
await waitForNextCycle(await client.getPoxInfo());

// todo: check signer set is different
});

// import * as P from 'micro-packed';
// import { sha512_256 } from '@noble/hashes/sha2';

// export type BitVec = {
// length: number;
// bytes: Uint8Array;
// };

// function dataLen(_len: number) {
// const len = BigInt(_len);
// const extra = len % 8n === 0n ? 0n : 1n;
// return len / 8n + extra;
// }

// export const BitVecCoder = P.wrap({
// encodeStream: (w: P.Writer, data: BitVec) => {
// // const len = data.length;
// P.U16BE.encodeStream(w, data.length);
// P.U32BE.encodeStream(w, data.bytes.length);
// w.bytes(data.bytes);
// },
// decodeStream: (r: P.Reader) => {
// const len = P.U16BE.decodeStream(r);
// // console.log("len", len);
// const bytesLen = dataLen(len);
// // console.log("bytesLen", bytesLen);
// const bytes = P.bytes(P.U32BE).decodeStream(r);
// return {
// length: len,
// bytes,
// };
// },
// });

// export const SignerMessageTypePrefix = {
// /// Block Proposal message from miners
// BlockProposal: 0,
// /// Block Response message from signers
// BlockResponse: 1,
// /// Block Pushed message from miners
// BlockPushed: 2,
// /// Mock block proposal message from Epoch 2.5 miners
// MockProposal: 3,
// /// Mock block signature message from Epoch 2.5 signers
// MockSignature: 4,
// /// Mock block message from Epoch 2.5 miners
// MockBlock: 5,
// } as const;

// export type SignerMessageTypeId =
// (typeof SignerMessageTypePrefix)[keyof typeof SignerMessageTypePrefix];

// export function TypeIdCoder<T extends SignerMessageTypeId>(id: T): P.CoderType<T> {
// return P.wrap({
// encodeStream: (w: P.Writer, value: T) => w.byte(value),
// decodeStream: (r: P.Reader): T => {
// const value = r.byte();
// return value as T;
// },
// validate: (value: T) => {
// if (typeof value !== 'number') throw new Error(`TypeIdCoder: invalid value ${value}`);
// if (value !== id) throw new Error(`TypeIdCoder: invalid value ${value}`);
// return value;
// },
// });
// }

// export const _TypeIdCoder = (id: SignerMessageTypeId): P.CoderType<SignerMessageTypeId> =>
// P.wrap({
// encodeStream: (w: P.Writer, value: SignerMessageTypeId) => w.byte(value),
// decodeStream: (r: P.Reader): SignerMessageTypeId => {
// const value = r.byte();
// return value as SignerMessageTypeId;
// },
// validate: (value: SignerMessageTypeId) => {
// if (typeof value !== 'number') throw new Error(`TypeIdCoder: invalid value ${value}`);
// if (value !== id) throw new Error(`TypeIdCoder: invalid value ${value}`);
// return value;
// },
// });

// export const MockProposalCoder = P.struct({
// typeId: TypeIdCoder(SignerMessageTypePrefix.MockProposal),
// burnBlockHeight: P.U64BE,
// consensusHash: P.bytes(20),
// stacksTip: P.bytes(32),
// stacksHeight: P.U64BE,
// server: P.prefix(P.U8, P.string(null)),
// poxConsensus: P.bytes(20),
// chainId: P.U32BE,
// signature: P.bytes(65),
// });

// export const MockBlockCoder = P.struct({
// typeId: TypeIdCoder(SignerMessageTypePrefix.MockBlock),
// proposal: MockProposalCoder,
// signatures: P.array(P.U32BE, P.bytes(65)),
// rest: P.bytes(null),
// });

// export const BlockProposalCoder = P.struct({
// typeId: TypeIdCoder(SignerMessageTypePrefix.BlockProposal),
// blockVersion: P.U8,
// chainLength: P.U64BE,
// burnSpent: P.U64BE,
// consensusHash: P.bytes(20),
// parentBlockId: P.bytes(32),
// txMerkleRoot: P.bytes(32),
// stateRoot: P.bytes(32),
// timestamp: P.U64BE,
// minerSignature: P.bytes(65),
// signerSignature: P.array(P.U32BE, P.bytes(65)),
// bitvec: BitVecCoder,
// // txs
// // burn height
// // reward cycle
// rest: P.bytes(null),
// });

// export const BlockPushedCoder = P.struct({
// typeId: TypeIdCoder(SignerMessageTypePrefix.BlockPushed),
// blockVersion: P.U8,
// chainLength: P.U64BE,
// burnSpent: P.U64BE,
// consensusHash: P.bytes(20),
// parentBlockId: P.bytes(32),
// txMerkleRoot: P.bytes(32),
// stateRoot: P.bytes(32),
// timestamp: P.U64BE,
// minerSignature: P.bytes(65),
// signerSignature: P.array(P.U32BE, P.bytes(65)),
// bitvec: BitVecCoder,
// // txs
// rest: P.bytes(null),
// });

// export const SignerSignatureHashCoder = P.struct({
// blockVersion: P.U8,
// chainLength: P.U64BE,
// burnSpent: P.U64BE,
// consensusHash: P.bytes(20),
// parentBlockId: P.bytes(32),
// txMerkleRoot: P.bytes(32),
// stateRoot: P.bytes(32),
// timestamp: P.U64BE,
// minerSignature: P.bytes(65),
// bitvec: BitVecCoder,
// });

// export const InverseBoolCoder: P.CoderType<boolean> = /* @__PURE__ */ P.wrap({
// size: 1,
// encodeStream: (w: P.Writer, value: boolean) => w.byte(value ? 0 : 1),
// decodeStream: (r: P.Reader): boolean => {
// const value = r.byte();
// if (value !== 0 && value !== 1) throw r.err(`bool: invalid value ${value}`);
// return value === 0;
// },
// validate: value => {
// if (typeof value !== 'boolean') throw new Error(`bool: invalid value ${value}`);
// return value;
// },
// });

// export const BlockResponseAcceptedCoder = P.struct({
// typeId: TypeIdCoder(SignerMessageTypePrefix.BlockResponse),
// accepted: InverseBoolCoder,
// signerSignatureHash: P.bytes(32),
// signature: P.bytes(65),
// });

// export const BlockResponseRejectedCoder = P.struct({
// typeId: TypeIdCoder(SignerMessageTypePrefix.BlockResponse),
// accepted: InverseBoolCoder,
// reason: P.prefix(P.U32BE, P.string(null)),
// reason_code: P.U8,
// signerSignatureHash: P.bytes(32),
// chain_id: P.U32BE,
// signature: P.bytes(65),
// });

// export type MockBlock = P.UnwrapCoder<typeof MockBlockCoder>;
// export type MockProposal = P.UnwrapCoder<typeof MockProposalCoder>;
// export type BlockProposal = P.UnwrapCoder<typeof BlockProposalCoder>;
// export type BlockResponseAccepted = P.UnwrapCoder<typeof BlockResponseAcceptedCoder>;
// export type BlockResponseRejected = P.UnwrapCoder<typeof BlockResponseRejectedCoder>;
// export type BlockPushed = P.UnwrapCoder<typeof BlockPushedCoder>;

// export type NakamotoBlock = Omit<BlockPushed, 'rest' | 'typeId'>;

// export function chunkTypeName(chunk: Uint8Array) {
// const type = chunk[0];
// return Object.keys(SignerMessageTypePrefix)[type];
// }

// export type DecodedChunk = ReturnType<typeof decodeChunk>;

// export function decodeChunk(chunk: Uint8Array) {
// const type = chunk[0];
// // console.log(Object.keys(SignerMessageTypePrefix));
// // console.log("Type: ", type);
// // console.log(Object.keys(SignerMessageTypePrefix)[type]);
// if (type === SignerMessageTypePrefix.MockBlock) {
// return MockBlockCoder.decode(chunk);
// }
// if (type === SignerMessageTypePrefix.MockProposal) {
// return MockProposalCoder.decode(chunk);
// }
// if (type === SignerMessageTypePrefix.BlockResponse) {
// const accepted = chunk[1];
// if (accepted === 0) {
// return BlockResponseAcceptedCoder.decode(chunk);
// }
// return BlockResponseRejectedCoder.decode(chunk);
// }
// if (type === SignerMessageTypePrefix.BlockProposal) {
// const { rest: remaining, ...proposal } = BlockProposalCoder.decode(chunk);
// const withoutTxs = remaining.slice(-16);
// const extra = P.struct({
// blockHeight: P.U64BE,
// rewardCycle: P.U64BE,
// }).decode(withoutTxs);
// return {
// ...proposal,
// blockHeight: extra.blockHeight,
// rewardCycle: extra.rewardCycle,
// };
// }
// if (type === SignerMessageTypePrefix.BlockPushed) {
// return BlockPushedCoder.decode(chunk);
// }
// throw new Error(`Unknown type: ${type} (${Object.keys(SignerMessageTypePrefix)[type]})`);
// }

// export function makeSignerSignatureHash(block: Omit<NakamotoBlock, 'signer_signature_hash'>) {
// const message = SignerSignatureHashCoder.encode(block);
// return sha512_256(message);
// }
Loading

0 comments on commit 32c6341

Please sign in to comment.