Skip to content

Commit

Permalink
fix: implement latest tenure change spec
Browse files Browse the repository at this point in the history
  • Loading branch information
janniks committed Dec 13, 2023
1 parent 60d75fa commit 54ec435
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 deletions.
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

0 comments on commit 54ec435

Please sign in to comment.