Skip to content

Commit 2ba36f9

Browse files
zone117xjanniks
andauthored
feat: rosetta pox4 stacking support (#1928)
* feat: rosetta pox4 support * fix: optional fields in `/preprocess` * chore: update rosetta pox-4 checks (#1929) Co-authored-by: janniks <[email protected]> * feat: optional pox-max-amount (default to stx amount) * fix: handle signer signature logic only in the rosetta /construction/metadata endpoint * fix: repair stacking w/ Rosetta in `/extended/v1/debug/broadcast/stack` * fix: repair stacking w/o Rosetta in `/extended/v1/debug/broadcast/stack` * fix: rosetta offline tests * fix: rosetta construction tests * fix: progress on fixing pox-4-rosetta tests * test: skip rosetta stacking auto-unlock tests (feature removed in pox-4) * fix: stack for 2 cycles in `rosetta-btc-addr-types` to reduce test flake * docs: update Rosetta schema/docs --------- Co-authored-by: janniks <[email protected]> Co-authored-by: janniks <[email protected]>
1 parent ab98b97 commit 2ba36f9

File tree

11 files changed

+436
-159
lines changed

11 files changed

+436
-159
lines changed

docs/entities/rosetta/rosetta-construction-options.schema.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@
7676
"type": "integer",
7777
"description": "Set the burnchain (BTC) block for stacking lock to start."
7878
},
79+
"reward_cycle_id": {
80+
"type": "integer",
81+
"description": "The reward cycle ID for stacking transaction."
82+
},
7983
"delegate_to": {
8084
"type": "string",
8185
"description": "Delegator address for when calling `delegate-stacking`."
@@ -87,6 +91,22 @@
8791
"signer_key": {
8892
"type": "string",
8993
"description": "The hex-encoded signer key (buff 33) for PoX."
94+
},
95+
"signer_private_key": {
96+
"type": "string",
97+
"description": "The hex-encoded signer private key for PoX. Specify either this or `signer_signature`, otherwise the PoX transaction requires allow-listing from the signer."
98+
},
99+
"signer_signature": {
100+
"type": "string",
101+
"description": "The hex-encoded signer signature for PoX. Specify either this or `signer_private_key`, otherwise the PoX transaction requires allow-listing from the signer."
102+
},
103+
"pox_max_amount": {
104+
"type": "string",
105+
"description": "The maximum amount of STX to stack for PoX. If not specified, the `amount` will be used as the `max-amount` for the PoX transaction."
106+
},
107+
"pox_auth_id": {
108+
"type": "string",
109+
"description": "The auth ID for the PoX transaction. If not specified, a random value will be generated."
90110
}
91111
}
92112
}

