Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Nakamoto] fix: update latest tenure change #1605

Merged
merged 1 commit into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 39 additions & 9 deletions packages/transactions/src/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,20 +262,36 @@ export function createNakamotoCoinbasePayload(
export enum TenureChangeCause {
/** A valid winning block-commit */
BlockFound = 0,
/** No winning block-commits */
NoBlockFound = 1,
/** A "null miner" won the block-commit */
NullMiner = 2,
/** The next burnchain block is taking too long, so extend the runtime budget */
Extended = 1,
}

export interface TenureChangePayload {
readonly type: StacksMessageType.Payload;
readonly payloadType: PayloadType.TenureChange;
/**
* The consensus hash of this tenure (hex string). Corresponds to the
* sortition in which the miner of this block was chosen. It may be the case
* that this miner's tenure gets _extended_ acrosssubsequent sortitions; if
* this happens, then this `consensus_hash` value _remains the same _as the
* sortition in which the winning block-commit was mined.
*/
readonly tenureHash: string;
/**
* The consensus hash (hex string) of the previous tenure. Corresponds to the
* sortition of the previous winning block-commit.
*/
readonly previousTenureHash: string;
/**
* Current consensus hash (hex string) on the underlying burnchain.
* Corresponds to the last-seen sortition.
*/
readonly burnViewHash: string;
/** Stacks block hash (hex string) */
readonly previousTenureEnd: string;
/** Number of blocks produced in the previous tenure */
/** The number of blocks produced since the last sortition-linked tenure */
readonly previousTenureBlocks: number;
/** Cause of change in mining tenure */
/** The cause of change in mining tenure */
readonly cause: TenureChangeCause;
/** The public key hash of the current tenure (hex string) */
readonly publicKeyHash: string;
Expand All @@ -286,6 +302,9 @@ export interface TenureChangePayload {
}

export function createTenureChangePayload(
tenureHash: string,
previousTenureHash: string,
burnViewHash: string,
previousTenureEnd: string,
previousTenureBlocks: number,
cause: TenureChangeCause,
Expand All @@ -296,6 +315,9 @@ export function createTenureChangePayload(
return {
type: StacksMessageType.Payload,
payloadType: PayloadType.TenureChange,
tenureHash,
previousTenureHash,
burnViewHash,
previousTenureEnd,
previousTenureBlocks,
cause,
Expand Down Expand Up @@ -351,15 +373,17 @@ export function serializePayload(payload: PayloadInput): Uint8Array {
bytesArray.push(payload.vrfProof);
break;
case PayloadType.TenureChange:
bytesArray.push(hexToBytes(payload.tenureHash));
bytesArray.push(hexToBytes(payload.previousTenureHash));
bytesArray.push(hexToBytes(payload.burnViewHash));
bytesArray.push(hexToBytes(payload.previousTenureEnd));
bytesArray.push(writeUInt32BE(new Uint8Array(4), payload.previousTenureBlocks));
bytesArray.push(writeUInt8(new Uint8Array(1), payload.cause));
bytesArray.push(hexToBytes(payload.publicKeyHash));
bytesArray.push(hexToBytes(payload.signature));
const signers = hexToBytes(payload.signers);
bytesArray.push(writeUInt32BE(new Uint8Array(4), signers.byteLength)); // signers length
bytesArray.push(signers);

bytesArray.push(hexToBytes(payload.signature));
break;
}

Expand Down Expand Up @@ -425,16 +449,22 @@ export function deserializePayload(bytesReader: BytesReader): Payload {
return createNakamotoCoinbasePayload(coinbaseBytes, recipient, vrfProof);
}
case PayloadType.TenureChange:
const tenureHash = bytesToHex(bytesReader.readBytes(20));
const previousTenureHash = bytesToHex(bytesReader.readBytes(20));
const burnViewHash = bytesToHex(bytesReader.readBytes(20));
const previousTenureEnd = bytesToHex(bytesReader.readBytes(32));
const previousTenureBlocks = bytesReader.readUInt32BE();
const cause = bytesReader.readUInt8Enum(TenureChangeCause, n => {
throw new Error(`Cannot recognize TenureChangeCause: ${n}`);
});
const publicKeyHash = bytesToHex(bytesReader.readBytes(20));
const signature = bytesToHex(bytesReader.readBytes(65));
const signersLength = bytesReader.readUInt32BE();
const signers = bytesToHex(bytesReader.readBytes(signersLength));
const signature = bytesToHex(bytesReader.readBytes(65));
return createTenureChangePayload(
tenureHash,
previousTenureHash,
burnViewHash,
previousTenureEnd,
previousTenureBlocks,
cause,
Expand Down
4 changes: 2 additions & 2 deletions packages/transactions/tests/builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2146,9 +2146,9 @@ test('Get contract map entry - no match', async () => {
});

test('deserialize/serialize tenure change transaction', () => {
// test vector taken from https://github.com/hirosystems/stacks-encoding-native-js/blob/bba3528685912e30a86f1e35ed62573e43a2aa88/tests/tx-decode-3.0.test.ts#L15
// test vector taken from https://github.com/hirosystems/stacks-encoding-native-js/blob/8e44b2d528191a3fdb7c32ee55bc1e0a25c04f14/tests/tx-decode-3.0.test.ts#L15
const txBytes =
'808000000004000f873150e9790e305b701aa8c7b3bcff9e31a5f9000000000000000000000000000000000001d367da530b92f4984f537f0b903c330eb5158262afa08d67cbbdea6c8e2ecae06008248ac147fc34101d3cc207b1b3e386e0f53732b5548bd5abe1570c2271340302000000000755c9861be5cff984a20ce6d99d4aa65941412889bdc665094136429b84f8c2ee00000001000000000000000000000000000000000000000000000000000279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980000000000000000000000000000000000000000000000000000000000000000';
'808000000004001dc27eba0247f8cc9575e7d45e50a0bc7e72427d000000000000001d000000000000000000011dc72b6dfd9b36e414a2709e3b01eb5bbdd158f9bc77cd2ca6c3c8b0c803613e2189f6dacf709b34e8182e99d3a1af15812b75e59357d9c255c772695998665f010200000000076f2ff2c4517ab683bf2d588727f09603cc3e9328b9c500e21a939ead57c0560af8a3a132bd7d56566f2ff2c4517ab683bf2d588727f09603cc3e932828dcefb98f6b221eef731cabec7538314441c1e0ff06b44c22085d41aae447c1000000010014ff3cb19986645fd7e71282ad9fea07d540a60e0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798000000000000000000000000000000000000000000000000000000000000000000000000';
const transaction = deserializeTransaction(txBytes);

expect(bytesToHex(transaction.serialize())).toEqual(txBytes);
Expand Down
8 changes: 7 additions & 1 deletion packages/transactions/tests/payload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,20 @@ test('Coinbase to contract principal recipient payload serialization and deseria
});

test('serialize/deserialize tenure change payload', () => {
const tenureHash = bytesToHex(randomBytes(20));
const previousTenureHash = bytesToHex(randomBytes(20));
const burnViewHash = bytesToHex(randomBytes(20));
const previousTenureEnd = bytesToHex(randomBytes(32));
const previousTenureBlocks = 100;
const cause = TenureChangeCause.NullMiner;
const cause = TenureChangeCause.Extended;
const publicKeyHash = bytesToHex(randomBytes(20));
const signers = bytesToHex(randomBytes(21));
const signature = bytesToHex(randomBytes(65));

const payload = createTenureChangePayload(
tenureHash,
previousTenureHash,
burnViewHash,
previousTenureEnd,
previousTenureBlocks,
cause,
Expand Down
Loading