Author Topic: Did I just find a bug in the client or is some other force at work here?  (Read 4130 times)

0 Members and 1 Guest are viewing this topic.

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
The potential for front-running exists because a delegate can see the unconfirmed order tx, and has the opportunity to insert a new tx in the block to take advantage of it.

Could this be avoided by simply changing the market-engine so that unconfirmed orders in inventory are only tested/matched against orders that have already been confirmed/written to the blockchain?

If orders in inventory are never matched against each other then won't that eliminate the delegate's ability to front-run the trade?
I think it's already done this way, confirmed orders take higher priority. However there is still chance for delegates to front-run.
BitShares committee member: abit
BitShares witness: in.abit

julian1

  • Guest
The potential for front-running exists because a delegate can see the unconfirmed order tx, and has the opportunity to insert a new tx in the block to take advantage of it.

Could this be avoided by simply changing the market-engine so that unconfirmed orders in inventory are only tested/matched against orders that have already been confirmed/written to the blockchain?

If orders in inventory are never matched against each other then won't that eliminate the delegate's ability to front-run the trade?


Offline arhag

  • Hero Member
  • *****
  • Posts: 1214
    • View Profile
    • My posts on Steem
  • BitShares: arhag
  • GitHub: arhag
Usually an "evil" delegate won't submit orders by their own account, instead, they may submit the front running orders from a couple of other accounts. This may make the statistical analysis more difficult.
By the way, if you encrypt an order for a special delegate, what will happen if the delegate missed its block, even if be voted out of top 101?

Okay, after thinking it through more, I am reconsidering the statistical analysis thing.

Here is how I originally saw it working. At any given time the client would determine who the next two delegates in the round to sign blocks are. If there is only one delegate left before the round ends (which should happen only 1% of the time), the client would simply delay for 10 to 20 seconds until the new round order is determined and the first two delegates of that new round are selected. The signed transaction is encrypted with a one-time symmetric key which is then encrypted twice (one for each delegate) and attached to the encrypted signed transaction and broadcast on the network. Furthermore, the expiration time of the transaction would be 30 seconds in the future, so if it doesn't get into the blocks produced by either of those two delegates, that particular transaction won't be added to the blockchain at all. If both delegates miss the block, the client (realizing this 30 seconds after the transaction was broadcast) will create a new transaction and try again. Most likely, it will get the transaction into the block on the first try.

Only encrypting the transaction for two delegates is important. Those who can decrypt the transaction are able to get access to the information that could allow them, or anyone they wish to help, to front run the market/limit order specified in the transaction with a high probability of success/profit. So it doesn't matter which account actually does the front running. We can simply assume that if there are three transactions in a block in which two of them conveniently were able to buy low and sell high to the third transaction, it is a mark of suspicion for either the delegate producing that block or the delegates producing the blocks immediately preceding it or immediately succeeding it. These bad marks can be accumulated over many rounds. Because the delegate order is randomized every round, we should expect the bad marks to accumulate significantly more on the delegate who is actually responsible for the front running rather than the ones who were just unlucky enough to be right before or after that bad delegate.

But the problem with the above approach is that an attacker can easily frame an honest delegate for front running to try to get them voted out, which basically compromises the entire purpose of statistical analysis for catching misbehaving delegates. An attacker could craft each of the transactions themselves, with one bid/ask pair front running the market/limit order, and submit them to the network simultaneously hoping they all get included in the same block produced by their target delegate. If they could do this enough, perhaps they could make it appear as if it was that target delegate that was misbehaving. I tried to think through various fixes for this, but ultimately I don't think anything works very well.


So now I am going back to my original idea that a two stage process is necessary if we want market/limit orders. We could cut it down to 20 seconds rather than 50 seconds though. It just means the probability of a market/limit order not being accepted fully into the blockchain goes up, which isn't a big deal because one can just redo the transaction for another tiny fee. WYAIWYG orders could also coexist and these wouldn't have such strict timing requirements because users suffer no loss from any potential front running of these orders anyway.

