예제 #1
0
static struct io_plan *write_json(struct io_conn *conn,
				  struct json_connection *jcon)
{
	struct json_output *out;
	
	out = list_pop(&jcon->output, struct json_output, list);
	if (!out) {
		if (jcon->stop) {
			log_unusual(jcon->log, "JSON-RPC shutdown");
			/* Return us to toplevel lightningd.c */
			io_break(jcon->dstate);
			return io_close(conn);
		}

		/* Reader can go again now. */
		io_wake(jcon);
		return io_out_wait(conn, jcon, write_json, jcon);
	}

	jcon->outbuf = tal_steal(jcon, out->json);
	tal_free(out);

	log_io(jcon->log, false, jcon->outbuf, strlen(jcon->outbuf));
	return io_write(conn,
			jcon->outbuf, strlen(jcon->outbuf), write_json, jcon);
}
예제 #2
0
void complain_bad_amount(struct state *state,
			 struct block *block,
			 const struct protocol_proof *proof,
			 const union protocol_tx *tx,
			 const struct protocol_input_ref *refs,
			 const union protocol_tx *intx[])
{
	struct protocol_pkt_complain_tx_bad_amount *pkt;
	unsigned int i;

	assert(num_inputs(tx));
	log_unusual(state->log, "Block %u ", le32_to_cpu(block->hdr->depth));
	log_add_struct(state->log, struct protocol_double_sha, &block->sha);
	log_add(state->log, " invalid amounts in tx %u of shard %u ",
		proof->pos.txoff, le16_to_cpu(proof->pos.shard));
	log_add_struct(state->log, union protocol_tx, tx);
	log_add(state->log, " with inputs: ");

	pkt = tal_packet(block, struct protocol_pkt_complain_tx_bad_amount,
			 PROTOCOL_PKT_COMPLAIN_TX_BAD_AMOUNT);
	tal_packet_append_proven_tx(&pkt, proof, tx, refs);

	for (i = 0; i < num_inputs(tx); i++) {
		log_add_struct(state->log, union protocol_tx, intx[i]);
		log_add(state->log, " (output %u)",
			le16_to_cpu(tx_input(tx, i)->output));

		tal_packet_append_tx(&pkt, intx[i]);
	}

	publish_complaint(state, block, pkt, NULL);
}
예제 #3
0
void complain_bad_input(struct state *state,
			struct block *block,
			const struct protocol_proof *proof,
			const union protocol_tx *tx,
			const struct protocol_input_ref *refs,
			unsigned int bad_input,
			const union protocol_tx *intx)
{
	struct protocol_pkt_complain_tx_bad_input *pkt;

	assert(tx_input(tx, bad_input));
	log_unusual(state->log, "Block %u ", le32_to_cpu(block->hdr->depth));
	log_add_struct(state->log, struct protocol_double_sha, &block->sha);
	log_add(state->log, " invalid due to tx %u in shard %u ",
		proof->pos.txoff, le16_to_cpu(proof->pos.shard));
	log_add_struct(state->log, union protocol_tx, tx);
	log_add(state->log, " with bad input %u ", bad_input);
	log_add_struct(state->log, union protocol_tx, intx);

	pkt = tal_packet(block, struct protocol_pkt_complain_tx_bad_input,
			 PROTOCOL_PKT_COMPLAIN_TX_BAD_INPUT);
	pkt->inputnum = cpu_to_le32(bad_input);

	tal_packet_append_proven_tx(&pkt, proof, tx, refs);
	tal_packet_append_tx(&pkt, intx);

	publish_complaint(state, block, pkt, NULL);
}
예제 #4
0
void command_fail(struct command *cmd, const char *fmt, ...)
{
	char *quote, *error;
	struct json_connection *jcon = cmd->jcon;
	va_list ap;

	if (!jcon) {
		log_unusual(cmd->dstate->base_log,
			    "Command failed after jcon close");
		tal_free(cmd);
		return;
	}

	va_start(ap, fmt);
	error = tal_vfmt(cmd, fmt, ap);
	va_end(ap);

	log_debug(jcon->log, "Failing: %s", error);

	/* Remove " */
	while ((quote = strchr(error, '"')) != NULL)
		*quote = '\'';

	/* Now surround in quotes. */
	quote = tal_fmt(cmd, "\"%s\"", error);

	assert(jcon->current == cmd);
	json_result(jcon, cmd->id, "null", quote);
	jcon->current = tal_free(cmd);
}
예제 #5
0
/* We know conflict_txoff (ie. it's already in the block), and proof
 * shows another tx which is misordered relative to that. */
