Files
LithosAnanake/v4/tests/test_host_unit.c
T
rajamesandClaude Opus 5.5 d5b7235464 feat(v4.0.0): every node asks the kernel for its blocks; a born node is not POSTed
A block is a kernel request, as ENGINE.md 3.3 has it: the node puts the
block's number and the address of 256 cells on its stack and writes the
request to port 0, and the kernel leaves the status there.  The requests
are -1, read, and -2, write, the same for every node.  v4/system/blocks.c
serves them from the kernel's block subsystem, which is v3's.  The four
storage registers are gone from the engine.

The device that spoke block messages (4a505a15) is withdrawn with its
test and its message types: Captain Bob ruled on 2026-10-07 that it, a
node's own drive, and nodes with no storage had left the OS as designed
(docs/v4.0.0/MESH.md 8.5).

Hera no longer sends POST to the nodes she births: POST is the kernel's,
once.  Every node has its kernel on port 0; it serves a node's blocks and,
for Hera alone, her requests for nodes and capsules.

Bare metal: the node boots and is POSTed against POST's own block RAM,
and the kernel's chain -- fast RAM, the ramdrive, the virtio disk -- is
set up after POST and before the prompt, as on the v3 path.  The disk is
read and not written: nothing in v4 yet gives the owner's word that it
may be formatted.  A hosted program has the chain's fast RAM, as hosted
v3 has with no disk.  Error 17 is Storage refused.

make -C v4 test and sanitize pass at both widths; hosted-check passes on
three ISAs; amd64, aarch64 and riscv64 boot, POST 538 of 538, with the
typed session: logs/20261007-081603, -081839, -082226.  The hashes are
the same on all six.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 08:24:47 -04:00

454 lines
22 KiB
C

