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); }
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); }
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); }
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); }
/* 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); }
/* 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); }
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; } }
/* 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); }
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"); }
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); }
/* 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); }
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); }
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); }
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; }
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; }
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; }
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; }