So the idea is that the important details of the order transaction (basically the quantities and the price limit) would be encrypted with some secret originally known only to the transaction creator. The transaction with encrypted info would be submitted to the network and if it was a valid transaction (within the constraints of the information made available) and had the appropriate fee it would be accepted into a block. After confirming the order was accepted into the block, the transaction creator's client would then have to automatically and quickly submit the second part of the transaction which referred to the first one already accepted into the block and provided the secret that decrypted the encrypted information in the first transaction (this transaction could have no fee). If this second transaction was not submitted into the block right after the block containing the first transaction, then that entire order becomes void. In that case, the transaction creator has to try again after evaluating the market to see if they still want to use the same exact order. If the second transaction is included in the very next block, then the order is activated (assuming it is actually valid) at that time. In order to prevent front running, WYAIWYG orders will also need to be delayed by one block: the order becomes activated the block after the block they are included in. However, activated WYAIWYG orders could all be processed before any activated market/limit orders. This would prevent anyone on the network from being able to use a market/limit order to front run a WYAIWYG order (assuming the WYAIWYG order gets accepted into the very next block after it was broadcast). However, a series of WYAIWYG orders could still be used to front run the WYAIWYG order even though it would not be a sure thing for anyone who is not the block-producing delegate (since they could have no guarantees about the order the WYAIWYG orders are processed in). And again, users using WYAIWYG orders shouldn't really be complaining about front running anyway. If front running really bothers them, they would have to use the more timing sensitive market/limit orders instead.

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
I think not only delegates but also every node (or say wallet) should check the market transactions inside new-produced blocks, if it contains bad transactions then just reject the block. The core code may already did so. No need for statistical analysis imho.

There is no "bad" market transaction. It is a perfectly valid transaction that was "lucky" enough to buy low and sell high. Of course, it wasn't actually "lucky", it was front-running. The front-runner is able to put in this transaction into the same block (and therefore as far as the blockchain is concerned both orders arrived simultaneously in time) as the transaction that it is "stealing" market savings from because it is at a more advantageous position in the block producing process: it is able to construct its front-running transaction after seeing the victim's transaction but before the deadline to include the front-running transaction into the same block is up.

I've already discussed how this could be solved by splitting market orders into two stages: a commitment followed later by a reveal. Unfortunately, this means that it would take 50 seconds for all market orders to go through rather than 10 seconds.

The compromise solution is to limit the potential front-runners to only the active delegates (by encrypting the market order transactions for the delegates only before broadcasting on the network) and to use statistical analysis to catch any misbehaving delegates. For the statistical analysis to work though, the encrypted transaction should not be capable of being decrypted by all 101 active delegates because then we have no way of confidently knowing which one of the 101 active delegates was the bad actor via statistical analysis. So, I recommend encrypting the market orders for only the next two block-producing delegates in the round.
OK, I understand now.
Usually an "evil" delegate won't submit orders by their own account, instead, they may submit the front running orders from a couple of other accounts. This may make the statistical analysis more difficult.
By the way, if you encrypt an order for a special delegate, what will happen if the delegate missed its block, even if be voted out of top 101?
BitShares committee member: abit
BitShares witness: in.abit

Offline arhag

  • Hero Member
  • *****
  • Posts: 1214
    • View Profile
    • My posts on Steem
  • BitShares: arhag
  • GitHub: arhag
I think not only delegates but also every node (or say wallet) should check the market transactions inside new-produced blocks, if it contains bad transactions then just reject the block. The core code may already did so. No need for statistical analysis imho.

There is no "bad" market transaction. It is a perfectly valid transaction that was "lucky" enough to buy low and sell high. Of course, it wasn't actually "lucky", it was front-running. The front-runner is able to put in this transaction into the same block (and therefore as far as the blockchain is concerned both orders arrived simultaneously in time) as the transaction that it is "stealing" market savings from because it is at a more advantageous position in the block producing process: it is able to construct its front-running transaction after seeing the victim's transaction but before the deadline to include the front-running transaction into the same block is up.

I've already discussed how this could be solved by splitting market orders into two stages: a commitment followed later by a reveal. Unfortunately, this means that it would take 50 seconds for all market orders to go through rather than 10 seconds.

