Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a83f935
Merge pull request #975 from qubic/develop
fnordspace Aug 5, 2026
66ce0d7
Merge pull request #981 from qubic/develop
cyber-pc Aug 12, 2026
5dddbf2
Merge pull request #982 from qubic/develop
fnordspace Aug 12, 2026
d2dac10
Merge pull request #984 from qubic/develop (Release v1.301.6)
Franziska-Mueller Aug 14, 2026
45f5d6d
Merge pull request #986 from qubic/develop
fnordspace Aug 19, 2026
9c8dd79
Merge pull request #988 from qubic/develop
cyber-pc Aug 26, 2026
3158875
ant colony: materialise ANN backlog in a walker sidecar
hackerby888 Aug 27, 2026
1008d76
ant walker: format to 160-column style
hackerby888 Aug 27, 2026
ff71193
ant walker: trim comments
hackerby888 Aug 27, 2026
b0542a1
ant walker: skip jobs already memoised, let the tick reclaim a stuck …
hackerby888 Aug 28, 2026
3b424e8
HOTFIX: Set tick 77259626 empty
cyber-pc Aug 31, 2026
3acbc90
Merge pull request #991 from qubic/hotfix/set-tick-77259626-empty
cyber-pc Aug 31, 2026
447f983
trace the stamped contract index
hackerby888 Aug 31, 2026
cc72f8c
Add a cheat host row for development cheatcodes
hackerby888 Aug 31, 2026
0979560
Refuse a negative deal amount
hackerby888 Aug 31, 2026
2e41017
Update ant bpp9000 (#989)
cyber-pc Sep 1, 2026
1d9fb4d
New version of Nostromo (#842)
N-010 Sep 1, 2026
2dc7531
update params for epoch229 / v1.303.0
fnordspace Sep 1, 2026
409d1c4
Remove Qswap_old.h and toggle
fnordspace Sep 1, 2026
018b50c
Read a cheat payload at guest offset zero
hackerby888 Sep 1, 2026
8966559
Merge remote-tracking branch 'upstream/develop' into develop
hackerby888 Sep 1, 2026
6b572b2
Merge remote-tracking branch 'upstream' into develop
fnordspace Sep 1, 2026
f5f42f7
Merge cheat host row into develop
hackerby888 Sep 1, 2026
c2ee8b4
QIP return funds fix (#993)
baoLuck Sep 1, 2026
88d80f5
Pin commit with hash in contract-verify workflow
fnordspace Sep 1, 2026
a2a14a5
Add missing version comment
fnordspace Sep 1, 2026
fc4c630
Check call kind and locals size in the inter-contract macros
hackerby888 Sep 2, 2026
26c4591
Update ant colony document.
cyber-pc Sep 2, 2026
1bfabe4
Merge pull request #995 from qubic/feature/2026-09-02-update-ant-docu…
fnordspace Sep 2, 2026
ad5970b
disable ant sidecar process by default
hackerby888 Sep 2, 2026
e2d2ccb
Merge branch 'develop' of https://github.com/qubic/core into develop
hackerby888 Sep 2, 2026
1401b42
Merge pull request #78 from qubic/feat/ant-walker-sidecar
hackerby888 Sep 2, 2026
8d2fce9
Merge pull request #79 from qubic/main
hackerby888 Sep 2, 2026
46e0ef5
Revert "New version of Nostromo (#842)"
fnordspace Sep 2, 2026
406f780
Update version number
fnordspace Sep 2, 2026
4981fe7
Merge branch 'develop' of https://github.com/qubic/core into develop
hackerby888 Sep 2, 2026
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ option(BUILD_BENCHMARK "Build the EFI benchmark application" OFF)
option(BUILD_BINARY "Build the EFI application" ON)
option(USE_SANITIZER "Build test with sanitizer support (clang only)" ON)
option(LITE_WASM_SC "Enable testnet-lite-RAM Wasm smart contracts" OFF)
option(ANT_WALKER "Build the ant-colony walker sidecar and its node client" ON)
set(QUBIC_STANDALONE_RELEASE ON CACHE INTERNAL "Standalone builds are the only supported configuration" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build static dependencies" FORCE)
set(OPENSSL_USE_STATIC_LIBS TRUE CACHE BOOL "Use static OpenSSL" FORCE)
Expand Down
Binary file modified data/bpp9000.task
Binary file not shown.
98 changes: 68 additions & 30 deletions doc/ant_colony_mining.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ Standalone mining searches alone: every attempt starts from scratch. Ant-colony

- Every **mining identity** (a computor or candidate public key) owns its **own tree** - the colony is
a per-identity forest. A pool's workers extend the tree of the computor they mine for.
- Each tree starts from a **virtual root**: a starting solution derived from that identity's public key
and the epoch's spectrum digest. It is fixed for the epoch, identical every time you derive it, and
is never stored or submitted.
- Every tree starts from the same **virtual root**: one starting solution per epoch, derived from the
epoch's spectrum digest alone - identical for all identities, identical every time you derive it,
and never stored or submitted. All identities search from one shared origin; the trees branching
from it stay per-identity.
- To mine, you pick a **parent** (the root, or any node already in your tree), **inherit** it, vary it
under your nonce, and score the result.
- If the result **strictly beats the parent** and clears the epoch **threshold**, you **submit** it. On
Expand All @@ -58,7 +59,7 @@ miner starts from there instead of from scratch. The goal of the epoch is the si
found anywhere in the forest.

```
virtual root (per identity, not stored)
virtual root (shared per epoch, not stored)
|
+----+----+
| |
Expand All @@ -73,20 +74,20 @@ Concretely, error gates every attachment: it only falls down a branch (a child m
and a *start* - a depth-1 child of the root - must clear the threshold.

```
error = error count, lower is better threshold = 3838
error = error count, lower is better threshold = 4000

root ~4044 raw a fresh root sits above 3838; a start must mutate below it
root ~4200 raw the epoch root (same for everyone) sits above 4000; a start must mutate below it
|
+-- A 3790 <= threshold ACCEPT (depth-1 start)
+-- A 3900 <= threshold ACCEPT (depth-1 start)
| |
| +-- B 3540 < 3790, beats A ACCEPT
| +-- B 3540 < 3900, beats A ACCEPT
| | |
| | +-- D 3120 < 3540, beats B ACCEPT
| | +-- E 3560 not < 3540 REJECT (must beat parent)
| |
| +-- C 3700 < 3790, beats A ACCEPT
| +-- C 3700 < 3900, beats A ACCEPT
|
+-- X 3900 > threshold REJECT (over threshold)
+-- X 4100 > threshold REJECT (over threshold)

Error only falls as you go deeper. The epoch winner is the single lowest-error node
found in any identity's forest.
Expand All @@ -96,7 +97,7 @@ At epoch end the node ranks every identity by its **single best** score and **ha
(the number of computors).

**Anti-spam deposit.** Each solution a computor publishes on-chain carries a **refundable
1,000,000 QU deposit**, funded by the computor - not the worker. It is returned when the solution is
1000000 QU deposit**, funded by the computor - not the worker. It is returned when the solution is
accepted **and** its claimed score matches the node's recompute; otherwise it is kept. So a computor
only publishes solutions it has already validated, and an honest, correct one costs nothing.

Expand All @@ -105,7 +106,7 @@ only publishes solutions it has already validated, and an honest, correct one co
## Part 2 - Miner / pool integration guide

**In short.** A miner works one identity's tree. It reads the epoch context, takes a **parent** (the
identity's virtual root, or a node already in the tree), picks a canonical **nonce**, inherits the
epoch's shared virtual root, or a node already in the tree), picks a canonical **nonce**, inherits the
parent's network, and **mutates and scores** it - reproducing the node's score exactly. If the result
**beats its parent** and **clears the threshold**, it hands the solution to the **computor**, which
re-checks it and **publishes it on-chain**; every node then recomputes the score, folds it into
Expand All @@ -117,8 +118,8 @@ scorer** - the tree, gates, deposit, and queries are the wrapper around it.
1. **Epoch context** - `REQUEST_ANT_EPOCH_CONTEXT` (public). Read the threshold, freshness window,
epoch spectrum digest, and child cap for this epoch, and **verify your task file** against the
returned `topologyHash` / `dataHash` (section 2.7a) before doing any work.
2. **Get a starting point** - derive your identity's virtual root, or fetch an existing node you want
to extend (`REQUEST_ANT_PARENT_ANN`).
2. **Get a starting point** - derive the epoch's shared virtual root (from the spectrum digest), or
fetch an existing node you want to extend (`REQUEST_ANT_PARENT_ANN`).
3. **Pick a parent** - the root, or any node in your own tree.
4. **Search** - choose a nonce (section 2.2), inherit the parent LUT, run the mutation walk, score
(section 2.3).
Expand Down Expand Up @@ -153,17 +154,21 @@ knobs are not two solutions.
### 2.3 Scoring - bpp9000 (must be bit-exact)

Throughout, `publicKey` is the **mining identity you are extending** - the computor you mine for, which
becomes the transaction's `sourcePublicKey`. Derive the root and the mutation seed from **that** key,
not your worker key, or the node's recompute will not match yours.
becomes the transaction's `sourcePublicKey`. The **mutation seed** derives from **that** key, not your
worker key, or the node's recompute will not match yours. The **root** derives from no key at all -
see below.

**Root.** `deriveRootANN(publicKey, epochPool)`: `K12(publicKey)` seeds a per-neuron LUT from the
epoch's random pool (the pool comes from the epoch-start spectrum digest). No mutation walk. Never
stored. The same every time for the epoch.
**Root.** `deriveRootANN(spectrumDigest, epochPool)`: `K12(spectrumDigest)` - the epoch-start
spectrum digest from the epoch context - seeds a per-neuron LUT from the epoch's random pool (the
pool itself also comes from that digest). No mutation walk. Never stored. **One root per epoch,
identical for every identity**; per-identity variation enters only through the mutation seeds.

**Child.** `computeScoreFromParent(parentLUT, publicKey, nonce, anchorTickDigest)`:

1. Inherit `parentLUT`.
2. `mutationSeed = K12(publicKey || nonce[3..31] || anchorTickDigest)` (`nonce[0..2]` zeroed).
2. `mutationSeed = K12(publicKey || nonce || anchorTickDigest)` with `nonce[0..2]` zeroed in place
(the full 32-byte nonce is hashed, its first 3 bytes set to 0, not dropped) - still keyed by the
mining identity, so different identities walk differently from the shared root.
3. Walk `numberOfMutations = 100` steps. Each step rewrites `L` LUT entries. For the first `K` steps
accept a worse-or-equal result (**explore**); after that accept only better-or-equal (**exploit**);
one-step rollback on reject. Keep and return the **best** score seen. The best is seeded with the
Expand All @@ -181,8 +186,9 @@ Score is an error count in `[0, 8088]`; lower is better.

### 2.4 Accept rules

A submission is accepted (`Valid` or `ValidNotStored`) only if **all** of these hold. The node checks
in this order; the first failure is the reject reason:
A submission is accepted (`Valid` or `ValidNotStored`) only if **all** of these hold. On failure the
reject reason names the rule the node found violated (the exact evaluation order is an internal
detail and can change):

| Check | Reject reason if it fails |
|-------|---------------------------|
Expand All @@ -201,14 +207,15 @@ recorded but the **deposit is kept** and the miner is **not ranked**.

**Starting a tree.** The root's record score is the worst possible value, so a first (depth-1) child
passes the "beats parent" check trivially - the **threshold is the only score gate** for starting a
tree. A random root scores far above the threshold, so a start still requires real mutation.
tree. The shared epoch root scores far above the threshold, so a start still requires real mutation -
and every identity starts from the same score, so ranking differences reflect search effort only.

**`ValidNotStored`.** Accepted, refunded, and ranked exactly like `Valid`, but the per-epoch store was
full so the node was not persisted for others to extend. Ranking and refund are unaffected.

### 2.5 The deposit

Every on-chain `AntColonyMiningSolutionTransaction` carries a **1,000,000 QU** deposit
Every on-chain `AntColonyMiningSolutionTransaction` carries a **1000000 QU** deposit
(`SOLUTION_SECURITY_DEPOSIT`), funded by the **computor** that publishes it - not the miner (see 2.6).
It is refunded **iff** the solution is accepted (`Valid` / `ValidNotStored`) **and** the claimed score
equals the node's recompute; otherwise it is kept. So a computor risks its own deposit and therefore
Expand All @@ -232,8 +239,8 @@ BroadcastMessage { // 96-byte envelope
}
// then the payload:
AntSolutionBroadcastPayload { // 48 bytes
unsigned int parentTick; // ABSOLUTE tick of the parent node (0 with the index below = root)
unsigned int parentSolutionIndexInTick;
unsigned int parentTick; // ABSOLUTE tick of the parent node; the virtual root is (0, 0xFFFFFFFF)
unsigned int parentSolutionIndexInTick; // parent's index within its tick; 0xFFFFFFFF with parentTick 0 = root
unsigned int anchorTick; // ABSOLUTE tick number
unsigned int claimedScore;
m256i nonce; // the 32-byte nonce from 2.2
Expand All @@ -254,7 +261,7 @@ AntColonyMiningSolutionTransaction : Transaction { // 80-byte header + 48-byte
// --- Transaction header ---
m256i sourcePublicKey; // the COMPUTOR (tree owner); signs the tx and funds the deposit
m256i destinationPublicKey; // zero (NULL_ID)
long long amount; // SOLUTION_SECURITY_DEPOSIT = 1,000,000 QU
long long amount; // SOLUTION_SECURITY_DEPOSIT = 1000000 QU
unsigned int tick; // publish tick
unsigned short inputType; // ANT_COLONY_MINING_SOLUTION_INPUT_TYPE = 12
unsigned short inputSize; // 48
Expand All @@ -281,6 +288,37 @@ becomes `sourcePublicKey` of the transaction. Workers hold no tree and post no o
hand solutions to the pool's computor, which pre-validates them and risks its own deposit only on
solutions it expects to be accepted and refunded. Fund the computor identity, not the workers.

### 2.6a Submitting a root (depth-1) solution

A root solution starts a tree: its parent is the epoch's shared virtual root, which is derived rather
than stored, so you never fetch it. This is the first solution every identity submits, and it differs
from extending an existing node only in how the parent is named and scored.

1. **Parent is the root.** Set `parentRef = (parentTick = 0, parentSolutionIndexInTick = 0xFFFFFFFF)`.
Both fields are load-bearing: `(0, 0xFFFFFFFF)` is the only value the node reads as root; a `0`
tick with any other index is treated as a normal parent, found nowhere, and rejected with
`RejectParentNotRegistered`.
2. **Derive the parent LUT yourself.** `deriveRootANN(spectrumDigest, epochPool)` from the epoch
context (section 2.3) - do **not** call `REQUEST_ANT_PARENT_ANN` for the root; it answers
`status = IS_ROOT` with no ANN payload precisely so you derive it locally. The root is identical
for every identity.
3. **Search.** Pick a canonical nonce (section 2.2), inherit the derived root LUT, run the walk, and
take the best score - exactly as for any parent.
4. **The only score gate is the threshold.** The root's record score is the worst possible value, so
the "strictly beats the parent" rule passes trivially; a root child is accepted on score iff its
score is `<=` the epoch threshold. The shared root scores far above the threshold, so a valid
start still requires real mutation - and since every identity starts from the same root score,
ranking reflects search effort alone.
5. **Anchor and submit.** Choose a non-empty anchor tick within `freshnessWindow` of the publish tick
(section 2.3), fill the payload with the root `parentRef` above, and hand it to your computor
(section 2.6, stage 1). The computor publishes it as the usual `AntColonyMiningSolutionTransaction`.

On the node, a root submission is recognized by `parentRef.isRoot()`: the parent lookup returns a
null record (root is not a stored solution), the node derives the shared root itself to recompute
your `claimedScore`, and root children are de-duplicated per miner because the root is shared by all
identities. Once accepted, the node becomes a normal parent - extend it by copying its `selfTick` /
`selfSolutionIndexInTick` from the identity-tree query (section 2.7b) into a child's `parentRef`.

### 2.7 Read queries

Three request/response pairs. **Identity tree** and **parent ANN** are **operator-signed**; **epoch
Expand All @@ -301,7 +339,7 @@ signature = sign(operatorSubseed, operatorPublicKey, digest) // 64 bytes, appe
Request: empty. Response `RespondAntEpochContext` (120 bytes, packed):

```
m256i spectrumDigest; // epoch-start spectrum digest (seeds every root)
m256i spectrumDigest; // epoch-start spectrum digest; IS the root seed (and seeds the pool)
m256i topologyHash; // canonical task topology-block hash (BPP9000_TOPOLOGY_HASH)
m256i dataHash; // canonical task data-block hash (BPP9000_DATA_HASH)
unsigned int threshold; // per-epoch accept bound
Expand Down Expand Up @@ -364,11 +402,11 @@ byte, the exact form the scorer consumes - no unpacking needed):
unsigned int parentRefTick;
unsigned int parentRefSolutionIndexInTick;
unsigned int annSizeBytes; // ANN LUT size when status is OK, else 0
unsigned char status; // 0 = OK, 1 = NOT_FOUND, 2 = IS_ROOT (derive your own root instead)
unsigned char status; // 0 = OK, 1 = NOT_FOUND, 2 = IS_ROOT (derive the epoch root instead)
unsigned char padding[3];
```

**Canonical ANN layout.** The same byte form is used everywhere ANN bytes leave the node: this response, the snapshot pool, and the epoch export. Under the current bpp9000 parameters it is 1'728 bytes = 64 rows of 27:
**Canonical ANN layout.** The same byte form is used everywhere ANN bytes leave the node: this response, the snapshot pool, and the epoch export. Under the current bpp9000 parameters it is 1728 bytes = 64 rows of 27:

```
row k, k = 0..45 LUT of neuron updatedNeuronIndices[k]: the k-th NON-INPUT neuron in
Expand Down
4 changes: 4 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,10 @@ if(IS_CLANG)
endif()
target_link_libraries(qubic-cli PRIVATE m)

if(ANT_WALKER)
target_compile_definitions(Qubic PRIVATE ANT_WALKER)
endif()

# Configure linker settings based on compiler
if(IS_MSVC)
if(NOT LITE_WASM_SC)
Expand Down
1 change: 0 additions & 1 deletion src/Qubic.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
<ClInclude Include="contracts\Qdraw.h" />
<ClInclude Include="contracts\qRWA.h" />
<ClInclude Include="contracts\Qswap.h" />
<ClInclude Include="contracts\Qswap_old.h" />
<ClInclude Include="contracts\RandomLottery.h" />
<ClInclude Include="contracts\QDuel.h" />
<ClInclude Include="contracts\SupplyWatcher.h" />
Expand Down
3 changes: 0 additions & 3 deletions src/Qubic.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,6 @@
<ClInclude Include="contracts\Qswap.h">
<Filter>contracts</Filter>
</ClInclude>
<ClInclude Include="contracts\Qswap_old.h">
<Filter>contracts</Filter>
</ClInclude>
<ClInclude Include="platform\uint128.h">
<Filter>platform</Filter>
</ClInclude>
Expand Down
4 changes: 0 additions & 4 deletions src/contract_core/contract_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,7 @@
#define CONTRACT_INDEX QSWAP_CONTRACT_INDEX
#define CONTRACT_STATE_TYPE QSWAP
#define CONTRACT_STATE2_TYPE QSWAP2
#ifdef OLD_QSWAP
#include "contracts/Qswap_old.h"
#else
#include "contracts/Qswap.h"
#endif

#undef CONTRACT_INDEX
#undef CONTRACT_STATE_TYPE
Expand Down
12 changes: 10 additions & 2 deletions src/contracts/QIP.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum QIPLogInfo {
QIP_invalidIssuer = 15,
QIP_qxAskOrderFound = 16,
QIP_icoAlreadyExists = 17,
QIP_returnFundsError = 18,
};

struct QIPLogger
Expand Down Expand Up @@ -715,9 +716,16 @@ struct QIP : public ContractBase
}
}

if (locals.log._type == QIP_success && locals.buyerInfo.isReturned)
if (locals.log._type == QIP_success)
{
locals.log._type = QIPLogInfo::QIP_fundsAlreadyReturned;
if (qpi.epoch() <= locals.ico.startEpoch + 2)
{
locals.log._type = QIPLogInfo::QIP_returnFundsError;
}
else if (locals.buyerInfo.isReturned)
{
locals.log._type = QIPLogInfo::QIP_fundsAlreadyReturned;
}
}

if (locals.log._type != QIP_success)
Expand Down
Loading
Loading