-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is still draft, but I was browsing and made some notes. Conceptually, this looks good. I didn't dig too deeply in the DB bits, but the interface looks about right, I think.
dex/msgjson/types.go
Outdated
|
||
RegFees map[string]*FeeAsset `json:"regFees"` | ||
BondExpiry uint64 `json:"bondExpiry"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be specified. We have the dex package const
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
client/core/bond.go
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
client/core/bond.go
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use
Sub
instead 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 Time
s and then I'd have to do <
or >
with the Duration
s. 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Working well, no issues while testing.
@@ -67,39 +68,37 @@ func (c *Core) AccountDisable(pw []byte, addr string) error { | |||
} | |||
|
|||
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not stick the bonds into Account
?
client/core/core.go
Outdated
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
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 thepostbond
RPC (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.RenewBond
method 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 https://github.com/orgs/decred/projects/2.