The compromise solution is to limit the potential front-runners to only the active delegates (by encrypting the market order transactions for the delegates only before broadcasting on the network) and to use statistical analysis to catch any misbehaving delegates. For the statistical analysis to work though, the encrypted transaction should not be capable of being decrypted by all 101 active delegates because then we have no way of confidently knowing which one of the 101 active delegates was the bad actor via statistical analysis. So, I recommend encrypting the market orders for only the next two block-producing delegates in the round.


Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
It allows front-running by the block producers who can put mix their own orders into the block in whatever order they want. There's nothing you can do about it, so we do the worst-case matching every time and keep the profit for shareholders instead.

What are your thoughts on the solution to front-running discussed in this thread that allows for market/limit orders to be enabled while reducing the parties who could potentially front run orders to only the top 101 active delegates, and in which even that front-running risk is further protected against through statistical analysis?
I think not only delegates but also every node (or say wallet) should check the market transactions inside new-produced blocks, if it contains bad transactions then just reject the block. The core code may already did so. No need for statistical analysis imho.
BitShares committee member: abit
BitShares witness: in.abit

Offline arhag

  • Hero Member
  • *****
  • Posts: 1214
    • View Profile
    • My posts on Steem
  • BitShares: arhag
  • GitHub: arhag
It allows front-running by the block producers who can put mix their own orders into the block in whatever order they want. There's nothing you can do about it, so we do the worst-case matching every time and keep the profit for shareholders instead.

What are your thoughts on the solution to front-running discussed in this thread that allows for market/limit orders to be enabled while reducing the parties who could potentially front run orders to only the top 101 active delegates, and in which even that front-running risk is further protected against through statistical analysis?

Offline toast

  • Hero Member
  • *****
  • Posts: 4001
    • View Profile
  • BitShares: nikolai
From your screenshot, we can find that the orders executed at 86.4393 indeed.
It's a feature.
If someone (say Alice) asks 1 usd for 86 bts, and another one (say Bob) bids 1 usd at 94 bts, both of them will get what they want: Alice will get 86 bts and pay 1 usd, and Bob will get 1 usd and pay 94 bts. The difference (94-86=8bts) will be destroyed (as fee).

Is this really better than having the distributed automated market find the lowest seller or highest bidder for me?  That sort of automation in finding best price would be the real feature IMO, but I think it would allow front-running so BM decided to go the other way.  I am not sure tho..

I don't think this is better than automated matching of the lowest and highest bidders. If I want to buy 100 BitUSD at market rate on an exchange that only allows limit orders, and I see that the lowest ask is only for 80, while the 2nd lowest is for another 20, I would match my bid for the 2nd lowest ask, expecting to pay the lowest ask price on the first 80.

It allows front-running by the block producers who can put mix their own orders into the block in whatever order they want. There's nothing you can do about it, so we do the worst-case matching every time and keep the profit for shareholders instead.
Do not use this post as information for making any important decisions. The only agreements I ever make are informal and non-binding. Take the same precautions as when dealing with a compromised account, scammer, sockpuppet, etc.

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
There's definitely something fishy going on with the price_history command that's used to generate that candlechart. I have an issue open on Github for it, Vikram has added it to the 0.7 milestone: https://github.com/BitShares/bitshares/issues/1300

I added a comment to this issue. My patch may fix monsterer's problem, but no idea about the problem in OP.

