Skip to content

Commit 7c32e54

Browse files
feat(deploy): on-chain binary publish + reusable publish workflow (CD-loop publish side) (#160)
* chore(deploy): record v0 publish for trading blueprints + race-fix in publish-binary * blueprints.tsv: replace "no_v0_published" with "v0=<sha>; release=v0.1.3" for ids 13/14/15/16 — all four ai-trading blueprints now have a genesis binary version on-chain (Base Sepolia, setActiveBinaryVersion confirmed). * publish-binary.sh: poll getBinaryVersionCount up to 5x after publishBinaryVersion to ride out the read-back gap immediately following cast send (where the call occasionally still sees stale state and parses to 0 → version_id=-1 → setActive reverts). Fail loudly with a clear error if the publish landed but the read still says 0 after retries. * feat(script): RequestTradingService — register+request+approve service in one shot Reusable operator go-live script for any blueprint, compiled against tnt-core's own ITangle interfaces so selectors always match the deployment (the reliable path when cargo-tangle bindings are version-skewed — symptom UnknownSelector on requestService). Env-driven: TANGLE_CORE / BLUEPRINT_ID / OPERATOR_KEY / OPERATOR_ADDR / OPERATOR_PUBKEY / OPERATOR_RPC. Proven: ai-trading blueprint 13, service 0 ACTIVE on Base Sepolia. * feat(deploy): make publish-blueprint-binary reusable via workflow_call Add a workflow_call trigger alongside workflow_dispatch so a blueprint repo's release CI can invoke the on-chain publish directly. Inputs mirror the manual trigger; PUBLISH_RPC_URL and BLUEPRINT_OWNER_KEY are exposed as workflow_call secrets so the owner key stays in tnt-core. The shared job reads inputs.*/secrets.* identically for both triggers. * refactor(script): generalize RequestTradingService → RequestService (blueprint-agnostic) The script is generic protocol ops (registerOperator → requestService → approveService for any BLUEPRINT_ID env). Only its name/comments were trading-specific; rename + de-trading the docs so tnt-core stays blueprint-agnostic. Logic unchanged. --------- Co-authored-by: Drew Stone <drewstone329@gmail.com>
1 parent ea604fd commit 7c32e54

2 files changed

Lines changed: 111 additions & 0 deletions

File tree

.github/workflows/publish-blueprint-binary.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,42 @@ on:
3838
description: 'Set this version active after publishing'
3939
required: false
4040
default: 'true'
41+
workflow_call:
42+
# Reusable entry point: a blueprint repo's release CI calls this after it
43+
# publishes a release, so the privileged on-chain publish stays here (the
44+
# owner key never leaves tnt-core). Inputs mirror workflow_dispatch; the
45+
# shared job below reads inputs.*/secrets.* identically for both triggers.
46+
inputs:
47+
blueprint_repo:
48+
type: string
49+
required: true
50+
tag:
51+
type: string
52+
required: true
53+
blueprint_id:
54+
type: string
55+
required: true
56+
binary_name:
57+
type: string
58+
required: false
59+
default: ''
60+
network:
61+
type: string
62+
required: false
63+
default: 'base-sepolia'
64+
target:
65+
type: string
66+
required: false
67+
default: 'x86_64-unknown-linux-gnu'
68+
set_active:
69+
type: string
70+
required: false
71+
default: 'true'
72+
secrets:
73+
PUBLISH_RPC_URL:
74+
required: true
75+
BLUEPRINT_OWNER_KEY:
76+
required: true
4177

4278
permissions:
4379
contents: read

script/RequestService.s.sol

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import { Script, console2 } from "forge-std/Script.sol";
5+
import { ITangleFull } from "../src/interfaces/ITangle.sol";
6+
import { Types } from "../src/libraries/Types.sol";
7+
8+
/// @title RequestService
9+
/// @notice Onboard an operator to ANY blueprint and stand up a live service in
10+
/// one shot: registerOperator -> requestService -> approveService. The target
11+
/// blueprint is chosen by `BLUEPRINT_ID`, so this is generic protocol ops — not
12+
/// specific to any one blueprint. Reliable path when cargo-tangle's bindings are
13+
/// version-skewed vs the deployed Tangle (this script compiles against tnt-core's
14+
/// own interfaces, so the selectors always match the deployment).
15+
///
16+
/// Env:
17+
/// TANGLE_CORE deployed Tangle (e.g. 0x8299d60f… on Base Sepolia)
18+
/// BLUEPRINT_ID uint64 — the target blueprint to register on / request
19+
/// OPERATOR_KEY operator private key (must already be a staked/active
20+
/// MAD operator; this only registers it on the blueprint)
21+
/// OPERATOR_ADDR operator address
22+
/// OPERATOR_PUBKEY 65-byte uncompressed secp256k1 pubkey (0x04‖X‖Y)
23+
/// OPERATOR_RPC operator RPC advertised on-chain (string)
24+
/// SERVICE_TTL_SECS optional, default 604800 (7 days)
25+
contract RequestService is Script {
26+
function run() external {
27+
ITangleFull tangle = ITangleFull(payable(vm.envAddress("TANGLE_CORE")));
28+
uint64 blueprintId = uint64(vm.envUint("BLUEPRINT_ID"));
29+
uint256 operatorKey = vm.envUint("OPERATOR_KEY");
30+
address operator = vm.envAddress("OPERATOR_ADDR");
31+
bytes memory pubkey = vm.envBytes("OPERATOR_PUBKEY");
32+
string memory rpc = vm.envString("OPERATOR_RPC");
33+
uint64 ttl = uint64(vm.envOr("SERVICE_TTL_SECS", uint256(7 days)));
34+
35+
// 1. Register the (already-staked) operator on the blueprint.
36+
vm.startBroadcast(operatorKey);
37+
tangle.registerOperator(blueprintId, pubkey, rpc);
38+
vm.stopBroadcast();
39+
console2.log("registerOperator OK; blueprint", blueprintId);
40+
41+
// 2. Request a service with this single operator, empty config, native
42+
// (free) payment, Any confidentiality — mirrors the canonical flow.
43+
address[] memory operators = new address[](1);
44+
operators[0] = operator;
45+
address[] memory permittedCallers = new address[](0);
46+
47+
vm.startBroadcast(operatorKey);
48+
uint64 serviceId = tangle.requestService(
49+
blueprintId,
50+
operators,
51+
"", // empty config (blueprints that accept it; matches CreateServiceForTLV2Test)
52+
permittedCallers,
53+
ttl,
54+
address(0), // native payment token
55+
0, // payment amount
56+
Types.ConfidentialityPolicy.Any
57+
);
58+
console2.log("requestService OK; serviceId", serviceId);
59+
vm.stopBroadcast();
60+
61+
// 3. Operator approves -> service becomes active (single-operator).
62+
vm.startBroadcast(operatorKey);
63+
tangle.approveService(
64+
Types.ApprovalParams({
65+
requestId: serviceId,
66+
securityCommitments: new Types.AssetSecurityCommitment[](0),
67+
blsPubkey: [uint256(0), 0, 0, 0],
68+
blsPopSignature: [uint256(0), 0],
69+
teeCommitments: new Types.TeeAttestationCommitment[](0)
70+
})
71+
);
72+
vm.stopBroadcast();
73+
console2.log("approveService OK; service ACTIVE serviceId", serviceId);
74+
}
75+
}

0 commit comments

Comments
 (0)