/* 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); }
/* Is this tx in the (other) shard affected by this tx? */ bool peer_wants_tx_other(const struct peer *peer, const union protocol_tx *tx) { u16 shard; switch (tx_type(tx)) { case TX_FROM_GATEWAY: /* These only affect one shard. */ return false; case TX_NORMAL: /* This also affects shard of output address. */ shard = shard_of(&tx->normal.output_addr, 16); return peer_wants_shard(peer, shard); case TX_TO_GATEWAY: case TX_CLAIM: /* These only affect one shard (inputs). */ return false; } abort(); }
bool find_output(const union protocol_tx *tx, u16 output_num, struct protocol_address *addr, u32 *amount) { const struct protocol_gateway_payment *out; switch (tx_type(tx)) { case TX_FROM_GATEWAY: if (output_num > le16_to_cpu(tx->from_gateway.num_outputs)) return false; out = get_from_gateway_outputs(&tx->from_gateway); *addr = out[output_num].output_addr; *amount = le32_to_cpu(out[output_num].send_amount); return true; case TX_NORMAL: if (output_num == 0) { /* Spending the send_amount. */ *addr = tx->normal.output_addr; *amount = le32_to_cpu(tx->normal.send_amount); return true; } else if (output_num == 1) { /* Spending the change. */ get_tx_input_address(tx, addr); *amount = le32_to_cpu(tx->normal.change_amount); return true; } return false; case TX_CLAIM: if (output_num == 0) { get_tx_input_address(tx, addr); *amount = le32_to_cpu(tx->claim.amount); return true; } return false; case TX_TO_GATEWAY: return false; } abort(); }
u32 tx_amount_sent(const union protocol_tx *tx) { switch (tx_type(tx)) { case TX_NORMAL: return le32_to_cpu(tx->normal.send_amount) + le32_to_cpu(tx->normal.change_amount); case TX_FROM_GATEWAY: { u32 i, total = 0; for (i = 0; i < le16_to_cpu(tx->from_gateway.num_outputs); i++){ le32 amount; amount = get_from_gateway_outputs(&tx->from_gateway) [i].send_amount; total += le32_to_cpu(amount); } return total; } case TX_TO_GATEWAY: return le32_to_cpu(tx->to_gateway.send_amount) + le32_to_cpu(tx->to_gateway.change_amount); case TX_CLAIM: return le32_to_cpu(tx->claim.amount); } abort(); }
void json_add_tx(struct json_result *response, const char *fieldname, struct state *state, const union protocol_tx *tx, const struct block *block, unsigned int confirms) { struct protocol_tx_id sha; json_object_start(response, fieldname); hash_tx(tx, &sha); json_add_tx_id(response, "txid", &sha); if (block) json_add_block_id(response, "block", &block->sha); json_add_num(response, "confirmations", confirms); json_add_num(response, "version", tx->hdr.version); json_add_num(response, "features", tx->hdr.features); switch (tx_type(tx)) { case TX_NORMAL: json_add_string(response, "type", "TX_NORMAL"); json_add_pubkey(response, "input_key", &tx->normal.input_key); json_add_address(response, "output_addr", state->test_net, &tx->normal.output_addr); json_add_num(response, "send_amount", le32_to_cpu(tx->normal.send_amount)); json_add_num(response, "change_amount", le32_to_cpu(tx->normal.change_amount)); json_add_signature(response, "signature", &tx->normal.signature); json_add_inputs(response, tx); goto finish; case TX_FROM_GATEWAY: json_add_string(response, "type", "TX_FROM_GATEWAY"); json_add_pubkey(response, "gateway_key", &tx->from_gateway.gateway_key); json_add_signature(response, "signature", &tx->normal.signature); json_add_outputs(response, state, tx); goto finish; case TX_TO_GATEWAY: json_add_string(response, "type", "TX_TO_GATEWAY"); json_add_pubkey(response, "input_key", &tx->to_gateway.input_key); json_add_address(response, "output_addr", state->test_net, &tx->to_gateway.to_gateway_addr); json_add_num(response, "send_amount", le32_to_cpu(tx->to_gateway.send_amount)); json_add_num(response, "change_amount", le32_to_cpu(tx->to_gateway.change_amount)); json_add_signature(response, "signature", &tx->to_gateway.signature); json_add_inputs(response, tx); goto finish; case TX_CLAIM: json_add_string(response, "type", "TX_CLAIM"); json_add_pubkey(response, "input_key", &tx->claim.input_key); json_add_num(response, "amount", le32_to_cpu(tx->claim.amount)); json_add_signature(response, "claim", &tx->claim.signature); json_add_input(response, "input", &tx->claim.input); goto finish; } abort(); finish: json_object_end(response); }
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; }