fidelity bonds foundation: integration, client db#1820
Conversation
113df67 to
7c53fa0
Compare
7c53fa0 to
f4c0919
Compare
1c6aaf5 to
7843ab0
Compare
7843ab0 to
9610fb5
Compare
|
This PR is based on both #1818 and #1819, and it can be used to test those pieces with dexcctl e.g. |
9610fb5 to
c8e7231
Compare
| DEXPubKey Bytes `json:"pubkey"` | ||
|
|
||
| RegFees map[string]*FeeAsset `json:"regFees"` | ||
| BondExpiry uint64 `json:"bondExpiry"` |
There was a problem hiding this comment.
Does this need to be specified. We have the dex package consts.
There was a problem hiding this comment.
Please see new comment here: #1819 (comment)
EDIT: wow this reply was part of a pending review for several days. GitHub can be so irritating.
| newRefundTx, err := wallet.RefundBond(ctx, bond.Version, bond.CoinID, bond.Script, bond.Amount, priv) | ||
| bondAlreadySpent := errors.Is(err, asset.CoinNotFoundError) // or never mined! | ||
| if err != nil && !bondAlreadySpent { | ||
| c.log.Errorf("Failed to generate bond refund tx: %v", err) | ||
| continue | ||
| } | ||
|
|
||
| // If the user hasn't already manually refunded the bond, broadcast | ||
| // the refund txn. Mark it refunded and stop tracking regardless. | ||
| if bondAlreadySpent { | ||
| c.log.Warnf("Bond output not found, possibly already spent or never mined! "+ | ||
| "Marking refunded. Backup refund transaction: %x", bond.RefundTx) | ||
| } else { | ||
| refundCoinID, err := wallet.SendTransaction(newRefundTx) |
There was a problem hiding this comment.
It's not clear to me what is the benefit of doing this in two steps instead of one.
RefundBond(...) (coinID string, err error)There was a problem hiding this comment.
It can be, but I was after a method that can prepare (as in advance of lock expiry) the bond transaction. The method is more versatile this way when you consider command line tools, recovery actions, bond export/backups etc. Not a big deal to change this later if needed though; it's just generally more useful this way, plus it is a bit more consistent with the mode of operation of the bond creation methods.
| if coinNotFound { | ||
| // Broadcast the bond and start waiting for confs. | ||
| c.log.Infof("Broadcasting bond %v (%s), script = %x.\n\n"+ | ||
| "BACKUP refund tx paying to current wallet: %x\n\n", | ||
| coinIDStr, unbip(bond.AssetID), bond.BondData, bond.RedeemTx) | ||
| if _, err = wallet.SendTransaction(bond.SignedTx); err != nil { | ||
| c.log.Warnf("Failed to broadcast bond txn: %v") |
There was a problem hiding this comment.
Feels funny to use this as the indicator for whether to broadcast the bond. Lemme see if I've got the reasoning. If 1) this is the first time we're broadcasting, this should obviously work. If 2) we are picking up monitoring on a transaction that had already been broadcast, but has not been picked up by the server's node, then a) If we're using a full node, we should get an error, which will be logged as just a warning. If b) we're using an SPV node, there will be no error or log. In the end, the result is the same, worst that can happen is a warning. Sound right?
There was a problem hiding this comment.
Yah the reasoning is right, but fair point that this feels funny to start this off with a CoinNotFound -> YOLO broadcast. Since the broadcast should have been done in (c *Core) PostBond first, the actions here shouldn't be necessary except for weird edges like clearing full node mempool, spv wallet recovery (wiping data files), etc.
I suspect it would be alright without this though. Will revisit to see if I had other reasoning that I failed to doc.
Thanks. It was ready for review previously, I just have it back in draft because I haven't re-tested. Very happy to have reviews, but won't be surprised if something small is broken and it doesn't work like it did before. The two prior PRs were tested using this PR to actually use bonds to get registered on a server. |
c8e7231 to
4b8e7f6
Compare
|
Only rebased, no substantive changes. |
4b8e7f6 to
30db927
Compare
|
Just rebased and added some fixup commits, but not ready for re-review. Need to split the postbond route as per #1819 (comment) |
8fbcd9e to
47d86bc
Compare
| // Still pending on server. Start waiting for confs. | ||
| c.log.Debugf("Preparing to post pending bond %v (%s).", bondIDStr, symb) | ||
| c.monitorBondConfs(dc, assetBond(bond), bondAsset.Confs) |
There was a problem hiding this comment.
Tested this case by forcing a server side error on initial bond submission, then on reconnect, retry and success:
[INF] CORE: Authenticated connection to 127.0.0.1:17273, acct 9f8d5cbea78335c1c3c789ef1b08c193468adddc6a892e006433bd4ca82e5cb2, 0 active bonds, 0 active orders, 0 active matches, score -1, tier
[DBG] CORE: Starting coin waiter for pending bond ca9a6c08dc4c973a0524b32a05fc9bf9b28182bed39223508aef190b18d229e3:0 (dcr), (known to server = false)
[DBG] CORE: Preparing to post pending bond ca9a6c08dc4c973a0524b32a05fc9bf9b28182bed39223508aef190b18d229e3:0 (dcr).
[DBG] CORE: Subscribing to the dcr_btc order book for 127.0.0.1:17273
[DBG] CORE[dcr_btc][book]: Processing 0 cached order notes
[INF] CORE: Bond confirmed ca9a6c08dc4c973a0524b32a05fc9bf9b28182bed39223508aef190b18d229e3:0 (dcr) with expire time of 2022-11-15 10:54:09 -0600 CST
[INF] CORE: Bond ca9a6c08dc4c973a0524b32a05fc9bf9b28182bed39223508aef190b18d229e3:0 (dcr) confirmed.
[INF] CORE: notify: |SUCCESS| (bondpost) BondConfirmed - New tier = 2.
|
|
||
| reqConfs := bondAsset.Confs | ||
| bondCoinStr := coinIDString(bond.AssetID, bond.CoinID) | ||
| c.log.Infof("DEX %v has validated our bond %v (%s) with strength %d. %d confirmations required to trade.", |
There was a problem hiding this comment.
2022-11-15 10:50:09.795 [INF] CORE: DEX 127.0.0.1:17273 has validated our bond ca9a6c08dc4c973a0524b32a05fc9bf9b28182bed39223508aef190b18d229e3:0 (dcr) with strength 1. 1 confirmations required to trade.
2022-11-15 10:50:09.796 [INF] CORE: Broadcasting bond ca9a6c08dc4c973a0524b32a05fc9bf9b28182bed39223508aef190b18d229e3:0 (dcr) with lock time 2022-11-15 10:58:09 -0600 CST, data = 0421c57363b17576a914fc39ce555e3be56aca916c7f90d64ca0d2ecc9cd88ac.
BACKUP refund tx paying to current wallet: 0100000001e329d2180b19ef8a502392d3be8281b2f99bfc052ab324053a974cdc086c9aca0000000000feffffff0164b69a3b0000000000001976a914ad75136d6b4be5c97a2121028926db02414e3f3988ac21c57363000000000100ca9a3b0000000000000000ffffffff8c483045022100dfbf624943905b66aa86c74a2920ab6e0a66430599e278575798b63e49547d7f02204277efa7ae463af39a541078a4b38981c1e26bdf2b8dc989beddd6bf1fdd8868012102a9dbec0914201023d67974b8b6b225673cd7cb9e186e16e30936692980f56d70200421c57363b17576a914fc39ce555e3be56aca916c7f90d64ca0d2ecc9cd88ac
2022-11-15 10:50:09.800 [TRC] CORE: notify: |DATA| (balance) balance updated
2022-11-15 10:50:09.801 [INF] CORE: notify: |SUCCESS| (bondpost) BondConfirming - Waiting for 1 confirmations to post bond ca9a6c08dc4c973a0524b32a05fc9bf9b28182bed39223508aef190b18d229e3:0 (dcr) to 127.0.0.1:17273
| c.log.Errorf("Failed to broadcast bond refund txn %v: %v", newRefundTx, err) | ||
| continue | ||
| } | ||
| c.log.Infof("Bond %v refunded in %v (%s)", bondIDStr, coinIDString(assetID, refundCoinID), unbip(bond.AssetID)) |
There was a problem hiding this comment.
[INF] CORE: Bond ca9a6c08dc4c973a0524b32a05fc9bf9b28182bed39223508aef190b18d229e3:0 (dcr) refunded in 0c21facb520a6e9ae79e81dbd9860a9c7e958b951cb12947e93a96b4c2ef07a3:0 (dcr)
Note that refundExpiredBond would be adapted to "bond maintenance" by renewing bonds directly instead of refunding.
| redemptionReservesKey = []byte("redemptionReservesKey") | ||
| refundReservesKey = []byte("refundReservesKey") | ||
| byteTrue = encode.ByteTrue | ||
| backupDir = "backup" | ||
| disabledRateSourceKey = []byte("disabledRateSources") | ||
| walletDisabledKey = []byte("walletDisabled") | ||
| programKey = []byte("program") |
There was a problem hiding this comment.
Sorry for bloating the diff here, but we had bucket keys, value keys, and values all mixed up in this var block.
47d86bc to
f932748
Compare
5647f97 to
86a584f
Compare
martonp
left a comment
There was a problem hiding this comment.
An issue I found:
If I initialize a new dexc, then post a bond I get no error. If I post a second bond, I will get this error:
failed to store account 6789063d3271c851419b0d7d6ddbd1d6392e5f61732c7bd0125013eea9997852 for dex 127.0.0.1:17273: failed to create account bucket
If I restart dexc, I can keep posting as many bonds as I want.
| return nil, newError(bondTimeErr, "lock time of %d has already passed the server's expiry time of %v (bond expiry %d)", | ||
| form.LockTime, expireTime, bondExpiry) | ||
| } | ||
| if lockTime.Add(-time.Minute).Before(expireTime) { |
There was a problem hiding this comment.
Could use Sub instead of Add. Also, I think an hour gap would be better.. is a minute even enough to do a trade?
There was a problem hiding this comment.
Could use
Subinstead ofAdd. Also, I think an hour gap would be better.. is a minute even enough to do a trade?
An hour sounds good.
(Time).Add(Duration) Time takes a duration and gets a Time so I can use Before to be expressive. But (Time).Sub(Time) Duration compares the two Times and then I'd have to do < or > with the Durations. Docs for Sub say // To compute t-d for a duration d, use t.Add(-d)., so even though adding a negative is odd, I prefer to return a Time so I can use Before.
There was a problem hiding this comment.
Oh, I couldn't just switch to an hour because the times are meant to be fairly fast on simnet. I'll make some network dependent limits shortly.
There was a problem hiding this comment.
Ah yeah, better to leave it short on simnet.
Good catch! Will fix and update tests for this. Thanks for reviewing and testing! |
86a584f to
075c282
Compare
martonp
left a comment
There was a problem hiding this comment.
Working well, no issues while testing.
|
|
||
| // AccountExport is used to retrieve account by host for export. | ||
| func (c *Core) AccountExport(pw []byte, host string) (*Account, error) { | ||
| func (c *Core) AccountExport(pw []byte, host string) (*Account, []*db.Bond, error) { |
There was a problem hiding this comment.
Why not stick the bonds into Account?
| // EXPERIMENT: Server knows of a bond we do not! Store what we can for | ||
| // tier accounting, but we can't redeem it. We'll make en entry in | ||
| // dc.acct.bonds for tier accounting, but this may not work out. i.e. | ||
| // What handling in the bond monitor / refund loop do we need to discard | ||
| // these after expiry? |
There was a problem hiding this comment.
Shouldn't we make an attempt to find the keys that created this?
If it cannot be found, it could be removed when a tier change note or an expired bond note arrives.
There was a problem hiding this comment.
Shouldn't we make an attempt to find the keys that created this?
We can search our DB, but they should have already been loaded into localBondMap, otherwise it would have already been redeemed and we don't need the keys, so this whole case feels like server spamming.
I think I just wanted to have it in our active bonds slice so we can locally match the bond-based tier that the server seems to have.
If it cannot be found, it could be removed when a tier change note or an expired bond note arrives.
That's a good plan. Just have the bond monitor / refund loop ignore anything without a PrivKey or a RefundTx, and let them stay until server says they are not longer active.
There was a problem hiding this comment.
I meant to search using RefundBondByCoinID once it exists. But yes I think it's good to have in the active bonds slice.
075c282 to
3de34ca
Compare
|
Fix-ups just pushed are not yet tested but address review items. |
3de34ca to
fc77e85
Compare
|
Rebased, squashed, and re-tested with both account creation with bond and adding bond to legacy accounts. Existing legacy account New account Re-verified automatic refunds, and tier adjustment. Next steps will be multifaceted. UI, updated messaging (suspended -> sub-tier or whatever), client bond maintenance, settings, switches, etc. I will begin client db and core work for maintaining a certain tier with limits on total bonding amount. |


Further breaking up #1480 for feasible review, this piece builds on both #1818 and #1819, and deals with:
These are the commits beginning with
client/db: storing, confirming, and refunding bonds.To make this transition smoother, the client and server retain all the legacy registration fee machinery, and the client's continues to use the legacy registration system.
For follow-up work
Auto add bond in
client.Core. There are comments in the code regarding an option to automatically postbond when previous bonds are about to expire on DEX. There are questions pertaining to timing, UI, and UX. Presently this PR requires using dexcctl to call thepostbondRPC (or a direct http API call to the endpoint of the same name, which is created for said UI plans).As suggested by @buck54321 on the prerequisite PRs, an
asset.RenewBondmethod could be devised for said bond maintenance code to do teh refund and post of a new bond in a single txn.See the project board at DCRDEX Fidelity Bonds.