void complain_misorder(struct state *state,
		       struct block *block,
		       const struct protocol_proof *proof,
		       const union protocol_tx *tx,
		       const struct protocol_input_ref *refs,
		       unsigned int conflict_txoff)
{
	struct protocol_pkt_complain_tx_misorder *pkt;
	const union protocol_tx *conflict_tx;
	const struct protocol_input_ref *conflict_refs;
	struct protocol_proof conflict_proof;
	u16 shardnum = le16_to_cpu(proof->pos.shard);

	conflict_tx = block_get_tx(block, shardnum, conflict_txoff);
	conflict_refs = block_get_refs(block, shardnum, conflict_txoff);

	log_unusual(state->log, "Block %u ", le32_to_cpu(block->hdr->depth));
	log_add_struct(state->log, struct protocol_double_sha, &block->sha);
	log_add(state->log, " invalid due to misorder shard %u tx %u vs %u ",
		shardnum, conflict_txoff, proof->pos.txoff);
	log_add_struct(state->log, union protocol_tx, conflict_tx);
	log_add(state->log, " vs ");
	log_add_struct(state->log, union protocol_tx, tx);

	pkt = tal_packet(block, struct protocol_pkt_complain_tx_misorder,
			 PROTOCOL_PKT_COMPLAIN_TX_MISORDER);
	tal_packet_append_proven_tx(&pkt, proof, tx, refs);

	create_proof(&conflict_proof, block, shardnum, conflict_txoff);
	tal_packet_append_proven_tx(&pkt, &conflict_proof,
				    conflict_tx, conflict_refs);

	publish_complaint(state, block, pkt, NULL);
}
예제 #6
0
/* refs[bad_refnum] points to the wrong tx! */
void complain_bad_input_ref(struct state *state,
			    struct block *block,
			    const struct protocol_proof *proof,
			    const union protocol_tx *tx,
			    const struct protocol_input_ref *refs,
			    unsigned int bad_refnum,
			    const struct block *block_referred_to)
{
	struct protocol_pkt_complain_bad_input_ref *pkt;	
	const struct protocol_input_ref *bad_ref;
	struct protocol_proof ref_proof;
	const union protocol_tx *bad_intx;
	const struct protocol_input_ref *bad_intx_refs;
	
	bad_ref = &refs[bad_refnum];
	bad_intx = block_get_tx(block_referred_to, le16_to_cpu(bad_ref->shard),
				bad_ref->txoff);
	bad_intx_refs = block_get_refs(block_referred_to,
				       le16_to_cpu(bad_ref->shard),
				       bad_ref->txoff);

	log_unusual(state->log, "Block %u ", le32_to_cpu(block->hdr->depth));
	log_add_struct(state->log, struct protocol_double_sha, &block->sha);
	log_unusual(state->log, " tx %u of shard %u ",
		    proof->pos.txoff, le16_to_cpu(proof->pos.shard));
	log_add_struct(state->log, union protocol_tx, tx);
	log_add(state->log, 
		" invalid due to wrong input %u reference %u ago tx %u/%u ",
		bad_refnum, le32_to_cpu(bad_ref->blocks_ago),
		le16_to_cpu(bad_ref->shard), bad_ref->txoff);
	log_add_struct(state->log, union protocol_tx, bad_intx);

	pkt = tal_packet(block, struct protocol_pkt_complain_bad_input_ref,
			 PROTOCOL_PKT_COMPLAIN_BAD_INPUT_REF);
	pkt->inputnum = cpu_to_le32(bad_refnum);

	/* This is the tx which has the bad reference. */
	tal_packet_append_proven_tx(&pkt, proof, tx, refs);

	/* This is where the ref points to. */
	create_proof(&ref_proof, block_referred_to,
		     le16_to_cpu(bad_ref->shard), bad_ref->txoff);
	tal_packet_append_proven_tx(&pkt, &ref_proof, bad_intx, bad_intx_refs);

	publish_complaint(state, block, pkt, NULL);
}
예제 #7
0
static void finish_jcon(struct io_conn *conn, struct json_connection *jcon)
{
	log_debug(jcon->log, "Closing (%s)", strerror(errno));
	if (jcon->current) {
		log_unusual(jcon->log, "Abandoning current command");
		jcon->current->jcon = NULL;
	}
}
예제 #8
0
/* Simple single-transacion error. */
void complain_bad_tx(struct state *state,
		     struct block *block,
		     enum protocol_ecode err,
		     const struct protocol_proof *proof,
		     const union protocol_tx *tx,
		     const struct protocol_input_ref *refs)
{
	struct protocol_pkt_complain_tx_invalid *pkt;

	switch (err) {
	case PROTOCOL_ECODE_TX_HIGH_VERSION:
	case PROTOCOL_ECODE_TX_LOW_VERSION:
	case PROTOCOL_ECODE_TX_TYPE_UNKNOWN:
	case PROTOCOL_ECODE_TX_TOO_LARGE:
	case PROTOCOL_ECODE_TX_BAD_SIG:
		break;

	case PROTOCOL_ECODE_TX_BAD_GATEWAY:
	case PROTOCOL_ECODE_TX_CROSS_SHARDS:
		assert(tx_type(tx) == TX_FROM_GATEWAY);
		break;

	case PROTOCOL_ECODE_TX_TOO_MANY_INPUTS:
		switch (tx_type(tx)) {
		case TX_NORMAL:
		case TX_TO_GATEWAY:
			break;
		case TX_FROM_GATEWAY:
		case TX_CLAIM:
			abort();
		}
		break;

	default:
		log_broken(state->log,
			   "Unknown complain_bad_tx error ");
		log_add_enum(state->log, enum protocol_ecode, err);
		abort();
	}

	log_unusual(state->log, "Block %u ", le32_to_cpu(block->hdr->depth));
	log_add_struct(state->log, struct protocol_double_sha, &block->sha);
	log_add(state->log, " invalid due to tx %u of shard %u ",
		proof->pos.txoff, le16_to_cpu(proof->pos.shard));
	log_add(state->log, " error ");
	log_add_enum(state->log, enum protocol_ecode, err);
	/* FIXME: Would be nice to log something about invalid tx! */

	pkt = tal_packet(block, struct protocol_pkt_complain_tx_invalid,
			 PROTOCOL_PKT_COMPLAIN_TX_INVALID);
	pkt->error = cpu_to_le32(err);

	tal_packet_append_proven_tx(&pkt, proof, tx, refs);

	publish_complaint(state, block, pkt, NULL);
}
예제 #9
0
static void dump_inputhash(struct state *state, struct inputhash *inputhash)
{
	struct inputhash_elem *ie;
	struct inputhash_iter it;

	log_unusual(state->log, "Input hash start:\n");
	for (ie = inputhash_first(inputhash, &it);
	     ie;
	     ie = inputhash_next(inputhash, &it)) {
		log_unusual(state->log, "  Tx ");
		log_add_struct(state->log, struct protocol_tx_id,
			       &ie->output.tx);
		log_add(state->log, " output %u used by ",
			ie->output.output_num);
		log_add_struct(state->log, struct protocol_tx_id,
			       &ie->used_by);
	}
	log_unusual(state->log, "Input hash end\n");
}
예제 #10
0
static struct io_plan *read_json(struct io_conn *conn,
				 struct json_connection *jcon)
{
	jsmntok_t *toks;
	bool valid;

