Browse Source

protocol: increase HTLC precision to 1/1000 satoshi.

This gets truncated for on-chain transactions (thus, rounding may
contribute to fees).

This also means we currently have an upper bound of 0.04 BTC per HTLC;
this can be increased later if required.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 10 years ago
parent
commit
5bc22f0368
  1. 6
      commit_tx.c
  2. 54
      funding.c
  3. 9
      funding.h
  4. 14
      lightning.pb-c.c
  5. 6
      lightning.pb-c.h
  6. 6
      lightning.proto
  7. 10
      pkt.c
  8. 2
      pkt.h
  9. 4
      test-cli/close-channel.c
  10. 4
      test-cli/create-close-tx.c
  11. 14
      test-cli/gather_updates.c
  12. 6
      test-cli/scripts/setup.sh
  13. 2
      test-cli/scripts/test.sh

6
commit_tx.c

@ -38,7 +38,7 @@ static bool add_htlc(struct bitcoin_tx *tx, size_t n,
htlc_abstime, locktime, rhash, htlc_abstime, locktime, rhash,
&htlc_rhash)); &htlc_rhash));
tx->output[n].script_length = tal_count(tx->output[n].script); tx->output[n].script_length = tal_count(tx->output[n].script);
tx->output[n].amount = h->amount; tx->output[n].amount = h->amount_msat / 1000;
return true; return true;
} }
@ -81,14 +81,14 @@ struct bitcoin_tx *create_commit_tx(const tal_t *ctx,
rhash); rhash);
tx->output[0].script = scriptpubkey_p2sh(tx, redeemscript); tx->output[0].script = scriptpubkey_p2sh(tx, redeemscript);
tx->output[0].script_length = tal_count(tx->output[0].script); tx->output[0].script_length = tal_count(tx->output[0].script);
tx->output[0].amount = cstate->a.pay; tx->output[0].amount = cstate->a.pay_msat / 1000;
/* Second output is a P2SH payment to them. */ /* Second output is a P2SH payment to them. */
tx->output[1].script = scriptpubkey_p2sh(ctx, tx->output[1].script = scriptpubkey_p2sh(ctx,
bitcoin_redeem_single(ctx, bitcoin_redeem_single(ctx,
&theirkey)); &theirkey));
tx->output[1].script_length = tal_count(tx->output[1].script); tx->output[1].script_length = tal_count(tx->output[1].script);
tx->output[1].amount = cstate->b.pay; tx->output[1].amount = cstate->b.pay_msat / 1000;
/* First two outputs done, now for the HTLCs. */ /* First two outputs done, now for the HTLCs. */
total = tx->output[0].amount + tx->output[1].amount; total = tx->output[0].amount + tx->output[1].amount;

54
funding.c