docs/generated.d.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2678,6 +2678,10 @@ export interface RosettaOptions {
26782678
* Set the burnchain (BTC) block for stacking lock to start.
26792679
*/
26802680
burn_block_height?: number;
2681+
/**
2682+
* The reward cycle ID for stacking transaction.
2683+
*/
2684+
reward_cycle_id?: number;
26812685
/**
26822686
* Delegator address for when calling `delegate-stacking`.
26832687
*/
@@ -2690,6 +2694,22 @@ export interface RosettaOptions {
26902694
* The hex-encoded signer key (buff 33) for PoX.
26912695
*/
26922696
signer_key?: string;
2697+
/**
2698+
* The hex-encoded signer private key for PoX. Specify either this or `signer_signature`, otherwise the PoX transaction requires allow-listing from the signer.
2699+
*/
2700+
signer_private_key?: string;
2701+
/**
2702+
* The hex-encoded signer signature for PoX. Specify either this or `signer_private_key`, otherwise the PoX transaction requires allow-listing from the signer.
2703+
*/
2704+
signer_signature?: string;
2705+
/**
2706+
* The maximum amount of STX to stack for PoX. If not specified, the `amount` will be used as the `max-amount` for the PoX transaction.
2707+
*/
2708+
pox_max_amount?: string;
2709+
/**
2710+
* The auth ID for the PoX transaction. If not specified, a random value will be generated.
2711+
*/
2712+
pox_auth_id?: string;
26932713
}
26942714
/**
26952715
* The ConstructionMetadataResponse returns network-specific metadata used for transaction construction. Optionally, the implementer can return the suggested fee associated with the transaction being constructed. The caller may use this info to adjust the intent of the transaction or to create a transaction with a different account that can pay the suggested fee. Suggested fee is an array in case fee payment must occur in multiple currencies.

src/api/routes/debug.ts

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ import {
3030
bufferCV,
3131
AnchorMode,
3232
deserializeTransaction,
33+
makeRandomPrivKey,
34+
privateKeyToString,
35+
someCV,
3336
} from '@stacks/transactions';
3437
import { StacksTestnet } from '@stacks/network';
3538
import { SampleContracts } from '../../sample-data/broadcast-contract-default';
@@ -54,7 +57,10 @@ import {
5457
RosettaOperation,
5558
} from '@stacks/stacks-blockchain-api-types';
5659
import { getRosettaNetworkName, RosettaConstants } from '../rosetta-constants';
57-
import { decodeBtcAddress } from '@stacks/stacking';
60+
import { StackingClient, decodeBtcAddress, poxAddressToTuple } from '@stacks/stacking';
61+
import { getPublicKeyFromPrivate } from '@stacks/encryption';
62+
import { randomBytes } from 'node:crypto';
63+
import { hexToBytes } from '@stacks/common';
5864

5965
const testnetAccounts = [
6066
{
@@ -587,6 +593,9 @@ export function createDebugRouter(db: PgStore): express.Router {
587593
btcAddr: string,
588594
cycleCount: number
589595
): Promise<{ txId: string; burnBlockHeight: number }> {
596+
const signerPrivKey = privateKeyToString(makeRandomPrivKey());
597+
const signerPubKey = getPublicKeyFromPrivate(signerPrivKey);
598+
590599
const stackingOperations: RosettaOperation[] = [
591600
{
592601
operation_identifier: {
@@ -607,6 +616,8 @@ export function createDebugRouter(db: PgStore): express.Router {
607616
metadata: {
608617
number_of_cycles: cycleCount,
609618
pox_addr: btcAddr,
619+
signer_key: signerPubKey,
620+
signer_private_key: signerPrivKey,
610621
},
611622
},
612623
{
@@ -723,21 +734,38 @@ export function createDebugRouter(db: PgStore): express.Router {
723734
));
724735
} else {
725736
const [contractAddress, contractName] = poxInfo.contract_id.split('.');
726-
const decodedBtcAddr = decodeBtcAddress(recipient_address);
737+
const poxAddrTuple = poxAddressToTuple(recipient_address);
727738
burnBlockHeight = poxInfo.current_burnchain_block_height as number;
739+
740+
const stackingRpc = new StackingClient('', stacksNetwork);
741+
const signerPrivKey = makeRandomPrivKey();
742+
const signerPubKey = getPublicKeyFromPrivate(signerPrivKey.data);
743+
const authId = `0x${randomBytes(16).toString('hex')}`;
744+
745+
const signerSig = stackingRpc.signPoxSignature({
746+
topic: 'stack-stx',
747+
poxAddress: recipient_address,
748+
rewardCycle: poxInfo.current_cycle.id,
749+
period: cycles,
750+
signerPrivateKey: signerPrivKey,
751+
maxAmount: ustxAmount,
752+
authId: authId,
753+
});
754+
728755
const txOptions: SignedContractCallOptions = {
729756
senderKey: sender.secretKey,
730757
contractAddress,
731758
contractName,
732759
functionName: 'stack-stx',
733760
functionArgs: [
734-
uintCV(ustxAmount.toString()),
735-
tupleCV({
736-
hashbytes: bufferCV(decodedBtcAddr.data),
737-
version: bufferCV(Buffer.from([decodedBtcAddr.version])),
738-
}),
739-
uintCV(burnBlockHeight),
740-
uintCV(cycles),
761+
uintCV(ustxAmount), // amount-ustx
762+
poxAddrTuple, // pox-addr
763+
uintCV(burnBlockHeight), // start-burn-ht
764+
uintCV(cycles), // lock-period
765+
someCV(bufferCV(hexToBytes(signerSig))), // signer-sig
766+
bufferCV(hexToBytes(signerPubKey)), // signer-key
767+
uintCV(ustxAmount), // max-amount
768+
uintCV(authId), // auth-id
741769
],
742770
network: stacksNetwork,
743771
anchorMode: AnchorMode.Any,

0 commit comments

Comments
 (0)