	log_io(jcon->log, true, jcon->buffer + jcon->used, jcon->len_read);

	/* Resize larger if we're full. */
	jcon->used += jcon->len_read;
	if (jcon->used == tal_count(jcon->buffer))
		tal_resize(&jcon->buffer, jcon->used * 2);

again:
	toks = json_parse_input(jcon->buffer, jcon->used, &valid);
	if (!toks) {
		if (!valid) {
			log_unusual(jcon->dstate->base_log,
				    "Invalid token in json input: '%.*s'",
				    (int)jcon->used, jcon->buffer);
			return io_close(conn);
		}
		/* We need more. */
		goto read_more;
	}

	/* Empty buffer? (eg. just whitespace). */
	if (tal_count(toks) == 1) {
		jcon->used = 0;
		goto read_more;
	}

	parse_request(jcon, toks);

	/* Remove first {}. */
	memmove(jcon->buffer, jcon->buffer + toks[0].end,
		tal_count(jcon->buffer) - toks[0].end);
	jcon->used -= toks[0].end;
	tal_free(toks);

	/* Need to wait for command to finish? */
	if (jcon->current) {
		jcon->len_read = 0;
		return io_wait(conn, jcon, read_json, jcon);
	}

	/* See if we can parse the rest. */
	goto again;

read_more:
	tal_free(toks);
	return io_read_partial(conn, jcon->buffer + jcon->used,
			       tal_count(jcon->buffer) - jcon->used,
			       &jcon->len_read, read_json, jcon);
}
예제 #11
0
/* After both fds close, this gets called. */
static void reap_generator(struct io_conn *conn, struct generator *gen)
{
	int status;
	struct state *state = gen->state;
	bool ok;
	int ret;

	log_debug(gen->log, "Generator closed");
	assert(!gen->answer);
	assert(!gen->update);

	/* If we use WHOHANG here, we get occasional failures under
	 * load.  The implicit close on exit is done before the kernel
	 * marks the process ready to be reaped, which is fair enough.
	 * But it should only close the fd on exit anyway. */
	if ((ret = waitpid(gen->pid, &status, 0)) != gen->pid) {
		ok = false;
		log_unusual(gen->log,
			    "Waiting for generator %s %u returned %i %s",
			    gen->state->generator, gen->pid, ret, strerror(errno));
	} else if (WIFSIGNALED(status) && WTERMSIG(status) != SIGUSR1) {
		log_unusual(gen->log,
			    "generator %s %u exited with signal %u",
			    gen->state->generator, gen->pid, WTERMSIG(status));
		ok = false;
	} else if (WEXITSTATUS(status) != 0) {
		ok = false;
		log_unusual(gen->log,
			    "generator %s %u exited with status %u",
			    gen->state->generator, gen->pid, WEXITSTATUS(status));
	} else {
		ok = true;
		log_debug(gen->log, "generator %s %u exited normally",
			  gen->state->generator, gen->pid);
	}

	assert(!state->gen || state->gen == gen);
	state->gen = tal_free(gen);

	if (ok)
		start_generating(state);
}
예제 #12
0
Pkt *pkt_err(struct peer *peer, const char *msg, ...)
{
	Error *e = tal(peer, Error);
	va_list ap;

	error__init(e);
	va_start(ap, msg);
	e->problem = tal_vfmt(e, msg, ap);
	va_end(ap);

	log_unusual(peer->log, "Sending PKT_ERROR: %s", e->problem);
	return make_pkt(peer, PKT__PKT_ERROR, e);
}
예제 #13
0
void command_success(struct command *cmd, struct json_result *result)
{
    struct json_connection *jcon = cmd->jcon;

    if (!jcon) {
        log_unusual(cmd->dstate->base_log,
                    "Command returned result after jcon close");
        tal_free(cmd);
        return;
    }
    assert(jcon->current == cmd);
    json_result(jcon, cmd->id, json_result_string(result), "null");
    jcon->current = tal_free(cmd);
}
예제 #14
0
static bool insert_pending_tx(struct state *state, const union protocol_tx *tx)
{
	struct pending_block *pending = state->pending;
	struct pending_tx *pend;
	u16 shard;
	size_t num, pos;

	shard = shard_of_tx(tx, next_shard_order(state->longest_knowns[0]));
	num = tal_count(pending->pend[shard]);

	if (num == 255) {
		log_unusual(state->log,
			    "Too many pending txs in shard %u", shard);
		/* Treat it as unknown, so it will get in next time. */
		add_to_unknown_pending(state, tx);
		return true;
	}

	/* Caller checks it isn't a dup! */
	if (find_pending_in_arr(pending->pend[shard], tx, &pos))
		abort();

	pend = new_pending_tx(state->pending, tx);
	pend->refs = create_refs(state, state->longest_knowns[0], tx, 1);

	/* If inputs are too *old*, we can fail to make references. */
	if (!pend->refs)
		return false;

	/* Insert into array at pos. */
	tal_arr_add(&pending->pend[shard], pos, pend);

	log_debug(state->log, "Added tx to shard %u position %zu",
		  shard, pos);
	tell_generator_new_pending(state, shard, pos);
	return true;
}
예제 #15
0
int main(void)
{
	struct state *state;
	union protocol_tx *t, *t2;
	struct protocol_gateway_payment payment;
	u8 *prev_txhashes;
	enum input_ecode e;
	struct protocol_tx_id txid;
	unsigned int bad_input;
	bool too_old, already_known;
	const struct block *b;
	struct protocol_block_id prevs[PROTOCOL_NUM_PREV_IDS];
	struct protocol_input inputs[1];
	
	pseudorand_init();
	state = new_state(true);

	prev_txhashes = make_prev_txhashes(state, &genesis, helper_addr(1));
	memset(prevs, 0, sizeof(prevs));
	prevs[0] = genesis.sha;
	w = new_working_block(state, block_difficulty(&genesis.bi),
			      prev_txhashes, tal_count(prev_txhashes),
			      block_height(&genesis.bi) + 1,
			      next_shard_order(&genesis),
			      prevs, helper_addr(1));

	/* Create a block with a gateway tx in it. */
	payment.send_amount = cpu_to_le32(1000);
	payment.output_addr = *helper_addr(0);
	t = create_from_gateway_tx(state, helper_gateway_public_key(),
				   1, &payment, false, helper_gateway_key(state));
	hash_tx(t, &txid);
	log_unusual(state->log, "Gateway tx is ");
	log_add_struct(state->log, struct protocol_tx_id, &txid);

	e = add_pending_tx(state, t, &txid, &bad_input,
			   &too_old, &already_known);
	assert(e == ECODE_INPUT_OK);
	assert(!already_known);

	assert(state->pending->num_unknown == 0);
	assert(num_pending_known(state) == 1);

	b = solve_pending(state);

	/* Now we can spend it. */
	prev_txhashes = make_prev_txhashes(state, b, helper_addr(1));
	prevs[0] = b->sha;
	prevs[1] = genesis.sha;
	w = new_working_block(state, block_difficulty(&b->bi),
			      prev_txhashes, tal_count(prev_txhashes),
			      block_height(&b->bi) + 1,
			      next_shard_order(b),
			      prevs, helper_addr(1));

	hash_tx(t, &inputs[0].input);
	inputs[0].output = 0;
	inputs[0].unused = 0;

	t2 = t = create_normal_tx(state, helper_addr(1),
				  500, 500 - PROTOCOL_FEE(500), 1, true, inputs,
				  helper_private_key(state, 0));
	
	hash_tx(t, &txid);
	e = add_pending_tx(state, t, &txid, &bad_input,
			   &too_old, &already_known);
	assert(e == ECODE_INPUT_OK);
	assert(!already_known);
	assert(state->pending->num_unknown == 0);
	assert(num_pending_known(state) == 1);

	log_unusual(state->log, "Normal tx is ");
	log_add_struct(state->log, struct protocol_tx_id, &txid);

	/* Should recognize double spend */
	t = create_normal_tx(state, helper_addr(2),
			     300, 700 - PROTOCOL_FEE(300), 1, true, inputs,
			     helper_private_key(state, 0));
	
	hash_tx(t, &txid);
	e = add_pending_tx(state, t, &txid, &bad_input,
			   &too_old, &already_known);
	assert(e == ECODE_INPUT_DOUBLESPEND);
	assert(!already_known);

	assert(state->pending->num_unknown == 0);
	assert(num_pending_known(state) == 1);

	b = solve_pending(state);
	/* The first should be included. */
	assert(num_txs(b) == 1);

	/* There should be nothing left. */ 
	assert(state->pending->num_unknown == 0);
	assert(num_pending_known(state) == 0);

	/* Now retry double spend. */
	t = create_normal_tx(state, helper_addr(2),
			     300, 700 - PROTOCOL_FEE(300), 1, true, inputs,
			     helper_private_key(state, 0));
	
	hash_tx(t, &txid);
	e = add_pending_tx(state, t, &txid, &bad_input,
			   &too_old, &already_known);
	assert(e == ECODE_INPUT_DOUBLESPEND);
	assert(!too_old);
	assert(!already_known);

	/* Clear inputhash manually. */
	inputhash_del_tx(&state->inputhash, t2);

	dump_inputhash(state, &state->inputhash);
	tal_free(state);
	return 0;
}
예제 #16
0
enum input_ecode add_pending_tx(struct state *state,
				const union protocol_tx *tx,
				const struct protocol_double_sha *sha,
				unsigned int *bad_input_num,
				bool *too_old)
{
	enum input_ecode ierr;

	/* If it's already in longest known chain, would look like
	 * doublespend so sort that out now. */
	if (txhash_gettx_ancestor(state, sha, state->longest_knowns[0]))
		return ECODE_INPUT_OK;

	/* If we already have it in pending, don't re-add. */
	if (txhash_get_pending_tx(state, sha))
		return ECODE_INPUT_OK;

	/* We check inputs for where *we* would mine it.
	 * We currently don't allow two dependent txs in the same block,
	 * so only resolve inputs in the chain. */
	ierr = check_tx_inputs(state, state->longest_knowns[0],
			       NULL, tx, bad_input_num);

	if (ierr == ECODE_INPUT_OK) {
		/* But that doesn't find doublespends in *pending*. */
		if (find_pending_doublespend(state, tx))
			ierr = ECODE_INPUT_DOUBLESPEND;
	}

	switch (ierr) {
	case ECODE_INPUT_OK:
		break;
	case ECODE_INPUT_UNKNOWN:
		/* FIXME: If we did a check_tx_inputs() which included
		 * TX_PENDING now, we might find doublespends or bad
		 * amounts already. */
		break;
	case ECODE_INPUT_BAD:
	case ECODE_INPUT_BAD_AMOUNT:
	case ECODE_INPUT_DOUBLESPEND:
	case ECODE_INPUT_CLAIM_BAD:
		log_debug(state->log, "Check tx inputs said ");
		log_add_enum(state->log, enum input_ecode, ierr);
		log_add(state->log, " for tx ");
		log_add_struct(state->log, union protocol_tx, tx);
		if (too_old)
			*too_old = false;
		return ierr;
	}

	/* We still want to transmit these to peers, just not keep them
	 * ourselves. */
	switch (tx_type(tx)) {
	case TX_NORMAL:
	case TX_TO_GATEWAY:
	case TX_CLAIM:
		if (state->require_non_gateway_tx_fee && !tx_pays_fee(tx)) {
			log_info(state->log, "Dropping feeless normal tx ");
			log_add_struct(state->log, union protocol_tx, tx);
			/* If we're ECODE_INPUT_UNKNOWN, we don't care. */
			return ECODE_INPUT_OK;
		}
		break;
	case TX_FROM_GATEWAY:
		if (state->require_gateway_tx_fee && !tx_pays_fee(tx)) {
			log_unusual(state->log, "Dropping feeless gateway tx ");
			log_add_struct(state->log, union protocol_tx, tx);
			/* If we're ECODE_INPUT_UNKNOWN, we don't care. */
			return ECODE_INPUT_OK;
		}
예제 #17
0
int main(void)
{
	struct protocol_double_sha dsha;
	struct protocol_net_address netaddr;
	int fds[2];
	char *p, *mem1, *mem2, *mem3;
	int status;
	size_t maxmem = sizeof(struct log_entry) * 4 + 25 + 25 + 28 + 161;
	void *ctx = tal(NULL, char);
	struct log *log = new_log(ctx, NULL, "PREFIX", LOG_BROKEN+1, maxmem);

	assert(tal_parent(log) == ctx);
	my_time.ts.tv_sec = 1384064855;
	my_time.ts.tv_nsec = 500;

	log_debug(log, "This is a debug %s!", "message");
	my_time.ts.tv_nsec++;
	log_info(log, "This is an info %s!", "message");
	my_time.ts.tv_nsec++;
	log_unusual(log, "This is an unusual %s!", "message");
	my_time.ts.tv_nsec++;
	log_broken(log, "This is a broken %s!", "message");
	my_time.ts.tv_nsec++;

	log_add(log, "the sha is ");
	memset(&dsha, 0xFF, sizeof(dsha));
	log_add_struct(log, struct protocol_double_sha, &dsha);

	log_add(log, " and the address is: ");
	memset(netaddr.addr, 0, 10);
	memset(netaddr.addr + 10, 0xFF, 2);
	netaddr.addr[12] = 127;
	netaddr.addr[13] = 0;
	netaddr.addr[14] = 0;
	netaddr.addr[15] = 1;
	netaddr.port = cpu_to_le16(65000);
	netaddr.time = time_now().ts.tv_sec - 10;
	log_add_struct(log, struct protocol_net_address, &netaddr);

	/* Make child write log, be sure it's correct. */
	pipe(fds);
	switch (fork()) {
	case -1:
		err(1, "forking");
	case 0:
		close(fds[0]);
		setenv("TZ", "UTC", 1);
		log_to_file(fds[1], log);
		tal_free(ctx);
		exit(0);
	}

	close(fds[1]);
	p = read_from(ctx, fds[0]);
	/* Shouldn't contain any NUL chars */
	assert(strlen(p) + 1 == tal_count(p));

	assert(tal_strreg(p, p,
			  "PREFIX ([0-9])* bytes, Sun Nov 10 06:27:35 2013\n"
			  "\\+0\\.000000500 DEBUG: This is a debug message!\n"
			  "\\+0\\.000000501 INFO: This is an info message!\n"
			  "\\+0\\.000000502 UNUSUAL: This is an unusual message!\n"
			  "\\+0\\.000000503 BROKEN: This is a broken message!the sha is ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff and the address is: ::ffff:127\\.0\\.0\\.1:65000 \\(10 seconds old\\)\n\n", &mem1));
	assert(atoi(mem1) < maxmem);
	tal_free(p);

	wait(&status);
	assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);

	/* This cleans us out! */
	log_debug(log, "Overflow!");
	
	/* Make child write log, be sure it's correct. */
	pipe(fds);
	switch (fork()) {
	case -1:
		err(1, "forking");
	case 0:
		close(fds[0]);
		setenv("TZ", "UTC", 1);
		log_to_file(fds[1], log);
		tal_free(ctx);
		exit(0);
	}

	close(fds[1]);

	p = read_from(ctx, fds[0]);
	/* Shouldn't contain any NUL chars */
	assert(strlen(p) + 1 == tal_count(p));

	assert(tal_strreg(p, p,
			  "PREFIX ([0-9]*) bytes, Sun Nov 10 06:27:35 2013\n"
			  "\\.\\.\\. 4 skipped\\.\\.\\.\n"
			  "\\+0.000000504 DEBUG: Overflow!\n"
			  "\\+0.000000504 DEBUG: Log pruned 4 entries \\(mem ([0-9]*) -> ([0-9]*)\\)\n\n", &mem1, &mem2, &mem3));
	assert(atoi(mem1) < maxmem);
	assert(atoi(mem2) >= maxmem);
	assert(atoi(mem3) < maxmem);
	tal_free(ctx);
	wait(&status);
	assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);
	return 0;
}