/* test_host_unit.c -- five nodes come up from nothing.
* docs/v4.0.0/MESH.md section 9, step 5; acceptance 1 and 2.
*
* [11]---[12]
* | \ / | the four outer nodes are each on a
* console --1 [10] | port of Hera, 10, and joined in a
* kernel --0 | / \ | square; the corners across from each
* [13]---[14] other are not wired
*
* Hera is born empty and is given the nucleus through the port her requests
* go to, as the boot gives it (v4/system/boot.c). She is told who she is,
* given the words that ask for nodes (v4/include/v4/manage.h), FORTH-79,
* POST, and her own capsule, capsules/v4/hera.4th. Then she is told
* `10 UNIT`, and everything after that is hers: she asks for four nodes,
* wires them, sends each the nucleus cell by cell through the port that
* joins them and then FORTH-79 and POST a line at a time, and joins them.
*
* What stands in here for the host: the capsules are read from the files
* the build bakes in, and the nucleus is assembled as mkimage assembles it.
*
* STORAGE (MESH.md section 8, acceptance 3). Every node asks its kernel
* for its blocks, on its port 0, and the kernel's block subsystem
* (v3/src/block_subsystem.c) has them: one chain, fast RAM and a device of
* 64 blocks. For Hera the kernel on that port also answers her requests
* for nodes and capsules; for every other node it serves blocks and
* nothing else.
*/
#include "v4/capsule.h"
#include "v4/fabric.h"
#include "v4/image.h"
#include "v4/manage.h"
#include "v4/blocks.h"
#include "v4/message.h"
#include "v4/text.h"
#include "block_subsystem.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "host_map.h"
static int failures = 0, checks = 0;
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
#define PLACES 10u
static v4_text tx;
static v4_node nucleus; /* the nucleus, assembled: what the capsule of F18 code is written from */
static v4_cell w_idle, w_fault;
static v4_fabric_node pool[PLACES];
static int pool_used[PLACES];
/* the kernel's blocks: fast RAM and a device of 64 blocks */
#define DISK_BLOCKS 64u
static unsigned char common_ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE], common_dev[DISK_BLOCKS * V4_BLOCK_BYTES];
extern void sf_time_init(void);
extern void log_set_level(int level);
static int storage_made(void)
{
sf_time_init();
log_set_level(-1);
return blk_subsys_init(common_ram, sizeof common_ram) == BLK_OK && blk_subsys_add_raw_device(common_dev, DISK_BLOCKS) == BLK_OK;
}
static v4_place places[PLACES];
static v4_fabric f;
static v4_image im; /* only what the dictionary hash needs of it */
/* ---- the capsules ---- */
static v4_cell f18[2u * V4_NODE_WORDS + 64u]; /* the nucleus as a capsule of F18 code */
static unsigned f18_count;
static void f18_put(void *self, v4_cell w) { (void)self; if (f18_count < sizeof f18 / sizeof f18[0]) f18[f18_count++] = w; }
typedef struct { char *text; long len, at; int in_block; unsigned cells_at; int is_f18; } reader;
static int read_file(reader *r, const char *name)
{
char path[600];
FILE *in;
snprintf(path, sizeof path, "%s/../../capsules/v4/%s", V4_CAPSULE_DIR, name);
in = fopen(path, "rb");
if (!in) return 0;
free(r->text);
fseek(in, 0, SEEK_END); r->len = ftell(in); fseek(in, 0, SEEK_SET);
r->text = (char *)malloc((size_t)r->len + 1);
if (!r->text || fread(r->text, 1, (size_t)r->len, in) != (size_t)r->len) { fclose(in); return 0; }
fclose(in);
r->at = 0; r->in_block = 0; r->is_f18 = 0;
return 1;
}
/* the next line of a block that has anything on it: its length, or -1 */
static int next_line(reader *r, char *text)
{
while (r->at < r->len) {
const char *p = r->text + r->at;
long n = 0;
while (r->at + n < r->len && p[n] != '\n') n++;
r->at += n + 1;
if (n >= 7 && memcmp(p, "Block ", 6) == 0 && p[6] >= '0' && p[6] <= '9') { r->in_block = 1; continue; }
if (n && p[n - 1] == '\r') n--;
if (!r->in_block || n == 0) continue;
if (n > V4_MANAGE_LINE) return -1;
memcpy(text, p, (size_t)n);
return (int)n;
}
return -1;
}
/* ---- what Hera's requests are answered with ---- */
static reader hera_reads; /* the capsule Hera has open */
static unsigned born_count, parity_count;
static v4_cell parity_number[8];
static uint64_t parity_hash[8];
/* THE KERNEL, for a node that is not Hera: on its port 0 it serves the
* node's blocks, and no other request (MESH.md 8.3). */
static unsigned refused;
static int node_kernel_take(void *self, v4_cell value)
{
if (!v4_blocks_serve((v4_node *)self, value)) refused++;
return 1;
}
static v4_device node_kernel[PLACES];
static int host_born(void *self)
{
unsigned k;
int place;
(void)self;
for (k = 0; k < PLACES && pool_used[k]; k++) { }
if (k == PLACES) return -1;
place = v4_fabric_add(&f, &pool[k], PORT);
if (place < 0) return -1;
pool_used[k] = 1;
born_count++;
/* what a node's hardware gives it; there is nothing in its memory */
v4_node_stack_regs_attach(&pool[k].n, DSTACK_REG, RSTACK_REG);
v4_node_error_attach(&pool[k].n, NODE_ERROR);
v4_node_fault_attach(&pool[k].n, w_fault);
if (k != 0) { /* Hera's kernel is wired where she is made */
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k].n;
if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1;
}
return place;
}
static void host_gone(void *self, unsigned place, v4_fabric_node *was)
{
(void)self; (void)place;
pool_used[was - pool] = 0;
}
static int host_open(void *self, const char *name, unsigned len)
{
(void)self;
if (len == 17 && memcmp(name, "v4:nucleus-64.f18", 17) == 0) { hera_reads.is_f18 = 1; hera_reads.cells_at = 0; return 1; }
if (len == 14 && memcmp(name, "v4:forth79.4th", 14) == 0) return read_file(&hera_reads, "forth79.4th");
if (len == 13 && memcmp(name, "v4:post79.4th", 13) == 0) return read_file(&hera_reads, "post79.4th");
return 0;
}
static int host_cell(void *self, v4_cell *w)
{
(void)self;
if (!hera_reads.is_f18 || hera_reads.cells_at >= f18_count) return 0;
*w = f18[hera_reads.cells_at++];
return 1;
}
static int host_line(void *self, char *text)
{
(void)self;
return hera_reads.is_f18 ? -1 : next_line(&hera_reads, text);
}
static void host_parity(void *self, unsigned place, v4_cell number, const v4_node *n)
{
(void)self; (void)place;
if (parity_count < 8) { parity_number[parity_count] = number; parity_hash[parity_count] = v4_image_dict_hash(n, &im); }
parity_count++;
}
static const v4_manage manage = { &f, host_born, host_gone, host_open, host_cell, host_line, host_parity, 0 };
/* ---- the kernel: a device on Hera's port 0 ---- */
#define FIRST_REQUEST 1 /* the number of NODE-ME on her */
static unsigned hera_place, given_cells;
static int kernel_give(void *self, v4_cell *value) /* the nucleus, to a node that has nothing in it */
{
(void)self;
if (given_cells >= f18_count) return 0;
*value = f18[given_cells++];
return 1;
}
static int kernel_take(void *self, v4_cell value) /* a request */
{
(void)self;
if (v4_blocks_serve(&v4_fabric_node_at(&f, hera_place)->n, value)) return 1; /* a block, as for any node */
if (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++;
return 1;
}
static const v4_device kernel = { kernel_take, kernel_give, 0 };
/* ---- the console: a device on Hera's port 1 ---- */
#define CONSOLE_ID 1
static v4_message going, coming;
static unsigned going_at;
static char printed[8192]; /* what has come back since the console last sent */
static unsigned printed_len;
static v4_cell printed_from;
static int ended;
static v4_cell ended_from, ended_how;
static unsigned post_passed[32]; /* by node number: POST tallies with no failures seen from it */
static unsigned post_other; /* tallies with failures */
static int console_give(void *self, v4_cell *value)
{
(void)self;
if (going_at >= going.count) return 0;
*value = going.word[going_at++];
return 1;
}
static int console_take(void *self, v4_cell value)
{
(void)self;
if (v4_message_word(&coming, value)) {
if (v4_message_type(&coming) == V4_MSG_OUTPUT) {
unsigned i, chars = v4_message_length(&coming), start = printed_len;
for (i = 0; i < chars && printed_len + 1 < sizeof printed; i++) printed[printed_len++] = v4_message_char(&coming, i);
printed[printed_len] = 0;
printed_from = v4_message_from(&coming);
if (strstr(printed + start, "PARITY:V4_POST")) {
if (strstr(printed + start, " fail=0\n") && printed_from >= 0 && printed_from < 32) post_passed[printed_from]++;
else post_other++;
}
} else if (v4_message_type(&coming) == V4_MSG_DONE) {
ended++;
ended_from = v4_message_from(&coming);
ended_how = coming.word[V4_MSG_HEADER];
}
coming.count = 0;
}
return 1;
}
static const v4_device console = { console_take, console_give, 0 };
/* Is there a node that will execute at the next step? A step in which a
* node only faulted counts nothing done (exec.h), and it goes on from its
* fault handler at the next. */
static int any_running(void)
{
unsigned k;
for (k = 0; k < PLACES; k++) {
const v4_fabric_node *x = v4_fabric_node_at(&f, k);
if (x && !places[k].asleep && !x->n.stopped && !x->n.asking && !(x->n.reading && !x->n.given)) return 1;
}
return 0;
}
/* What every node is doing, for a test that does not end. */
static unsigned long step_limit = 4000000000ul;
static void stuck(void)
{
unsigned k;
for (k = 0; k < PLACES; k++) {
const v4_fabric_node *x = v4_fabric_node_at(&f, k);
if (x) printf(" stuck: node %ld p=%ld asking=%d port=%u reading=%d rport=%u given=%d stopped=%d mq=%ld lost=%ld\n", (long)x->n.mem[ME], (long)x->n.p,
x->n.asking, x->n.ask_port, x->n.reading, x->n.read_port, x->n.given, x->n.stopped, (long)x->n.mem[MQ_COUNT], (long)x->n.mem[LOST]);
}
}
/* Send text to a node and let everything that follows from it happen. */
static const char *tell(v4_cell node, const char *text)
{
unsigned long steps = 0;
printed_len = 0; printed[0] = 0; printed_from = -1;
ended = 0; ended_from = -1; ended_how = -1;
if (!v4_message_text(&going, node, CONSOLE_ID, V4_MSG_TEXT, text, (unsigned)strlen(text))) return "(too long)";
going_at = 0;
while (steps < step_limit && (v4_fabric_step(&f) != 0 || any_running())) steps++;
if (steps >= step_limit) { stuck(); return "(still running)"; }
return printed;
}
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
/* Every line of a capsule of FORTH blocks, told to a node from the console:
* how many lines did not complete. */
static unsigned load(v4_cell node, const char *file)
{
static reader r;
char line[V4_MANAGE_LINE + 1];
unsigned bad = 0;
int n;
if (!read_file(&r, file)) return 1;
while ((n = next_line(&r, line)) >= 0) {
line[n] = 0;
(void)tell(node, line);
if (ended != 1 || ended_how != V4_TEXT_COMPLETED) { if (!bad) { const v4_node *q = &pool[0].n; printf(" %s: \"%s\" -> \"%s\" ended=%d how=%ld; stopped=%d asking=%d port=%u reading=%d rport=%u p=%ld fault=%u@%ld\n", file, line, printed, ended, (long)ended_how, q->stopped, q->asking, q->ask_port, q->reading, q->read_port, (long)q->p, q->fault_kind, (long)q->fault_addr); } bad++; }
}
return bad;
}
static v4_node *node_numbered(v4_cell number)
{
unsigned k;
for (k = 0; k < PLACES; k++) if (v4_fabric_node_at(&f, k) && v4_fabric_node_at(&f, k)->n.mem[ME] == number) return &v4_fabric_node_at(&f, k)->n;
return 0;
}
static v4_uheat_t clock_of(v4_cell number)
{
unsigned k;
for (k = 0; k < PLACES; k++) if (v4_fabric_node_at(&f, k) && v4_fabric_node_at(&f, k)->n.mem[ME] == number) return v4_fabric_node_at(&f, k)->es.anticlock;
return 0;
}
static int waiting(v4_cell number)
{
const v4_node *n = node_numbered(number);
return n && n->reading && !n->given && n->read_port == V4_PORT_ANY;
}
int main(void)
{
unsigned i;
int place;
printf("v4 unit tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
if (V4_CELL_BITS != 64) {
/* The capsules the unit is built from are the 64-bit products': the
* expected results in post79.4th are v3's, on 64-bit cells, and the
* nucleus capsule is nucleus-64. POST has never been run on 32-bit
* cells (docs/v4.0.0/MESH.md, step 5). */
printf(" not run at this width: the capsules are the 64-bit products'\n");
return 0;
}
CHECK(storage_made(), "the kernel's blocks are there before any node is");
{
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4",
"numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
CHECK(host_load(&tx, &nucleus, files, 14), "the nucleus assembles");
}
CHECK(v4_text_finish(&tx), "everything is defined: %s", v4_text_error(&tx));
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
w_idle = v4_text_word(&tx, "(IDLE)");
w_fault = v4_text_word(&tx, "(FAULTS)");
nucleus.mem[DP] = DICT_W * 4; /* the variables, as at switch-on (tools/mkimage.c) */
nucleus.mem[LATEST] = v4_text_latest(&tx);
nucleus.mem[CFP] = CFS_W;
nucleus.mem[BASE] = 10;
nucleus.mem[FENCE] = DICT_W;
nucleus.mem[BOOT_CELLS] = nucleus.mem[DP];
nucleus.mem[BOOT_CELLS + 1] = nucleus.mem[LATEST];
nucleus.mem[LOG_LEVEL] = 2;
nucleus.mem[CONTEXT] = LATEST;
nucleus.mem[CURRENT] = LATEST;
nucleus.mem[SRC] = TIB;
nucleus.mem[LINE_STATUS] = 1;
HOST_MESSAGES_EMPTY(&nucleus);
(void)v4_capsule_write(&nucleus, w_idle, f18_put, 0);
CHECK(f18_count > 1000 && f18_count < sizeof f18 / sizeof f18[0], "the nucleus is a capsule of F18 code: %u cells", f18_count);
memset(&im, 0, sizeof im);
im.dp = DP; im.latest = LATEST;
/* ---- Hera is born empty, and takes in the nucleus ---- */
v4_fabric_init(&f, places, PLACES);
place = host_born(0);
CHECK(place == 0, "a node is born");
hera_place = (unsigned)place;
for (i = 0; i < 4096 && pool[0].n.mem[i] == 0; i++) { }
CHECK(i == 4096 && pool[0].n.p == PORT + (v4_cell)V4_PORT_ANY, "with nothing in it, listening at its ports");
CHECK(v4_fabric_wire_device(&f, hera_place, 0, &kernel) && v4_fabric_wire_device(&f, hera_place, 1, &console), "the kernel is on its port 0 and the console on its port 1");
going.count = 0; going_at = 0;
for (i = 0; i < 400000 && v4_fabric_step(&f) != 0; i++) { }
CHECK(given_cells == f18_count && pool[0].n.reading && pool[0].n.read_port == V4_PORT_ANY && !pool[0].n.stopped,
"it takes in the nucleus through its port and waits for a message: %u of %u cells", given_cells, f18_count);
CHECK(told(0, "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE") && ended_from == 10, "it is told it is 10, and where the console is");
{
char text[600];
size_t at = 0;
for (i = 0; i < V4_MANAGE_WORDS; i++) at += (size_t)snprintf(text + at, sizeof text - at, "%u KERNEL-WORD %s ", FIRST_REQUEST + i, v4_manage_words[i]);
CHECK(told(10, text), "and given the words that ask for nodes and capsules: \"%s\"", printed);
}
CHECK(strcmp(tell(10, "NODE-ME ."), "0 ") == 0 && refused == 0, "it asks where it is, and is answered");
CHECK(load(10, "forth79.4th") == 0, "it takes in FORTH-79");
CHECK(load(10, "post79.4th") == 0 && post_passed[10] == 1 && post_other == 0, "and passes POST");
CHECK(load(10, "hera.4th") == 0, "and its own capsule, which makes it Hera");
CHECK(told(10, "(SEAL)"), "that is the system it comes back to");
/* ---- she births the unit ---- */
born_count = 0;
{
const char *r = tell(10, "10 UNIT");
CHECK(ended == 1 && ended_how == V4_TEXT_COMPLETED && ended_from == 10, "Hera births four nodes and joins them: \"%s\"", r);
}
CHECK(born_count == 4 && refused == 0, "four nodes were asked for: %u", born_count);
CHECK(post_passed[10] == 1 && post_passed[11] == 0 && post_passed[12] == 0 && post_passed[13] == 0 && post_passed[14] == 0 && post_other == 0,
"a node she births is not POSTed: POST is the kernel's, once, on Hera: %u; %u %u %u %u, %u failed",
post_passed[10], post_passed[11], post_passed[12], post_passed[13], post_passed[14], post_other);
CHECK(parity_count == 4 && parity_number[0] == 11 && parity_number[1] == 12 && parity_number[2] == 13 && parity_number[3] == 14, "the parity of each was recorded: %u", parity_count);
CHECK(parity_hash[0] == parity_hash[1] && parity_hash[1] == parity_hash[2] && parity_hash[2] == parity_hash[3],
"and their dictionary hashes are the same: %016llx %016llx %016llx %016llx",
(unsigned long long)parity_hash[0], (unsigned long long)parity_hash[1], (unsigned long long)parity_hash[2], (unsigned long long)parity_hash[3]);
printf(" the four outer nodes: dict_hash=0x%016llx\n", (unsigned long long)parity_hash[0]);
CHECK(parity_hash[0] != v4_image_dict_hash(node_numbered(10), &im), "Hera's is not: she has her own capsule");
CHECK(waiting(10) && waiting(11) && waiting(12) && waiting(13) && waiting(14), "all five are waiting at their ports");
{
v4_uheat_t c[5];
for (i = 0; i < 5; i++) c[i] = clock_of(10 + (v4_cell)i);
for (i = 0; i < 200; i++) (void)v4_fabric_step(&f);
for (i = 0; i < 5 && clock_of(10 + (v4_cell)i) == c[i]; i++) { }
CHECK(i == 5, "and execute nothing while they wait");
}
/* ---- they talk ---- */
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && printed_from == 10, "a line typed at the console is done by Hera");
for (i = 11; i <= 14; i++) {
char text[32], want[16];
snprintf(text, sizeof text, "%u 100 * .", i);
snprintf(want, sizeof want, "%u ", i * 100);
CHECK(strcmp(tell((v4_cell)i, text), want) == 0 && printed_from == (v4_cell)i && ended_from == (v4_cell)i, "text for node %u is done there and what it prints comes back: \"%s\"", i, printed);
}
CHECK(strcmp(tell(13, "3 4 U* . ."), "0 12 ") == 0, "an outer node has FORTH-79: U* is a word of the capsule");
CHECK(strcmp(tell(13, ": SQ DUP * ; 7 SQ ."), "49 ") == 0 && strstr(tell(12, "7 SQ ."), "UNKNOWN WORD") != NULL, "each has its own dictionary");
CHECK(strstr(tell(13, "COLD"), "Cold Start") != NULL && strcmp(tell(13, "3 4 U* . ."), "0 12 ") == 0 && strstr(tell(13, "7 SQ ."), "UNKNOWN WORD") != NULL,
"COLD on it comes back to the nucleus and FORTH-79");
{
v4_uheat_t hera = clock_of(10);
CHECK(strcmp(tell(11, ": T S\" 65 EMIT\" 12 SEND ; T"), "A") == 0 && printed_from == 12, "an outer node sends text to the one beside it: \"%s\"", printed);
hera = clock_of(10);
CHECK(strcmp(tell(11, ": X S\" 66 EMIT\" 14 SEND ; X"), "B") == 0 && printed_from == 14, "and to the one across from it, which it is not wired to: \"%s\"", printed);
CHECK(clock_of(10) > hera, "that went by a node in between");
}
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(11, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(14, "(LOST) @ ."), "0 ") == 0, "nothing was let go");
/* ---- they share the store (acceptance 3) ---- */
step_limit = 30000000ul;
CHECK(told(12, "2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS") && common_dev[52 * V4_BLOCK_BYTES] == 'A' && common_dev[52 * V4_BLOCK_BYTES + 1] == ' ',
"node 12 writes a block, by asking the kernel: \"%s\"", printed);
for (i = 10; i <= 14; i++) {
if (i == 12) continue;
CHECK(strcmp(tell((v4_cell)i, "2100 BLOCK C@ ."), "65 ") == 0, "node %u reads what node 12 wrote: \"%s\"", i, printed);
}
CHECK(strstr(tell(14, "9000 BLOCK"), "Block out of range") != NULL && strcmp(tell(14, "14 100 * ."), "1400 ") == 0, "a block there is not says so, and the node goes on: \"%s\"", printed);
{
char text[64];
unsigned before = refused, born = born_count;
snprintf(text, sizeof text, "%u KERNEL-WORD ASK-BORN ASK-BORN", FIRST_REQUEST + 1);
(void)tell(12, text);
CHECK(refused == before + 1 && born_count == born, "an outer node's request for a node is not honoured: only its blocks are");
}
/* two nodes use the store at once */
CHECK(told(10, ": W 10 0 DO 97 I 2060 + BLOCK C! UPDATE SAVE-BUFFERS LOOP ;") && told(12, ": W 10 0 DO 98 I 2070 + BLOCK C! UPDATE SAVE-BUFFERS LOOP ;"), "each of two nodes has ten blocks to write");
CHECK(told(10, ": GO S\" W\" 12 SEND W ; GO"), "Hera sets node 12 writing and writes her own meanwhile: \"%s\"", printed);
for (i = 0; i < 10 && common_dev[(12 + i) * V4_BLOCK_BYTES] == 'a' && common_dev[(22 + i) * V4_BLOCK_BYTES] == 'b'; i++) { }
CHECK(i == 10, "all twenty blocks reached the store: the first that did not is %u", i);
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(12, "(LOST) @ ."), "0 ") == 0, "and no message was let go");
CHECK(waiting(10) && waiting(11) && waiting(12) && waiting(13) && waiting(14), "all five are waiting at their ports again");
printf(" %d checks, %d failures\n", checks, failures);
return failures != 0;
}