@ -11,7 +11,7 @@ static bool subtract_fees(uint64_t *funder, uint64_t *non_funder,
uint64_t *funder_fee, uint64_t *non_funder_fee, uint64_t *funder_fee, uint64_t *non_funder_fee,
bool non_funder_paying, uint64_t fee) bool non_funder_paying, uint64_t fee)
{ {
/* Funder gets 1 satsoshi rounding benefit! */ /* Funder gets 1 millisatsoshi rounding benefit! */
*non_funder_fee = fee - fee / 2; *non_funder_fee = fee - fee / 2;
if (*non_funder < *non_funder_fee) { if (*non_funder < *non_funder_fee) {
@ -37,70 +37,71 @@ static bool subtract_fees(uint64_t *funder, uint64_t *non_funder,
return true; return true;
} }
static uint64_t htlcs_total(UpdateAddHtlc *const *htlcs) /* Total, in millisatoshi. */
static uint32_t htlcs_total(UpdateAddHtlc *const *htlcs)
{ {
size_t i, n = tal_count(htlcs); size_t i, n = tal_count(htlcs);
uint64_t total = 0; uint32_t total = 0;
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
total += htlcs[i]->amount; total += htlcs[i]->amount_msat;
return total; return total;
} }
bool funding_delta(const OpenChannel *oa, bool funding_delta(const OpenChannel *oa,
const OpenChannel *ob, const OpenChannel *ob,
const OpenAnchor *anchor, const OpenAnchor *anchor,
int64_t delta_a, int64_t delta_a_msat,
int64_t htlc, int64_t htlc_msat,
struct channel_oneside *a_side, struct channel_oneside *a_side,
struct channel_oneside *b_side) struct channel_oneside *b_side)
{ {
uint64_t a, b, a_fee, b_fee; uint64_t a, b, a_fee, b_fee;
int64_t delta_b; int64_t delta_b_msat;
uint64_t fee; uint64_t fee;
bool got_fees; bool got_fees;
a = a_side->pay + a_side->fee; a = a_side->pay_msat + a_side->fee_msat;
b = b_side->pay + b_side->fee; b = b_side->pay_msat + b_side->fee_msat;
fee = a_side->fee + b_side->fee; fee = a_side->fee_msat + b_side->fee_msat;
assert(a + b + htlcs_total(a_side->htlcs) + htlcs_total(b_side->htlcs) assert(a + b + htlcs_total(a_side->htlcs) + htlcs_total(b_side->htlcs)
== anchor->amount); == anchor->amount * 1000);
/* Only one can be funder. */ /* Only one can be funder. */
if (is_funder(oa) == is_funder(ob)) if (is_funder(oa) == is_funder(ob))
return false; return false;
/* B gets whatever A gives. */ /* B gets whatever A gives. */
delta_b = -delta_a; delta_b_msat = -delta_a_msat;
/* A also pays for the htlc (if any). */ /* A also pays for the htlc (if any). */
delta_a -= htlc; delta_a_msat -= htlc_msat;
/* Transferring more than we have? */ /* Transferring more than we have? */
if (delta_b < 0 && -delta_b > b) if (delta_b_msat < 0 && -delta_b_msat > b)
return false; return false;
if (delta_a < 0 && -delta_a > a) if (delta_a_msat < 0 && -delta_a_msat > a)
return false; return false;
/* Adjust amounts. */ /* Adjust amounts. */
a += delta_a; a += delta_a_msat;
b += delta_b; b += delta_b_msat;
/* Take off fee from both parties if possible. */ /* Take off fee from both parties if possible. */
if (is_funder(oa)) if (is_funder(oa))
got_fees = subtract_fees(&a, &b, &a_fee, &b_fee, got_fees = subtract_fees(&a, &b, &a_fee, &b_fee,
delta_b < 0, fee); delta_b_msat < 0, fee);
else else
got_fees = subtract_fees(&b, &a, &b_fee, &a_fee, got_fees = subtract_fees(&b, &a, &b_fee, &a_fee,
delta_a < 0, fee); delta_a_msat < 0, fee);
if (!got_fees) if (!got_fees)
return false; return false;
/* Now we know we're succeeding, update caller's state */ /* Now we know we're succeeding, update caller's state */
a_side->pay = a; a_side->pay_msat = a;
b_side->pay = b; b_side->pay_msat = b;
a_side->fee = a_fee; a_side->fee_msat = a_fee;
b_side->fee = b_fee; b_side->fee_msat = b_fee;
return true; return true;
} }
@ -118,9 +119,12 @@ struct channel_state *initial_funding(const tal_t *ctx,
if (fee > anchor->amount) if (fee > anchor->amount)
return tal_free(state); return tal_free(state);
if (anchor->amount > (1ULL << 32) / 1000)
return tal_free(state);
/* Initially, all goes back to funder. */ /* Initially, all goes back to funder. */
state->a.pay = anchor->amount - fee; state->a.pay_msat = anchor->amount * 1000 - fee * 1000;
state->a.fee = fee; state->a.fee_msat = fee * 1000;
/* If B (not A) is funder, invert. */ /* If B (not A) is funder, invert. */
if (is_funder(b)) if (is_funder(b))

9
funding.h

@ -6,7 +6,8 @@
#include "lightning.pb-c.h" #include "lightning.pb-c.h"
struct channel_oneside { struct channel_oneside {
uint64_t pay, fee; /* Payment and fee is in millisatoshi. */
uint32_t pay_msat, fee_msat;
/* Use tal_count to get the number */ /* Use tal_count to get the number */
UpdateAddHtlc **htlcs; UpdateAddHtlc **htlcs;
}; };
@ -21,7 +22,7 @@ struct channel_state {
* @a: A's openchannel offer * @a: A's openchannel offer
* @b: B's openchannel offer * @b: B's openchannel offer
* @anchor: The anchor offer (A or B) * @anchor: The anchor offer (A or B)
* @fee: amount to pay in fees. * @fee: amount to pay in fees (in satoshi).
* *
* Returns state, or NULL if malformed. * Returns state, or NULL if malformed.
*/ */
@ -36,8 +37,8 @@ struct channel_state *initial_funding(const tal_t *ctx,
* @a: A's openchannel offer * @a: A's openchannel offer
* @b: B's openchannel offer * @b: B's openchannel offer
* @anchor: The anchor offer (A or B) * @anchor: The anchor offer (A or B)
* @delta_a: How much A changes (-ve => A pay B, +ve => B pays A) * @delta_a: How many millisatoshi A changes (-ve => A pay B, +ve => B pays A)
* @htlc: How much A is putting into a HTLC (-ve if htlc is cancelled) * @htlc: Millisatoshi A is putting into a HTLC (-ve if htlc is cancelled)
* @a_side: channel a's state to update. * @a_side: channel a's state to update.
* @b_side: channel b's state to update. * @b_side: channel b's state to update.
*/ */

14
lightning.pb-c.c

@ -1476,12 +1476,12 @@ static const ProtobufCFieldDescriptor update__field_descriptors[2] =
0,NULL,NULL /* reserved1,reserved2, etc */ 0,NULL,NULL /* reserved1,reserved2, etc */
}, },
{ {
"delta", "delta_msat",
2, 2,
PROTOBUF_C_LABEL_REQUIRED, PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_SINT64, PROTOBUF_C_TYPE_SINT64,
0, /* quantifier_offset */ 0, /* quantifier_offset */
offsetof(Update, delta), offsetof(Update, delta_msat),
NULL, NULL,
NULL, NULL,
0, /* flags */ 0, /* flags */
@ -1489,7 +1489,7 @@ static const ProtobufCFieldDescriptor update__field_descriptors[2] =
}, },
}; };
static const unsigned update__field_indices_by_name[] = { static const unsigned update__field_indices_by_name[] = {
1, /* field[1] = delta */ 1, /* field[1] = delta_msat */
0, /* field[0] = revocation_hash */ 0, /* field[0] = revocation_hash */
}; };
static const ProtobufCIntRange update__number_ranges[1 + 1] = static const ProtobufCIntRange update__number_ranges[1 + 1] =
@ -1527,12 +1527,12 @@ static const ProtobufCFieldDescriptor update_add_htlc__field_descriptors[4] =
0,NULL,NULL /* reserved1,reserved2, etc */ 0,NULL,NULL /* reserved1,reserved2, etc */
}, },
{ {
"amount", "amount_msat",
2, 2,
PROTOBUF_C_LABEL_REQUIRED, PROTOBUF_C_LABEL_REQUIRED,
PROTOBUF_C_TYPE_UINT64, PROTOBUF_C_TYPE_UINT32,
0, /* quantifier_offset */ 0, /* quantifier_offset */
offsetof(UpdateAddHtlc, amount), offsetof(UpdateAddHtlc, amount_msat),
NULL, NULL,
NULL, NULL,
0, /* flags */ 0, /* flags */
@ -1564,7 +1564,7 @@ static const ProtobufCFieldDescriptor update_add_htlc__field_descriptors[4] =
}, },
}; };
static const unsigned update_add_htlc__field_indices_by_name[] = { static const unsigned update_add_htlc__field_indices_by_name[] = {
1, /* field[1] = amount */ 1, /* field[1] = amount_msat */
3, /* field[3] = expiry */ 3, /* field[3] = expiry */
2, /* field[2] = r_hash */ 2, /* field[2] = r_hash */
0, /* field[0] = revocation_hash */ 0, /* field[0] = revocation_hash */

6
lightning.pb-c.h

@ -233,7 +233,7 @@ struct _Update
/* /*
* Change in current payment to-me (implies reverse to-you). * Change in current payment to-me (implies reverse to-you).
*/ */
int64_t delta; int64_t delta_msat;
}; };
#define UPDATE__INIT \ #define UPDATE__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&update__descriptor) \ { PROTOBUF_C_MESSAGE_INIT (&update__descriptor) \
@ -251,9 +251,9 @@ struct _UpdateAddHtlc
*/ */
Sha256Hash *revocation_hash; Sha256Hash *revocation_hash;
/* /*
* Amount for htlc * Amount for htlc (millisatoshi)
*/ */
uint64_t amount; uint32_t amount_msat;
/* /*
* Hash for HTLC R value. * Hash for HTLC R value.
*/ */