EDIT:
The part of code in market_engine.cpp which deals with market history looks like a total mess.. >:(
I'm trying to figure it out. Headache.

EDIT again:
Comment on the issue updated. May work. Not tested though. (Don't know whether need to modify market_engine_v*.cpp)
« Last Edit: January 24, 2015, 04:04:20 pm by abit »
BitShares committee member: abit
BitShares witness: in.abit

Offline Ashaman

  • Newbie
  • *
  • Posts: 15
    • View Profile
From your screenshot, we can find that the orders executed at 86.4393 indeed.
It's a feature.
If someone (say Alice) asks 1 usd for 86 bts, and another one (say Bob) bids 1 usd at 94 bts, both of them will get what they want: Alice will get 86 bts and pay 1 usd, and Bob will get 1 usd and pay 94 bts. The difference (94-86=8bts) will be destroyed (as fee).

Is this really better than having the distributed automated market find the lowest seller or highest bidder for me?  That sort of automation in finding best price would be the real feature IMO, but I think it would allow front-running so BM decided to go the other way.  I am not sure tho..

I don't think this is better than automated matching of the lowest and highest bidders. If I want to buy 100 BitUSD at market rate on an exchange that only allows limit orders, and I see that the lowest ask is only for 80, while the 2nd lowest is for another 20, I would match my bid for the 2nd lowest ask, expecting to pay the lowest ask price on the first 80.

On a side note, I just burned a few BTS testing this out and the candlestick chart reflects the lowest ask price, while blockchain order history shows the higher price that I bid.




It may be that it's late and I'm not seeing this clearly, but I'm not seeing how automated price matching would allow front running. The explanation in the wiki makes a little more sense, but is still confusing:
Quote
This is expected to reduce volatility and liquidity as trading noise is removed from the network.
Does that mean it is expected to reduce both volatility AND liquidity? Reduced volatility is a good thing, but not at the cost of a reduction in liquidity IMO. I'm definitely going to give this more thought, but my initial feeling is that we would be better off with automated price matching (or alternately, the addition of market buys/sells).

Offline svk

There's definitely something fishy going on with the price_history command that's used to generate that candlechart. I have an issue open on Github for it, Vikram has added it to the 0.7 milestone: https://github.com/BitShares/bitshares/issues/1300

While alt's theory could have been correct, I don't think it is. I looked at the order history for that period and there's no such overlap between the asks and bids that could account for this:

Code: [Select]
timestamp: 2015-01-24T01:29:00 | bid price: 86.5000 | ask price: 86.5001 | delta: 0.0001 | amount USD: 12.9646
timestamp: 2015-01-24T00:55:50 | bid price: 85.9999 | ask price: 86.4393 | delta: 0.4394 | amount USD: 4.9645
timestamp: 2015-01-24T00:55:50 | bid price: 86.0000 | ask price: 86.4393 | delta: 0.4393 | amount USD: 97.5019
timestamp: 2015-01-24T00:13:40 | bid price: 85.5000 | ask price: 85.5002 | delta: 0.0002 | amount USD: 97.9997
timestamp: 2015-01-23T20:17:10 | bid price: 84.5000 | ask price: 84.5084 | delta: 0.0084 | amount USD: 569.9433
timestamp: 2015-01-23T16:41:40 | bid price: 84.8067 | ask price: 84.8068 | delta: 0.0001 | amount USD: 321.0000
timestamp: 2015-01-23T14:50:30 | bid price: 82.0802 | ask price: 82.0884 | delta: 0.0082 | amount USD: 169.9828
timestamp: 2015-01-23T14:30:40 | bid price: 84.3806 | ask price: 84.3890 | delta: 0.0084 | amount USD: 67.9493
timestamp: 2015-01-23T14:30:40 | bid price: 84.3806 | ask price: 84.3890 | delta: 0.0084 | amount USD: 296.2766
timestamp: 2015-01-23T14:30:40 | bid price: 84.3806 | ask price: 84.3890 | delta: 0.0084 | amount USD: 296.2766
timestamp: 2015-01-23T13:44:40 | bid price: 85.1443 | ask price: 85.1443 | delta: 0.0000 | amount USD: 2.9999
timestamp: 2015-01-23T12:56:20 | bid price: 85.2587 | ask price: 85.3100 | delta: 0.0513 | amount USD: 0.4999
timestamp: 2015-01-23T12:22:40 | bid price: 85.3117 | ask price: 85.3200 | delta: 0.0083 | amount USD: 70.0000
timestamp: 2015-01-23T10:53:40 | bid price: 85.2406 | ask price: 85.2491 | delta: 0.0085 | amount USD: 226.0000
timestamp: 2015-01-23T10:45:00 | bid price: 85.2000 | ask price: 85.2085 | delta: 0.0085 | amount USD: 99.9900
timestamp: 2015-01-23T07:11:00 | bid price: 82.7147 | ask price: 84.0001 | delta: 1.2854 | amount USD: 4.9999
timestamp: 2015-01-23T07:11:00 | bid price: 82.7147 | ask price: 82.8371 | delta: 0.1224 | amount USD: 0.3470
timestamp: 2015-01-23T07:11:00 | bid price: 82.7147 | ask price: 82.7230 | delta: 0.0083 | amount USD: 394.5351
timestamp: 2015-01-23T06:27:30 | bid price: 85.8811 | ask price: 85.9086 | delta: 0.0275 | amount USD: 0.0230
timestamp: 2015-01-23T06:27:10 | bid price: 85.9462 | ask price: 86.0085 | delta: 0.0623 | amount USD: 0.0004

Looking at the price history I found the point that's incorrect in your graph:

Code: [Select]
{
            "timestamp" : "2015-01-24T00:55:50",
            "highest_bid" : 1000,
            "lowest_ask" : 86.43930000337716,
            "opening_price" : 85.99989999690828,
            "closing_price" : 85.999999998108,
            "volume" : 885713089
        }

Now we can see why there's an issue! The client reports highest bid of 1000, this was already reported by monsterer a long time ago here: https://bitsharestalk.org/index.php?topic=10171.msg133005#msg133005, and had a Github issue that's been replaced by my most recent one.

The GUI is reporting a high of 94.6 due to a smoothing logic introduced in order to correct this issue of incorrect highs or lows from the client, that code is in https://github.com/BitShares/web_wallet/blob/master/app/js/services/market_service.coffee:

Code: [Select]
        @blockchain_api.market_price_history(market.asset_base_symbol, market.asset_quantity_symbol, start_time, 10*24*3600, 0).then (result) =>
            ohlc_data = []
            volume_data = []
            for t in result
                time = @helper.date(t.timestamp)
                o = prc(t.opening_price)
                c = prc(t.closing_price)
                lowest_ask = prc(t.lowest_ask)
                highest_bid = prc(t.highest_bid)
                h = if lowest_ask > highest_bid then lowest_ask else highest_bid
                l = if lowest_ask < highest_bid then lowest_ask else highest_bid

                h = o if o > h
                h = c if c > h
                l = o if o < l
                l = c if c < l

                oc_avg = (o + c) / 2.0
                h = 1.10 * Math.max(o,c) if h/oc_avg > 1.25
                l = 0.90 * Math.min(o,c) if oc_avg/l > 1.25
Worker: dev.bitsharesblocks

Offline carpet ride

  • Hero Member
  • *****
  • Posts: 544
    • View Profile
From your screenshot, we can find that the orders executed at 86.4393 indeed.
It's a feature.
If someone (say Alice) asks 1 usd for 86 bts, and another one (say Bob) bids 1 usd at 94 bts, both of them will get what they want: Alice will get 86 bts and pay 1 usd, and Bob will get 1 usd and pay 94 bts. The difference (94-86=8bts) will be destroyed (as fee).

Is this really better than having the distributed automated market find the lowest seller or highest bidder for me?  That sort of automation in finding best price would be the real feature IMO, but I think it would allow front-running so BM decided to go the other way.  I am not sure tho..
All opinions are my own. Anything said on this forum does not constitute an intent to create a legal obligation between myself and anyone else.
Check out my blog: http://CertainAssets.com
Buy the ticket, take the ride.

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
From your screenshot, we can find that the orders executed at 86.4393 indeed.
It's a feature.
If someone (say Alice) asks 1 usd for 86 bts, and another one (say Bob) bids 1 usd at 94 bts, both of them will get what they want: Alice will get 86 bts and pay 1 usd, and Bob will get 1 usd and pay 94 bts. The difference (94-86=8bts) will be destroyed (as fee).
BitShares committee member: abit
BitShares witness: in.abit

Offline Ashaman

  • Newbie
  • *
  • Posts: 15
    • View Profile
Over 4 hours ago, I made two sell orders for BitUSD @ 90.75 and 92.25 BTS. In the last hour, the candlestick chart reports a high of 94.6, however my sell orders have not filled, and there are no trades above 87 on the blockchain orders history.
Is this a bug, or is something else going on?

Relevant screenshots: https://imgur.com/a/KKrLv