Skip to content

dex/order,client/asset: fix UserMatch DB serialization, and change address assumption#531

Merged
chappjc merged 6 commits into
decred:masterfrom
chappjc:btc-fee-cycles
Jul 16, 2020
Merged

dex/order,client/asset: fix UserMatch DB serialization, and change address assumption#531
chappjc merged 6 commits into
decred:masterfrom
chappjc:btc-fee-cycles

Conversation

@chappjc
Copy link
Copy Markdown
Member

@chappjc chappjc commented Jul 6, 2020

Resolves #529

The order.UserMatch was not being serialized with the new FeeRateSwap field so it was being loaded from DB as 0 by default.

In addition, the 'connect' response was not setting either base or quote fee rates, but it was also missing the recently added MatchTime that the client needs for lock time computation.

The server/db changes provide the data needed by (*AuthManager).handleConnect to provide this new match data to clients on connect.

In client/asset/{dcr,btc}, (*ExchangeWallet).sendWithReturn was assuming a certain change address format when computing the updated tx size. This fixes that and refactors a bit so the functions mirror each other more closely, and to log more information about the generated transaction and its fees.

EDIT: Another 'connect' bug was that only one msgjson.Match was being sent if the user was on both sides of the match. This is fixed to send two matches now as is done when the matches are initially made. Even with this though the client will fail to act if dexc is restart in TakerSwapCast status.. Finally, the dex/encode package is updated to load empty data pushes as nil instead is a zero length non-nil slice.

Comment thread client/core/trade.go Outdated
Comment thread client/asset/btc/btc.go
Copy link
Copy Markdown
Member

@buck54321 buck54321 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some draft comments. Sorry if I commented on things that were not meant to be permanent.

Comment thread client/core/trade.go Outdated
Comment thread client/asset/btc/btc.go Outdated
Comment thread client/asset/btc/btc.go Outdated
Comment thread client/asset/interface.go Outdated
@chappjc chappjc changed the title client/asset/btc: tweak and log sendWithReturn dex/order,client/asset: fix UserMatch DB serialization, and change address assumption Jul 8, 2020
@chappjc chappjc marked this pull request as ready for review July 8, 2020 14:10
@chappjc chappjc marked this pull request as draft July 8, 2020 15:11
@chappjc

This comment has been minimized.