6
lightning.proto

@ -99,15 +99,15 @@ message update {
// Hash for which I will supply preimage to revoke this. // Hash for which I will supply preimage to revoke this.
required sha256_hash revocation_hash = 1; required sha256_hash revocation_hash = 1;
// Change in current payment to-me (implies reverse to-you). // Change in current payment to-me (implies reverse to-you).
required sint64 delta = 2; required sint64 delta_msat = 2;
} }
// Start a new commitment tx to add an HTLC me -> you. // Start a new commitment tx to add an HTLC me -> you.
message update_add_htlc { message update_add_htlc {
// Hash for which I will supply preimage to revoke this commitment tx. // Hash for which I will supply preimage to revoke this commitment tx.
required sha256_hash revocation_hash = 1; required sha256_hash revocation_hash = 1;
// Amount for htlc // Amount for htlc (millisatoshi)
required uint64 amount = 2; required uint32 amount_msat = 2;
// Hash for HTLC R value. // Hash for HTLC R value.
required sha256_hash r_hash = 3; required sha256_hash r_hash = 3;
// Time at which HTLC expires (absolute) // Time at which HTLC expires (absolute)

10
pkt.c

@ -136,21 +136,25 @@ struct pkt *update_pkt(const tal_t *ctx,
{ {
Update u = UPDATE__INIT; Update u = UPDATE__INIT;
u.revocation_hash = sha256_to_proto(ctx, revocation_hash); u.revocation_hash = sha256_to_proto(ctx, revocation_hash);
u.delta = delta; u.delta_msat = delta * 1000;
return to_pkt(ctx, PKT__PKT_UPDATE, &u); return to_pkt(ctx, PKT__PKT_UPDATE, &u);
} }
struct pkt *update_htlc_add_pkt(const tal_t *ctx, struct pkt *update_htlc_add_pkt(const tal_t *ctx,
const struct sha256 *revocation_hash, const struct sha256 *revocation_hash,
u64 value, u32 value,
const struct sha256 *htlc_rhash, const struct sha256 *htlc_rhash,
u32 abs_locktime_seconds) u32 abs_locktime_seconds)
{ {
UpdateAddHtlc u = UPDATE_ADD_HTLC__INIT; UpdateAddHtlc u = UPDATE_ADD_HTLC__INIT;
Locktime l = LOCKTIME__INIT; Locktime l = LOCKTIME__INIT;
/* HTLC total must fit in 32 bits. */
if (value > (1ULL << 32) / 1000)
return NULL;
u.revocation_hash = sha256_to_proto(ctx, revocation_hash); u.revocation_hash = sha256_to_proto(ctx, revocation_hash);
u.amount = value; u.amount_msat = value * 1000;
u.r_hash = sha256_to_proto(ctx, htlc_rhash); u.r_hash = sha256_to_proto(ctx, htlc_rhash);
l.locktime_case = LOCKTIME__LOCKTIME_SECONDS; l.locktime_case = LOCKTIME__LOCKTIME_SECONDS;
l.seconds = abs_locktime_seconds; l.seconds = abs_locktime_seconds;

2
pkt.h

@ -100,7 +100,7 @@ struct pkt *update_pkt(const tal_t *ctx,
*/ */
struct pkt *update_htlc_add_pkt(const tal_t *ctx, struct pkt *update_htlc_add_pkt(const tal_t *ctx,
const struct sha256 *revocation_hash, const struct sha256 *revocation_hash,
u64 value, u32 value,
const struct sha256 *htlc_rhash, const struct sha256 *htlc_rhash,
u32 abs_locktime_seconds); u32 abs_locktime_seconds);

4
test-cli/close-channel.c

@ -88,7 +88,9 @@ int main(int argc, char *argv[])
/* This is what the anchor pays to. */ /* This is what the anchor pays to. */
redeemscript = bitcoin_redeem_2of2(ctx, &pubkey1, &pubkey2); redeemscript = bitcoin_redeem_2of2(ctx, &pubkey1, &pubkey2);
close_tx = create_close_tx(ctx, o1, o2, a, cstate->a.pay, cstate->b.pay); close_tx = create_close_tx(ctx, o1, o2, a,
cstate->a.pay_msat / 1000,
cstate->b.pay_msat / 1000);
/* Sign it for them. */ /* Sign it for them. */
sign_tx_input(ctx, close_tx, 0, redeemscript, tal_count(redeemscript), sign_tx_input(ctx, close_tx, 0, redeemscript, tal_count(redeemscript),

4
test-cli/create-close-tx.c

@ -66,7 +66,9 @@ int main(int argc, char *argv[])
redeemscript = bitcoin_redeem_2of2(ctx, &pubkey1, &pubkey2); redeemscript = bitcoin_redeem_2of2(ctx, &pubkey1, &pubkey2);
/* Now create the close tx to spend 2/2 output of anchor. */ /* Now create the close tx to spend 2/2 output of anchor. */
close_tx = create_close_tx(ctx, o1, o2, a, cstate->a.pay, cstate->b.pay); close_tx = create_close_tx(ctx, o1, o2, a,
cstate->a.pay_msat / 1000,
cstate->b.pay_msat / 1000);
/* Signatures well-formed? */ /* Signatures well-formed? */
sig1.stype = sig2.stype = SIGHASH_ALL; sig1.stype = sig2.stype = SIGHASH_ALL;

14
test-cli/gather_updates.c

@ -144,7 +144,7 @@ struct channel_state *gather_updates(const tal_t *ctx,
sig = pkt->open_commit_sig->sig; sig = pkt->open_commit_sig->sig;
break; break;
case PKT__PKT_UPDATE_ADD_HTLC: case PKT__PKT_UPDATE_ADD_HTLC:
amount = pkt->update_add_htlc->amount; amount = pkt->update_add_htlc->amount_msat;
if (received) { if (received) {
if (!funding_delta(o2, o1, oa, 0, amount, if (!funding_delta(o2, o1, oa, 0, amount,
&cstate->b, &cstate->a)) &cstate->b, &cstate->a))
@ -173,7 +173,7 @@ struct channel_state *gather_updates(const tal_t *ctx,
pkt->update_remove_htlc->r_hash); pkt->update_remove_htlc->r_hash);
if (n == tal_count(cstate->b.htlcs)) if (n == tal_count(cstate->b.htlcs))
errx(1, "Unknown R hash in %s", *argv); errx(1, "Unknown R hash in %s", *argv);
amount = cstate->b.htlcs[n]->amount; amount = cstate->b.htlcs[n]->amount_msat;
if (!funding_delta(o2, o1, oa, 0, -amount, if (!funding_delta(o2, o1, oa, 0, -amount,
&cstate->b, &cstate->a)) &cstate->b, &cstate->a))
errx(1, "Impossible htlc %llu %s", errx(1, "Impossible htlc %llu %s",
@ -184,7 +184,7 @@ struct channel_state *gather_updates(const tal_t *ctx,
pkt->update_remove_htlc->r_hash); pkt->update_remove_htlc->r_hash);
if (n == tal_count(cstate->a.htlcs)) if (n == tal_count(cstate->a.htlcs))
errx(1, "Unknown R hash in %s", *argv); errx(1, "Unknown R hash in %s", *argv);
amount = cstate->a.htlcs[n]->amount; amount = cstate->a.htlcs[n]->amount_msat;
if (!funding_delta(o1, o2, oa, 0, -amount, if (!funding_delta(o1, o2, oa, 0, -amount,
&cstate->a, &cstate->b)) &cstate->a, &cstate->b))
errx(1, "Impossible htlc %llu %s", errx(1, "Impossible htlc %llu %s",
@ -211,7 +211,7 @@ struct channel_state *gather_updates(const tal_t *ctx,
n = find_htlc(&cstate->a, rh); n = find_htlc(&cstate->a, rh);
if (n == tal_count(cstate->a.htlcs)) if (n == tal_count(cstate->a.htlcs))
errx(1, "Unknown R hash in %s", *argv); errx(1, "Unknown R hash in %s", *argv);
amount = cstate->a.htlcs[n]->amount; amount = cstate->a.htlcs[n]->amount_msat;
if (!funding_delta(o1, o2, oa, amount, -amount, if (!funding_delta(o1, o2, oa, amount, -amount,
&cstate->a, &cstate->b)) &cstate->a, &cstate->b))
errx(1, "Impossible htlc %llu %s", errx(1, "Impossible htlc %llu %s",
@ -222,7 +222,7 @@ struct channel_state *gather_updates(const tal_t *ctx,
n = find_htlc(&cstate->b, rh); n = find_htlc(&cstate->b, rh);
if (n == tal_count(cstate->b.htlcs)) if (n == tal_count(cstate->b.htlcs))
errx(1, "Unknown R hash in %s", *argv); errx(1, "Unknown R hash in %s", *argv);
amount = cstate->b.htlcs[n]->amount; amount = cstate->b.htlcs[n]->amount_msat;
if (!funding_delta(o2, o1, oa, amount, -amount, if (!funding_delta(o2, o1, oa, amount, -amount,
&cstate->b, &cstate->a)) &cstate->b, &cstate->a))
errx(1, "Impossible htlc %llu %s", errx(1, "Impossible htlc %llu %s",
@ -238,9 +238,9 @@ struct channel_state *gather_updates(const tal_t *ctx,
case PKT__PKT_UPDATE: case PKT__PKT_UPDATE:
if (received) if (received)
delta = -pkt->update->delta; delta = -pkt->update->delta_msat;
else else
delta = pkt->update->delta; delta = pkt->update->delta_msat;
if (!funding_delta(o1, o2, oa, delta, 0, if (!funding_delta(o1, o2, oa, delta, 0,
&cstate->a, &cstate->b)) &cstate->a, &cstate->b))
errx(1, "Impossible funding update %lli %s", errx(1, "Impossible funding update %lli %s",

6
test-cli/scripts/setup.sh

@ -24,10 +24,10 @@ done
scripts/generate-block.sh init scripts/generate-block.sh init
A1=`scripts/get-new-address.sh` A1=`scripts/get-new-address.sh`
TX=`$CLI sendmany "" "{ \"$A1\":10 }"` TX=`$CLI sendmany "" "{ \"$A1\":0.01 }"`
scripts/generate-block.sh scripts/generate-block.sh
# Find the inputs number corresponding to that 10 btc out # Find the inputs number corresponding to that 0.01 btc out
echo "Argument to test.sh:" echo "Argument to test.sh:"
for i in $(seq 1 $($CLI listunspent | grep -c txid) ); do scripts/getinput.sh $i | grep -q "$TX.*/1000000000/" && echo -n "$i "; done for i in $(seq 1 $($CLI listunspent | grep -c txid) ); do scripts/getinput.sh $i | grep -q "$TX.*/1000000/" && echo -n "$i "; done
echo echo

2
test-cli/scripts/test.sh

@ -53,7 +53,7 @@ A_INPUTNUM=$1
shift shift
#A_INPUTNUM=4 #A_INPUTNUM=4
#B_INPUTNUM=1 #B_INPUTNUM=1
A_AMOUNT=100000000 A_AMOUNT=900000
A_CHANGEADDR=`scripts/get-new-address.sh` A_CHANGEADDR=`scripts/get-new-address.sh`
A_TMPADDR=`scripts/get-new-address.sh` A_TMPADDR=`scripts/get-new-address.sh`

Loading…
Cancel
Save