void remove_tx_from_hashes(struct state *state, struct block *block, u16 shard, u8 txoff) { struct protocol_double_sha scratch; const struct protocol_double_sha *txhash; const union protocol_tx *tx; union txhash_block_or_tx u; u.block = block; if (shard_is_tx(block->shard[shard], txoff)) { tx = tx_for(block->shard[shard], txoff); hash_tx(tx, &scratch); txhash = &scratch; } else { txhash = &block->shard[shard]->u[txoff].hash->txhash; tx = NULL; } txhash_del_tx(&state->txhash, u, shard, txoff, TX_IN_BLOCK, txhash); /* If this tx is no longer known at *all*, we can remove from * input hash too. */ if (tx && !txhash_gettx(&state->txhash, txhash, TX_PENDING)) inputhash_del_tx(&state->inputhash, tx); }
void hash_tx_and_refs(const union protocol_tx *tx, const struct protocol_input_ref *refs, struct protocol_txrefhash *txrefhash) { hash_tx(tx, &txrefhash->txhash); hash_refs(refs, num_inputs(tx), &txrefhash->refhash); }
void remove_pending_tx_from_hashes(struct state *state, const union protocol_tx *tx) { struct protocol_double_sha txhash; union txhash_block_or_tx u; hash_tx(tx, &txhash); u.tx = tx; txhash_del_tx(&state->txhash, u, 0, 0, TX_PENDING, &txhash); /* If this tx is no longer known at *all*, we can remove from * input hash too. */ if (!txhash_gettx(&state->txhash, &txhash, TX_PENDING)) inputhash_del_tx(&state->inputhash, tx); }
void add_tx_to_hashes(struct state *state, const tal_t *ctx, struct block *block, u16 shard, u8 txoff, const union protocol_tx *tx) { struct protocol_double_sha txhash; hash_tx(tx, &txhash); /* First time we heard of full transaction? Add inputs. */ if (!txhash_gettx(&state->txhash, &txhash, TX_PENDING)) inputhash_add_tx(&state->inputhash, ctx, tx); add_txhash_to_hashes(state, ctx, block, shard, txoff, &txhash); }
void add_pending_tx_to_hashes(struct state *state, const tal_t *ctx, const union protocol_tx *tx) { struct protocol_double_sha txhash; union txhash_block_or_tx u; hash_tx(tx, &txhash); assert(!txhash_get_pending_tx(state, &txhash)); /* First time we heard of full transaction? Add inputs. */ if (!txhash_gettx(&state->txhash, &txhash, TX_PENDING)) inputhash_add_tx(&state->inputhash, ctx, tx); u.tx = tx; txhash_add_tx(&state->txhash, ctx, u, 0, 0, TX_PENDING, &txhash); }
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; }
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); }
/* We've added a whole heap of transactions, recheck them and set input refs. */ void recheck_pending_txs(struct state *state) { unsigned int unknown, known, total; unsigned int i, shard; const union protocol_tx **txs; struct pending_unknown_tx *utx; if (!state->pending->needs_recheck) return; state->pending->needs_recheck = false; /* Size up and allocate an array. */ unknown = state->pending->num_unknown; known = num_pending_known(state); /* Avoid logging if nothing pending. */ if (unknown == 0 && known == 0) return; txs = tal_arr(state, const union protocol_tx *, unknown + known); log_info(state->log, "Rechecking pending (%u known, %u unknown)", known, unknown); /* Now move pending from shards. */ total = 0; for (shard = 0; shard < ARRAY_SIZE(state->pending->pend); shard++) { struct pending_tx **pend = state->pending->pend[shard]; unsigned int i; for (i = 0; i < tal_count(pend); i++) { remove_pending_tx_from_hashes(state, pend[i]->tx); txs[total++] = tal_steal(txs, pend[i]->tx); } } /* And last we move the unknown ones. */ while ((utx = list_pop(&state->pending->unknown_tx, struct pending_unknown_tx, list)) != NULL) { remove_pending_tx_from_hashes(state, utx->tx); txs[total++] = tal_steal(txs, utx->tx); } assert(total == unknown + known); /* Clean up pending (frees everything above as a side effect). */ tal_free(state->pending); state->pending = new_pending_block(state); /* Now re-add them */ for (i = 0; i < tal_count(txs); i++) { unsigned int bad_input_num; struct protocol_double_sha sha; hash_tx(txs[i], &sha); add_pending_tx(state, txs[i], &sha, &bad_input_num, NULL); } /* Just to print the debug! */ known = 0; for (shard = 0; shard < ARRAY_SIZE(state->pending->pend); shard++) known += tal_count(state->pending->pend[shard]); log_info(state->log, "Now have %u known, %u unknown", known, state->pending->num_unknown); /* Restart generator on this block. */ restart_generating(state); }