Comment thread server/auth/auth.go
Comment on lines +781 to +810
ServerTime: encode.UnixMilliU(match.Epoch.End()),
Address: addr,
FeeRateBase: match.BaseRate, // contract txn fee rate if user is selling
FeeRateQuote: match.QuoteRate, // contract txn fee rate if user is buying
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ServerTime (used for client's lock time calculations) was also unset.

Comment thread server/db/driver/pg/matches.go Outdated
Comment thread server/db/driver/pg/matches.go Outdated
var takerSell sql.NullBool
var takerAddr, makerAddr sql.NullString
err := rows.Scan(&m.ID, &takerSell, &m.Active,
err := rows.Scan(&m.ID, &m.Active, &takerSell,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These booleans were switched. Caught by the AllActiveUserMatches test that now uses this function.

@chappjc chappjc marked this pull request as ready for review July 8, 2020 18:05
Copy link
Copy Markdown
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this pr, but I am able to get "stuck" and unable to trade sometimes:

[joe@hyrule dexcctl]$ ./dexcctl -ua -Pbb trade 127.0.0.1:7232 true true 42 0 100000000 1000000 false
App password:
unable to trade: rpc error: 35: coin {txid = 084558ca27a30318de6840134c2a2cd513cc37e8f82b9f9c03431edf10c52eb5, vout = 2} is locked

At one point hit this error:

2020-07-09 11:15:33.062 [ERR] CORE: notify: |ERROR| (order) Swap error - Error encountered sending a swap output(s) worth 0.22000000 btc on order 3087a710 - Order: 3087a7104de06fd894629dc27fcd0aa620fd714b53290444680dec8e2b73c610
2020-07-09 11:15:33.062 [ERR] CORE: 127.0.0.1:7232 tick: swapMatches order 3087a7104de06fd894629dc27fcd0aa620fd714b53290444680dec8e2b73c610 - btc coin 0000000000000000000000000000000000000000000000000000000000000000:0 not found

and

2020-07-09 11:31:00.058 [DBG] CORE: swappable match c5d2519843dc145d2bad19a95c13c07303719b5c14e860f4855c3f7e56f089b9
2020-07-09 11:31:00.073 [DBG] CORE[btc]: Change output size = 31, addr = bcrt1qhggzepsnake4kux8v7vgpgs22l8vkqd76z389y
2020-07-09 11:31:00.075 [DBG] CORE[btc]: 2 signature cycles to converge on fees for tx 67301cc74bd11c0dd20b672205c8324269d0658b07413f6b9b94c8d635c54ee7: min rate = 4, actual fee rate = 4 (892 for 223 bytes), change = true
2020-07-09 11:31:00.077 [ERR] CORE: notify: |ERROR| (order) Swap error - Error encountered sending a swap output(s) worth 2.00000000 btc on order ce80fe12 - Order: ce80fe12ffcf7bf28aea48a7ddcb501af6723e8c32a920f184145c865f2e3ef6
2020-07-09 11:31:00.077 [ERR] CORE: 127.0.0.1:7232 tick: swapMatches order ce80fe12ffcf7bf28aea48a7ddcb501af6723e8c32a920f184145c865f2e3ef6 - error sending swap transaction: -26: txn-mempool-conflict (code 18)

I am still experiencing #528 but #529 is fixed.

Comment thread client/core/trade.go Outdated
if dbMatch.Side == order.Taker && metaData.Status == order.MakerRedeemed {
return true
}
log.Debugf("match side = %v, status = %v", dbMatch.Side, metaData.Status)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of loud when status = MatchComplete

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. These should be remove from the tracked trades. It's part of why I'm always pushing for more logging, because otherwise things like this can be neglected.

Comment thread client/core/trade.go Outdated
log.Debugf("refundable match %v", match.id)
refunds = append(refunds, match)
default:
log.Debugf("match not ready for action: %v", match.id)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this doesn't need to log if status = MatchComplete?

Copy link
Copy Markdown
Member Author

@chappjc chappjc Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such a match should be removed, but that's out of scope for this PR.

@chappjc
Copy link
Copy Markdown
Member Author

chappjc commented Jul 9, 2020

Regarding the coin is locked errors, @buck54321 is assessing that in #525 but it's not quite ready.

Would like repro details for the BTC 0000000000000000000000000000000000000000000000000000000000000000:0 not found error if you can.

@JoeGruffins
Copy link
Copy Markdown
Member

Adding one more error:
client

2020-07-09 12:12:35.010 [ERR] CORE: match proof csum mismatch for epoch 159426434: expected 6714faa9a07b42ece9b9d7c0377603dca7ace758c86e083526cc7aa4ab1a60bf, got b3bb2616324c251c9533a113e5036b4434bde310ac6a0c6d354cce7307bab254

server

2020-07-09 12:12:00.075 [INF] AUTH: new user registered
2020-07-09 12:12:00.083 [DBG] AUTH: User bdc918e65d7cd4741a6f4ca6fb2ec15c6f420225a1f872f46ed27c00148626a9 connected from 127.0.0.1 with 0 pending requests and 0 pending responses/notifications.
2020-07-09 12:12:26.263 [TRC] MKT: Received order 809c574b2b023c1af15747076b36257a98c662ede87078210d98edaf0529e0bc at 2020-07-09 03:12:26.263 +0000 UTC
2020-07-09 12:12:29.997 [TRC] MKT: Received order ee9edc27dbe73fe72f702e523276774e8be1aae9c19bc8c2bb3deddde53518e8 at 2020-07-09 03:12:29.997 +0000 UTC
2020-07-09 12:12:30.001 [TRC] MKT: Preimage request sent for order 809c574b2b023c1af15747076b36257a98c662ede87078210d98edaf0529e0bc
2020-07-09 12:12:30.001 [TRC] MKT: Preimage request sent for order ee9edc27dbe73fe72f702e523276774e8be1aae9c19bc8c2bb3deddde53518e8
2020-07-09 12:12:30.002 [TRC] MKT: Good preimage received for order 809c574b2b023c1af15747076b36257a98c662ede87078210d98edaf0529e0bc: 86f33a54653063487ca6ca8794a818271db91d837a7b827faad11defc23a9f03
2020-07-09 12:12:32.486 [TRC] MKT: Received order a3509ffaf237022539972f4dd7fe5f43b1657d3b315c13594ee769c72fac0a56 at 2020-07-09 03:12:32.486 +0000 UTC
2020-07-09 12:12:35.001 [INF] MKT: Collected 1 valid order preimages, missed 1. Commit checksum: 6714faa9a07b42ece9b9d7c0377603dca7ace758c86e083526cc7aa4ab1a60bf
2020-07-09 12:12:35.001 [INF] MKT: No preimage received for order ee9edc27dbe73fe72f702e523276774e8be1aae9c19bc8c2bb3deddde53518e8 from user bdc918e65d7cd4741a6f4ca6fb2ec15c6f420225a1f872f46ed27c00148626a9. Penalizing user and revoking order.
2020-07-09 12:12:35.002 [DBG] AUTH: user bdc918e65d7cd4741a6f4ca6fb2ec15c6f420225a1f872f46ed27c00148626a9 penalized for rule 4
2020-07-09 12:12:35.008 [DBG] AUTH: Recorded order 9f5c6941ab8f10254d1cbb947f594dde70d50d7f403b975e756f9769b54c855b that has finished processing: user=bdc918e65d7cd4741a6f4ca6fb2ec15c6f420225a1f872f46ed27c00148626a9, time=1594264355004, target=ee9edc27dbe73fe72f702e523276774e8be1aae9c19bc8c2bb3deddde53518e8
2020-07-09 12:12:35.008 [INF] MKT: Matching complete for market dcr_btc epoch 159426434: 0 matches (0 partial fills), 0 completed OK (not booked), 1 booked, 0 unbooked, 0 failed

@chappjc
Copy link
Copy Markdown
Member Author

chappjc commented Jul 9, 2020

The double spend error (- error sending swap transaction: -26: txn-mempool-conflict (code 18)) is almost certainly unlocked swap change from a partially filled book order. That's another one that will be resolved in #525

The btc coin 0000000000000000000000000000000000000000000000000000000000000000:0 not found error might also be related to the change handling,

dcrdex/client/core/trade.go

Lines 701 to 711 in f3e94af

coinIDs := t.Trade().Coins
if len(t.metaData.ChangeCoin) > 0 {
coinIDs = []order.CoinID{t.metaData.ChangeCoin}
}
swaps.Inputs = make([]asset.Coin, 0, len(coinIDs))
for _, coinID := range coinIDs {
coin, found := t.coins[hex.EncodeToString(coinID)]
if !found {
fromID := t.wallets.fromAsset.ID
return errs.add("%s coin %s not found", unbip(fromID), coinIDString(fromID, coinID))
, but it's still more unexpected. Might be a race with chained swaps initiating simultaneously since swap change is stored in t.change and t.coins in this function.

The No preimage received error is very much unexpected. Please add to the client logs for the preimage issue, specifically around the time of the server request and client response.

@itswisdomagain
Copy link
Copy Markdown
Member

Regarding the coin is locked errors, @buck54321 is assessing that in #525 but it's not quite ready.

Aren't those coin is locked errors from the server? The user does not specify the coins to use, so if client picks a locked coin, client must think the coin isn't locked, only for the server to say otherwise. I bring this up because #525 doesn't appear to touch coin unlocking on the server.

@chappjc
Copy link
Copy Markdown
Member Author

chappjc commented Jul 13, 2020

Aren't those coin is locked errors from the server? The user does not specify the coins to use, so if client picks a locked coin, client must think the coin isn't locked, only for the server to say otherwise. I bring this up because #525 doesn't appear to touch coin unlocking on the server.

Yes, that's from the server. We know that the client is not locking and unlocking as needed, yet, so the server may be correct here. There may be coin locking/unlocking issues to resolve on the server, but if the client tries to use coins that it should have locked, but which the server had correctly locked, this error from the server makes sense.

@chappjc
Copy link
Copy Markdown
Member Author

chappjc commented Jul 13, 2020

Adding one more error:
client

2020-07-09 12:12:35.010 [ERR] CORE: match proof csum mismatch for epoch 159426434: expected 6714faa9a07b42ece9b9d7c0377603dca7ace758c86e083526cc7aa4ab1a60bf, got b3bb2616324c251c9533a113e5036b4434bde310ac6a0c6d354cce7307bab254

This is #446. Resolved in #541

I have no idea about the preimage error. Need more context and repro info. EDIT: The preimage error seems to be a client/core.(*Core).Trade sequence issue. buck is investigating has a resolution in PR #525

Copy link
Copy Markdown
Member

@buck54321 buck54321 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvements. More logging is nice.

Comment thread client/asset/dcr/dcr.go
Comment on lines +1586 to +1587
if checkRate > feeRate*3 {
return nil, nil, fmt.Errorf("final fee rate for %s, %d, is seemingly outrageous, target = %d",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did someone see an error when it was 2?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but I think before it was comparing rate with total fee (was checkRate > float64(feeRate*uint64(size)*2, with the size multiplied there) so it's hard to know if double rate will sometimes be too strict especially if the rate is very low to begin with. I was actually thinking about removing this though since btc doesn't do the same check and above it also verifies checkFee != lastFee.

Comment thread client/core/core.go Outdated
Comment on lines +1888 to +1890
// for _, match := range matches {
// match.tracker.tick()
// }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can delete?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was considering ticking the matches now rather than waiting for the ticker in listen. Just removing the comment though as i don't want to muddle the responsibilities for doing this.

Comment thread client/core/trade.go Outdated
Comment thread client/core/core.go Outdated
Comment thread dex/encode/encode.go
Comment thread server/auth/auth.go Outdated
Comment thread server/db/driver/pg/matches.go Outdated

func userMatches(ctx context.Context, dbe *sql.DB, tableName string, aid account.AccountID) ([]*db.MatchData, error) {
func userMatches(ctx context.Context, dbe *sql.DB, tableName string, aid account.AccountID, includeInactive bool) ([]*db.MatchData, error) {
stmt := fmt.Sprintf(internal.RetrieveUserMatches, tableName)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you switch on includeInactive to use internal.RetrieveActiveUserMatches here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Changed: 3e4946e

Comment thread client/db/bolt/db.go Outdated
Status: order.OrderStatus(intCoder.Uint16(oBkt.Get(statusKey))),
Host: string(oBkt.Get(dexKey)),
ChangeCoin: oBkt.Get(changeKey),
ChangeCoin: oBkt.Get(changeKey), // what if it was stored as nil?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Should be storing nil interface. Should also favor len(change) == 0 over nil check throughout.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since ChangeCoin is a order.CoinID, which is a []byte, I guess I'm pointing out here that oBkt.Get(changeKey) may or may not return nil vs []byte{}... just need to check and see since I'm not that familiar with bbolt. Maybe this is just fine and ChangeCoin will come back nil... dunno.

Copy link
Copy Markdown
Member Author

@chappjc chappjc Jul 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just confirmed that putting nil comes back as []byte{} instead. 84db38c The len(change) == 0 checks in higher level code is also a good idea that I'll get on as well. EDIT: Actually, the checks for ChangeCoin do seem to be on length rather than nil, unlike with RefundCoin.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Yep, bbolt will return nil for nil and []byte{} for []byte{}.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed a commit to do length checks in favor of nil checks anyway. e2e6908

But I observed that bolt returns []byte{} always, even when nil is stored. That is, in 84db38c, if you get rid of that if len(changeCoin) == 0 I added in decodeOrderBucket, it would never set the returned dexdb.OrderMetaData.ChangeCoin to nil even if you've stored it that way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. My test revealed otherwise.

func TestThis(t *testing.T) {
	boltdb := newTestDB(t)
	bktID := []byte("abc")
	k := []byte("k")
	err := boltdb.Update(func(tx *bbolt.Tx) error {
		bkt, err := tx.CreateBucketIfNotExists(bktID)
		if err != nil {
			return err
		}
		err = bkt.Put(k, []byte{})
		if err != nil {
			return err
		}
		b := bkt.Get(k)
		fmt.Println("[]byte{} is nil?", b == nil)
		err = bkt.Put(k, nil)
		if err != nil {
			return err
		}
		b = bkt.Get(k)
		fmt.Println("nil is nil?", b == nil)
		return nil
	})
	if err != nil {
		t.Fatalf("Update error: %v", err)
	}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, not good. That means (*BoltDB).Get is broken for nil.

But I am not seeing why the bkt.Get from decodeOrderBucket will not return nil, only []byte{}.

Copy link
Copy Markdown
Member Author

@chappjc chappjc Jul 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, actually we seemed to have stumbled on some undefined behavior in bolt. For a stored nil slice, you will receive either an empty non-nil slice or a nil slice, depending on whether the Cursor retrieves the value from the "page" or "node", respectively.

https://github.com/etcd-io/bbolt/blob/232d8fc87f50244f9c808f4745759e08a304c029/cursor.go#L334-L351

In the "retrieve value from page" branch, it uses (*leafPageElement).value > unsafeByteSlice to get the value byte slice, which always results in a non-nil slice:

https://github.com/etcd-io/bbolt/blob/232d8fc87f50244f9c808f4745759e08a304c029/unsafe.go#L27

This is all it can do because a leafPageElement only knows the "size" and 0 does not disambiguate nil from empty non-nil slice. https://github.com/etcd-io/bbolt/blob/232d8fc87f50244f9c808f4745759e08a304c029/page.go#L115-L120

I don't see a fix for bolt, other than possibly to revise their use of inode to always return []byte{} when len(inode.value) is zero rather than returning whatever was stored there (nil or []byte{}).

I'll make a minimal repro to demonstrate, but it seems simple to hit with enough entries in the same bucket by committing the update txn.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With orders:

func TestOrderChange(t *testing.T) {
	boltdb := newTestDB(t)
	// Create an account to use.
	acct := dbtest.RandomAccountInfo()
	err := boltdb.CreateAccount(acct)
	if err != nil {
		t.Fatalf("CreateAccount error: %v", err)
	}

	ord := randOrderForMarket(randU32(), randU32())
	mordIn := &db.MetaOrder{
		MetaData: &db.OrderMetaData{
			Status: 3,
			Host:   acct.Host,
			Proof:  db.OrderProof{DEXSig: randBytes(73)},
		},
		Order: ord,
	}

	fmt.Printf("%#v\n", mordIn.MetaData.ChangeCoin)
	fmt.Printf("%#v\n", mordIn.MetaData.Proof.Preimage)

	err = boltdb.UpdateOrder(mordIn)
	if err != nil {
		t.Fatalf("error inserting order: %v", err)
	}

	mord, err := boltdb.Order(ord.ID())
	if err != nil {
		t.Fatalf("unable to retrieve order by id")
	}
	fmt.Printf("%#v\n", mord.MetaData.ChangeCoin)     // incorrect
	fmt.Printf("%#v\n", mord.MetaData.Proof.Preimage) // correct
	if mord.MetaData.ChangeCoin != nil {
		t.Errorf("ChangeCoin was not nil: %#v", mord.MetaData.ChangeCoin)
	}
}

Just plain bolt:

func TestNilValue(t *testing.T) {
	tDir, _ := ioutil.TempDir("", "dbtest")
	defer os.RemoveAll(tDir)
	tDbFile := filepath.Join(tDir, "bolt.db")
	boltdb, err := bbolt.Open(tDbFile, 0600, nil)
	if err != nil {
		t.Fatal(err)
	}

	bktID := []byte("bucket")
	nk := []byte("nilkey")
	ek := []byte("emptykey")

	err = boltdb.Update(func(tx *bbolt.Tx) error {
		bkt, err := tx.CreateBucketIfNotExists(bktID)
		if err != nil {
			return err
		}

		err = bkt.Put(nk, nil)
		if err != nil {
			return err
		}

		// (*Cursor).keyValue retrieves value from node, returning the nil byte
		// slice from inode.value.
		b := bkt.Get(nk)
		if b != nil {
			return fmt.Errorf("not nil, %#v", b)
		}

		err = bkt.Put(ek, []byte{})
		if err == nil {
			return err
		}

		b = bkt.Get(ek)
		if b == nil {
			return fmt.Errorf("not []byte{}, %#v", b)
		}

		return nil
	})
	// Commit "spills" to pages.
	if err != nil {
		t.Fatalf("Update error: %v", err)
	}

	err = boltdb.View(func(tx *bbolt.Tx) error {
		bkt := tx.Bucket(bktID)
		// (*Cursor).keyValue retrieves value from page via unsafeByteSlice,
		// always creating a non-nil byte slice.
		b := bkt.Get(nk)
		if b != nil {
			// Fails here.
			t.Errorf("not nil, %#v", b)
		}
		b = bkt.Get(ek)
		if b == nil {
			// OK here.
			t.Errorf("not []byte{}, %#v", b)
		}
		return nil
	})
	if err != nil {
		t.Fatalf("View error: %v", err)
	}
}

Comment thread client/core/trade.go
Comment on lines -731 to +763
t.change = change
t.coins[hex.EncodeToString(change.ID())] = change
t.metaData.ChangeCoin = []byte(change.ID())
t.db.SetChangeCoin(t.ID(), t.metaData.ChangeCoin) // TODO: lock the change coin in the wallet if the order is still on the book
if change != nil {
log.Debugf("storing change coin %v", change.String())
t.change = change
t.coins[hex.EncodeToString(change.ID())] = change
t.metaData.ChangeCoin = []byte(change.ID())
t.db.SetChangeCoin(t.ID(), t.metaData.ChangeCoin) // TODO: lock the change coin in the wallet if the order is still on the book
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd still want to save the change coin if it's nil, no? We don't want to append it to t.coins though.

Copy link
Copy Markdown
Member Author

@chappjc chappjc Jul 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah t.change = nil would still be desirable, but change.ID() would of course panic with t.metaData.ChangeCoin = []byte(change.ID()). Probably OK to handle this with an else with t.metaData.ChangeCoin = nil. EDIT: like 0144938

BTW, this gets to the other comment about ChangeCoin: oBkt.Get(changeKey) where I'm not certain that oBkt.Put(nil) comes back as nil. Just need to check that.

Comment thread server/auth/auth.go Outdated
Comment thread server/auth/auth.go Outdated
Comment thread server/auth/auth_test.go Outdated
Comment thread client/core/trade.go
@JoeGruffins
Copy link
Copy Markdown
Member

Please add to the client logs for the preimage issue, specifically around the time of the server request and client response.

client log
2020-07-09 12:12:00.082 [INF] CORE: notify: |SUCCESS| (feepayment) Account registered - You may now trade at 127.0.0.1:7232
2020-07-09 12:12:08.563 [DBG] RPC: authenticated user with ip: 127.0.0.1:59078
2020-07-09 12:12:08.563 [TRC] CORE: wallet status requested for unknown asset 0 -> btc
2020-07-09 12:12:08.588 [INF] CORE[btc]: Setting up new btc wallet at localhost:20556/wallet/gamma.
2020-07-09 12:12:08.698 [TRC] CORE: notify: |DATA| (balance) balance updated
2020-07-09 12:12:08.698 [INF] CORE: Created btc wallet. Account "gamma" balance available = 8399878512 / locked = 0, Deposit address = mwGWxhiP2FfoB3gaTPv8wpsCH746Nge6Wy
2020-07-09 12:12:08.859 [TRC] CORE: notify: |DATA| (balance) balance updated
2020-07-09 12:12:08.859 [INF] CORE: Connected to and unlocked btc wallet. Account "gamma" balance available = 8399878512 / locked = 0, Deposit address = mwGWxhiP2FfoB3gaTPv8wpsCH746Nge6Wy
2020-07-09 12:12:10.847 [DBG] CORE: ticking 0 trades
2020-07-09 12:12:12.617 [TRC] WEB: Disconnected websocket client 127.0.0.1
2020-07-09 12:12:12.739 [DBG] WEB: New websocket client 127.0.0.1
2020-07-09 12:12:14.619 [INF] CORE: Loaded 0 active orders.
2020-07-09 12:12:14.619 [INF] CORE: Loaded 0 active matches.
2020-07-09 12:12:14.662 [TRC] WEB: message of type 1 received for route loadmarket
2020-07-09 12:12:17.918 [TRC] WEB: Disconnected websocket client 127.0.0.1
2020-07-09 12:12:18.015 [DBG] WEB: New websocket client 127.0.0.1
2020-07-09 12:12:19.231 [INF] CORE: Loaded 0 active orders.
2020-07-09 12:12:19.231 [INF] CORE: Loaded 0 active matches.
2020-07-09 12:12:19.273 [TRC] WEB: message of type 1 received for route loadmarket
2020-07-09 12:12:20.002 [TRC] CORE: notify: |DATA| (epoch) - Index: 159426434
2020-07-09 12:12:26.269 [DBG] CORE: notify: |POKE| (order) Order placed - selling 1.00000000 dcr (809c574b) - Order: 809c574b2b023c1af15747076b36257a98c662ede87078210d98edaf0529e0bc
2020-07-09 12:12:26.281 [TRC] CORE: notify: |DATA| (balance) balance updated
2020-07-09 12:12:30.002 [DBG] CORE: notify: |POKE| (order) Preimage sent - match cycle has begun for order 809c574b - Order: 809c574b2b023c1af15747076b36257a98c662ede87078210d98edaf0529e0bc
2020-07-09 12:12:30.002 [ERR] CORE: no active order found for preimage request for ee9edc27dbe73fe72f702e523276774e8be1aae9c19bc8c2bb3deddde53518e8
2020-07-09 12:12:30.003 [DBG] CORE: notify: |POKE| (order) Order placed - selling 1.00000000 dcr (ee9edc27) - Order: ee9edc27dbe73fe72f702e523276774e8be1aae9c19bc8c2bb3deddde53518e8
2020-07-09 12:12:30.026 [TRC] CORE: notify: |DATA| (balance) balance updated
2020-07-09 12:12:30.847 [DBG] CORE: ticking 2 trades
2020-07-09 12:12:32.491 [TRC] CORE: notify: |DATA| (epoch) - Index: 159426435
2020-07-09 12:12:32.494 [DBG] CORE: notify: |POKE| (order) Order placed - selling 1.00000000 dcr (a3509ffa) - Order: a3509ffaf237022539972f4dd7fe5f43b1657d3b315c13594ee769c72fac0a56
2020-07-09 12:12:32.504 [TRC] CORE: notify: |DATA| (balance) balance updated
2020-07-09 12:12:35.010 [ERR] CORE: match proof csum mismatch for epoch 159426434: expected 6714faa9a07b42ece9b9d7c0377603dca7ace758c86e083526cc7aa4ab1a60bf, got b3bb2616324c251c9533a113e5036b4434bde310ac6a0c6d354cce7307bab254
2020-07-09 12:12:37.589 [ERR] WEB: error placing order: rpc error: 47: suspended account may not submit trade orders
2020-07-09 12:12:39.612 [ERR] WEB: error placing order: rpc error: 47: suspended account may not submit trade orders
2020-07-09 12:12:40.000 [DBG] CORE: notify: |POKE| (order) Preimage sent - match cycle has begun for order a3509ffa - Order: a3509ffaf237022539972f4dd7fe5f43b1657d3b315c13594ee769c72fac0a56
2020-07-09 12:12:40.005 [TRC] CORE: notify: |DATA| (epoch) - Index: 159426436
2020-07-09 12:12:40.007 [ERR] CORE: unable to generate match proof for epoch 159426435: cannot generate match proof with an empty epoch queue
2020-07-09 12:12:42.731 [ERR] WEB: error placing order: rpc error: 47: suspended account may not submit trade orders
2020-07-09 12:12:44.847 [TRC] COMMS: got pinged, and ponged the server
2020-07-09 12:12:50.002 [TRC] CORE: notify: |DATA| (epoch) - Index: 159426437
2020-07-09 12:12:50.847 [DBG] CORE: ticking 3 trades

@chappjc
Copy link
Copy Markdown
Member Author

chappjc commented Jul 14, 2020

@JoeGruffins Thanks for the additional context. That helps a lot, notice the order of these messages indicates the server requested the preimage "before" the client placed the order:

2020-07-09 12:12:30.002 [ERR] CORE: no active order found for preimage request for ee9edc27dbe73fe72f702e523276774e8be1aae9c19bc8c2bb3deddde53518e8
2020-07-09 12:12:30.003 [DBG] CORE: notify: |POKE| (order) Order placed - selling 1.00000000 dcr (ee9edc27) - Order: ee9edc27dbe73fe72f702e523276774e8be1aae9c19bc8c2bb3deddde53518e8

This suggests that the client is submitting the order to the server before registering it's own order where the preimage request handler can find it.

@buck54321
Copy link
Copy Markdown
Member

Looks like the sequence in Trade is wrong. I'm working on synchronization around there in #525, so I'll pick it up there.

@chappjc
Copy link
Copy Markdown
Member Author

chappjc commented Jul 14, 2020

@buck54321 Great. We can track the issue in #544.

Copy link
Copy Markdown
Member

@buck54321 buck54321 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the asset.Swaps in client/asset/btc/livetest/regnet_test.go and client/asset/dcr/simnet_test.go both need to have the FeeRate set. Can probably just use the MaxFeeRate from the dex.Asset.

Otherwise, this seems good to go.

Comment thread client/core/trade.go
dbMatch, metaData, proof, _ := match.parts()
if match.failErr != nil || proof.RefundCoin != nil {
if match.failErr != nil || len(proof.RefundCoin) != 0 {
log.Debugf("Match %v not redeemable: failErr = %v, RefundCoin = %v",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might go a Tracef here. This will be the case for most matches.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If failErr is set, that's pretty helpful to log. RefundCoin being set should be unusual too, but less important to log.

Comment thread client/core/trade.go
dbMatch, _, proof, _ := match.parts()
if match.refundErr != nil || proof.RefundCoin != nil {
if match.refundErr != nil || len(proof.RefundCoin) != 0 {
log.Debugf("Match %v not refundable: refundErr = %v, RefundCoin = %v",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a Tracef?

Comment thread client/db/bolt/db_test.go Outdated
Comment thread client/db/bolt/db_test.go Outdated
Comment on lines +595 to +598
/*
func TestNilValue(t *testing.T) {
tDir, _ := ioutil.TempDir("", "dbtest")
defer os.RemoveAll(tDir)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to leave this comment, let's drop a link to this convo and a short explanation.

Copy link
Copy Markdown
Member Author

@chappjc chappjc Jul 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in a play now instead: https://play.golang.org/p/eU3x9oYeYfk

@chappjc

This comment has been minimized.

@chappjc chappjc force-pushed the btc-fee-cycles branch 2 times, most recently from 5815012 to 9990b2e Compare July 15, 2020 16:50
chappjc and others added 6 commits July 15, 2020 23:05
client/asset/btc: tweak and log sendWithReturn

client/asset/dcr: integer fee check in sendwithReturn

client/asset: set asset.Swaps.FeeRate in simnet harness tests
server/db: replace ActiveMatches with AllActiveUserMatches

server/auth: use AllActiveUserMatches in handleConnect

The msgjson.Matches sent with the connect response need the match time,
base fee rate, and quote fee rates set.

server/auth: handleConnect must send 2 match msgs if the user is both sides

server/db/driver/pg: remove (*Archiver).ActiveMatches
client/core: still store nil change coin

client/core: cleanup

client/core: match logging

log and check fee received from server

client/core: convert slice nil => length checks
client/db/bolt: fix dropped errors in filteredOrders and filteredMatches

client/db/bolt: order change tests and demo of bbolt ambiguity bug
@chappjc chappjc merged commit 0f53b60 into decred:master Jul 16, 2020
@chappjc chappjc deleted the btc-fee-cycles branch July 16, 2020 04:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

client/tick/swapMatches: min relay fee not met, 0 < 142 (code 66)

4 participants