Skip to content

Commit

Permalink
[Feature] oracle refactor & add oracle slashing (#265)
Browse files Browse the repository at this point in the history
* storing changes to tally, abvi, and keeper

* Revert "storing changes to tally, abvi, and keeper"

This reverts commit 70445a8.

* stylistic refactor done - not building

* Refactored oracle module. changing variable names

Price references changed to ExhangeRate

* Refactored rewards to be given out only for luna
removed claimpool, as it is no longer being used

* Implement oracle slashing

* update client code for miss count querying

* align comment & change DistributionPeriod to DistributionWindow & do swagger update

* jail the validator after oracle slashing

* * extract slashing from the keeper
* reduce tally cost by computing vote power at organizing
* split abstain vote for each denom
  • Loading branch information
yys authored and dokwon committed Nov 17, 2019
1 parent 864756a commit 543d1d2
Show file tree
Hide file tree
Showing 62 changed files with 1,698 additions and 1,108 deletions.
17 changes: 11 additions & 6 deletions app/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,21 +168,26 @@ func (app *TerraApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []s
/* Handle oracle state. */

// Clear all prevotes
app.oracleKeeper.IteratePrevotes(ctx, func(prevote oracle.PricePrevote) (stop bool) {
app.oracleKeeper.DeletePrevote(ctx, prevote)
app.oracleKeeper.IterateExchangeRatePrevotes(ctx, func(prevote oracle.ExchangeRatePrevote) (stop bool) {
app.oracleKeeper.DeleteExchangeRatePrevote(ctx, prevote)

return false
})

// Clear all votes
app.oracleKeeper.IterateVotes(ctx, func(vote oracle.PriceVote) (stop bool) {
app.oracleKeeper.DeleteVote(ctx, vote)
app.oracleKeeper.IterateExchangeRateVotes(ctx, func(vote oracle.ExchangeRateVote) (stop bool) {
app.oracleKeeper.DeleteExchangeRateVote(ctx, vote)
return false
})

// Clear all prices
app.oracleKeeper.IterateLunaPrices(ctx, func(denom string, price sdk.Dec) bool {
app.oracleKeeper.DeletePrice(ctx, denom)
app.oracleKeeper.IterateLunaExchangeRates(ctx, func(denom string, _ sdk.Dec) bool {
app.oracleKeeper.DeleteLunaExchangeRate(ctx, denom)
return false
})

app.oracleKeeper.IterateMissCounters(ctx, func(operator sdk.ValAddress, _ int64) bool {
app.oracleKeeper.SetMissCounter(ctx, operator, 0)
return false
})

Expand Down
2 changes: 1 addition & 1 deletion app/sim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ func testAndRunTxs(app *TerraApp) []simulation.WeightedOperation {
})
return v
}(nil),
oraclesim.SimulateMsgDelegateFeederPermission(app.oracleKeeper),
oraclesim.SimulateMsgDelegateFeedConsent(app.oracleKeeper),
},
{
func(_ *rand.Rand) int {
Expand Down
76 changes: 50 additions & 26 deletions client/lcd/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2140,7 +2140,7 @@ paths:
description: Internal Server Error
/oracle/denoms/{denom}/votes:
post:
summary: Generate oracle price vote message containing price and salt for an prevote
summary: Generate oracle exchange rate vote message containing exchange rate and salt for an prevote
tags:
- Oracle
produces:
Expand All @@ -2165,7 +2165,7 @@ paths:
500:
description: Internal Server Error
get:
summary: Request to get the currently unelected outstanding price oracle vote
summary: Request to get the currently unelected outstanding exchange rate oracle vote
tags:
- Oracle
produces:
Expand All @@ -2182,14 +2182,14 @@ paths:
schema:
type: array
items:
$ref: "#/definitions/PriceVote"
$ref: "#/definitions/ExchangeRateVote"
400:
description: Bad Request
500:
description: Internal Server Error
/oracle/denoms/{denom}/votes/{validator}:
get:
summary: Request to get the currently unelected outstanding price oracle vote
summary: Request to get the currently unelected outstanding exchange rate oracle vote
tags:
- Oracle
produces:
Expand All @@ -2210,14 +2210,14 @@ paths:
schema:
type: array
items:
$ref: "#/definitions/PriceVote"
$ref: "#/definitions/ExchangeRateVote"
400:
description: Bad Request
500:
description: Internal Server Error
/oracle/voters/{validator}/votes:
get:
summary: Request to get the currently outstanding price oracle votes of a validator
summary: Request to get the currently outstanding exchange rate oracle votes of a validator
tags:
- Oracle
produces:
Expand All @@ -2233,14 +2233,14 @@ paths:
schema:
type: array
items:
$ref: "#/definitions/PriceVote"
$ref: "#/definitions/ExchangeRateVote"
400:
description: Bad Request
500:
description: Internal Server Error
/oracle/denoms/{denom}/prevotes:
post:
summary: Generate oracle price prevote message containing hash of an vote
summary: Generate oracle exchange rate prevote message containing hash of an vote
tags:
- Oracle
produces:
Expand All @@ -2265,7 +2265,7 @@ paths:
500:
description: Internal Server Error
get:
summary: Request to get the currently outstanding price oracle prevote
summary: Request to get the currently outstanding exchange rate oracle prevote
tags:
- Oracle
produces:
Expand All @@ -2282,14 +2282,14 @@ paths:
schema:
type: array
items:
$ref: "#/definitions/PricePrevote"
$ref: "#/definitions/ExchangeRatePrevote"
400:
description: Bad Request
500:
description: Internal Server Error
/oracle/denoms/{denom}/prevotes/{validator}:
get:
summary: Request to get the currently outstanding price oracle prevote
summary: Request to get the currently outstanding exchange rate oracle prevote
tags:
- Oracle
produces:
Expand All @@ -2310,14 +2310,14 @@ paths:
schema:
type: array
items:
$ref: "#/definitions/PricePrevote"
$ref: "#/definitions/ExchangeRatePrevote"
400:
description: Bad Request
500:
description: Internal Server Error
/oracle/voters/{validator}/prevotes:
get:
summary: Request to get the currently outstanding price oracle prevotes of a validator
summary: Request to get the currently outstanding exchange rate oracle prevotes of a validator
tags:
- Oracle
produces:
Expand All @@ -2333,14 +2333,14 @@ paths:
schema:
type: array
items:
$ref: "#/definitions/PricePrevote"
$ref: "#/definitions/ExchangeRatePrevote"
400:
description: Bad Request
500:
description: Internal Server Error
/oracle/denoms/{denom}/price:
/oracle/denoms/{denom}/exchange_rate:
get:
summary: Get the current effective price in Luna for the asset
summary: Get the current effective exchange rate in Luna for the asset
tags:
- Oracle
produces:
Expand All @@ -2353,17 +2353,17 @@ paths:
type: string
responses:
200:
description: current price of denom i.e. "1000.0"
description: current exchange rate of denom i.e. "1000.0"
schema:
type: number
example: "1872.000000000000000000"
400:
description: Bad Request
500:
description: Internal Server Error
/oracle/denoms/prices:
/oracle/denoms/exchange_rates:
get:
summary: Get all activated denom coins
summary: Get all activated exchange rates
tags:
- Oracle
produces:
Expand Down Expand Up @@ -2445,6 +2445,30 @@ paths:
description: Bad Request
500:
description: Internal Server Error
/oracle/voters/{validator}/miss:
get:
summary: Get the number of vote periods missed in this oracle slash window.
tags:
- Oracle
produces:
- application/json
parameters:
- in: path
name: validator
description: oracle operator
required: true
type: string
responses:
200:
description: OK
schema:
type: number
format: integer
example: "100"
400:
description: Bad Request
500:
description: Internal Server Error
/oracle/parameters:
get:
summary: Get oracle params
Expand Down Expand Up @@ -3314,10 +3338,10 @@ definitions:
properties:
base_req:
$ref: "#/definitions/BaseReq"
price:
exchange_rate:
type: number
example: "1000.0"
description: "price of Luna in denom currency is to make provte hash; this field is required to submit prevote in case absense of hash"
description: "exchange rate of Luna in denom currency is to make provte hash; this field is required to submit prevote in case absense of hash"
salt:
type: string
example: "abcd"
Expand All @@ -3333,10 +3357,10 @@ definitions:
properties:
base_req:
$ref: "#/definitions/BaseReq"
price:
exchange_rate:
type: number
example: "1000.0"
description: "proof price of Luna in denom currency was used to make prevote hash; initial prevote does not require this field"
description: "proof exchange rate of Luna in denom currency was used to make prevote hash; initial prevote does not require this field"
salt:
type: string
example: "abcd"
Expand All @@ -3350,18 +3374,18 @@ definitions:
$ref: "#/definitions/BaseReq"
feeder:
$ref: "#/definitions/Address"
PriceVote:
ExchangeRateVote:
type: object
properties:
price:
exchange_rate:
type: number
example: "0.01241"
denom:
type: string
example: "ukrw"
voter:
$ref: "#/definitions/ValidatorAddress"
PricePrevote:
ExchangeRatePrevote:
type: object
properties:
hash:
Expand Down
2 changes: 1 addition & 1 deletion docs/specifications/market.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The Market module facilitates the atomic swaps among terra currencies and luna.

The market module adopts uni-swap modle to facilitate swaps between all terra currencies that have an active exchange rate with Luna registered with the Oracle module and Luna.

* A user can swap SDT \(TerraSDR\) and UST \(TerraUSD\) at the exchange rate registered with the oracle. For example, if Luna<>SDT exchange rate returned by `GetLunaPrice` by the oracle is 10, and Luna<>KRT exchange rate is 10,000, a swapping 1 SDT will return 1000 KRT.
* A user can swap SDT \(TerraSDR\) and UST \(TerraUSD\) at the exchange rate registered with the oracle. For example, if Luna<>SDT exchange rate returned by `GetLunaExchangeRate` by the oracle is 10, and Luna<>KRT exchange rate is 10,000, a swapping 1 SDT will return 1000 KRT.
* A user cap swap any of the Terra currencies for Luna at the oracle exchange rate. Using the same exchange rates in the above example, a user can swap 1 SDT for 0.1 Luna, or 0.1 Luna for 1 SDT.

## Safety mechanisms for Luna swaps
Expand Down
22 changes: 11 additions & 11 deletions docs/specifications/oracle.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ The objective of the oracle module is to get accurate exchange rates of Luna wit
In order to get fair exchange rates, the oracle operates in the following way:

* Let P = {P1, P2, ...} be a time series split up by `params.VotePeriod`, currently 1 minute. In each P, validators must submit two votes:
* A `MsgPricePrevote`, containing the SHA256 hash of the exchange rate of Luna is with respect to a Terra peg. For example, in order to support swaps for Terra currencies pegged to KRW, USD, SDR, three prevotes must be submitted each containing the uluna<>ukrw, uluna<>uusd, and uluna<>usdr exchange rates.
* A `MsgPriceVote`, containing the salt used to create the hash for the prevote submitted in P-1.
* A `MsgExchangeRatePrevote`, containing the SHA256 hash of the exchange rate of Luna is with respect to a Terra peg. For example, in order to support swaps for Terra currencies pegged to KRW, USD, SDR, three prevotes must be submitted each containing the uluna<>ukrw, uluna<>uusd, and uluna<>usdr exchange rates.
* A `MsgExchangeRateVote`, containing the salt used to create the hash for the prevote submitted in P-1.
* At the end of each P, votes submitted are tallied.
* The submitted salt of each vote is used to verify consistency with the prevote submitted by the validator in P-1. If the validator has not submitted a prevote, or the SHA256 resulting from the salt does not match the hash from the prevote, the vote is dropped.
* For each currency, if the total voting power of submitted votes exceeds 50%, a weighted median price of the vote is taken and is record on-chain as the effective exchange rate for Luna w.r.t. said currency for P+1.
Expand All @@ -31,18 +31,18 @@ Effectively this scheme forces the voter to commit to a firm price submission be
### Submit a prevote

```go
// MsgPricePrevote - struct for prevoting on the PriceVote.
// MsgExchangeRatePrevote - struct for prevoting on the ExchangeRateVote.
// The purpose of prevote is to hide vote price with hash
// which is formatted as hex string in SHA256("salt:price:denom:voter")
type MsgPricePrevote struct {
type MsgExchangeRatePrevote struct {
Hash string `json:"hash"` // hex string
Denom string `json:"denom"`
Feeder sdk.AccAddress `json:"feeder"`
Validator sdk.ValAddress `json:"validator"`
}
```

The `MsgPricePrevote` is just the submission of the leading 20 bytes of the SHA256 hex string run over a string containing the metadata of the actual `MsgPriceVote` to follow in the next period. The string is of the format: `salt:price:denom:voter`. Note that since in the subsequent `MsgPriceVote` the salt will have to be revealed, the salt used must be regenerated for each prevote submission.
The `MsgExchangeRatePrevote` is just the submission of the leading 20 bytes of the SHA256 hex string run over a string containing the metadata of the actual `MsgExchangeRateVote` to follow in the next period. The string is of the format: `salt:price:denom:voter`. Note that since in the subsequent `MsgExchangeRateVote` the salt will have to be revealed, the salt used must be regenerated for each prevote submission.

`Denom` is the denomination of the currency for which the vote is being cast. For example, if the voter wishes to submit a prevote for the usd, then the correct `Denom` is `uusd`.

Expand All @@ -55,10 +55,10 @@ The price used in the hash must be the open market price of Luna, w.r.t. to the
### Submit a vote

```go
// MsgPriceVote - struct for voting on the price of Luna denominated in various Terra assets.
// MsgExchangeRateVote - struct for voting on the price of Luna denominated in various Terra assets.
// For example, if the validator believes that the effective price of Luna in USD is 10.39, that's
// what the price field would be, and if 1213.34 for KRW, same.
type MsgPriceVote struct {
type MsgExchangeRateVote struct {
Price sdk.Dec `json:"price"` // the effective price of Luna in {Denom}
Salt string `json:"salt"`
Denom string `json:"denom"`
Expand All @@ -67,12 +67,12 @@ type MsgPriceVote struct {
}
```

The `MsgPriceVote` contains the actual price vote. The `Salt` parameter must match the salt used to create the prevote, otherwise the voter cannot be rewarded.
The `MsgExchangeRateVote` contains the actual price vote. The `Salt` parameter must match the salt used to create the prevote, otherwise the voter cannot be rewarded.


### Delegate voting rights to another key

Validators may also elect to delegate voting rights to another key to prevent the block signing key from being kept online. To do so, they must submit a `MsgDelegateFeederPermission`, delegating their oracle voting rights to a `FeedDelegate`, which in turn sign `MsgPricePrevote` and `MsgPriceVote` on behalf of the validator.
Validators may also elect to delegate voting rights to another key to prevent the block signing key from being kept online. To do so, they must submit a `NewMsgDelegateFeedConsent`, delegating their oracle voting rights to a `Delegatee`, which in turn sign `MsgExchangeRatePrevote` and `MsgExchangeRateVote` on behalf of the validator.

{% hint style="info" %}
Make sure to populate the delegate address with some coins by which to pay fees.
Expand All @@ -82,11 +82,11 @@ Make sure to populate the delegate address with some coins by which to pay fees.
// MsgDelegateFeederPermission - struct for delegating oracle voting rights to another address.
type MsgDelegateFeederPermission struct {
Operator sdk.ValAddress `json:"operator"`
FeedDelegate sdk.AccAddress `json:"feed_delegate"`
Delegatee sdk.AccAddress `json:"delegatee"`
}
```

The `Operator` field contains the operator address of the validator. The `FeedDelegate` field is the address of the delegate account that will be submitting price related votes and prevotes on behalf of the `Operator`.
The `Operator` field contains the operator address of the validator. The `Delegatee` field is the address of the delegate account that will be submitting price related votes and prevotes on behalf of the `Operator`.


## Parameters
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion x/market/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestSwapMsg(t *testing.T) {

afterTerraPoolDelta := input.MarketKeeper.GetTerraPoolDelta(input.Ctx)
diff := beforeTerraPoolDelta.Sub(afterTerraPoolDelta)
price, _ := input.OracleKeeper.GetLunaPrice(input.Ctx, core.MicroSDRDenom)
price, _ := input.OracleKeeper.GetLunaExchangeRate(input.Ctx, core.MicroSDRDenom)
require.Equal(t, price.MulInt(amt), diff.Abs())

swapMsg = NewMsgSwap(keeper.Addrs[0], offerCoin, core.MicroLunaDenom)
Expand Down
2 changes: 1 addition & 1 deletion x/market/internal/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestTerraPoolDeltaUpdate(t *testing.T) {
// each pools move towards base pool
func TestReplenishPools(t *testing.T) {
input := CreateTestInput(t)
input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, sdk.OneDec())
input.OracleKeeper.SetLunaExchangeRate(input.Ctx, core.MicroSDRDenom, sdk.OneDec())

basePool := input.MarketKeeper.BasePool(input.Ctx)
terraPoolDelta := input.MarketKeeper.GetTerraPoolDelta(input.Ctx)
Expand Down
2 changes: 1 addition & 1 deletion x/market/internal/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestQuerySwap(t *testing.T) {
input := CreateTestInput(t)

price := sdk.NewDecWithPrec(17, 1)
input.OracleKeeper.SetLunaPrice(input.Ctx, core.MicroSDRDenom, price)
input.OracleKeeper.SetLunaExchangeRate(input.Ctx, core.MicroSDRDenom, price)

querier := NewQuerier(input.MarketKeeper)
var err error
Expand Down
4 changes: 2 additions & 2 deletions x/market/internal/keeper/swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ func (k Keeper) ComputeInternalSwap(ctx sdk.Context, offerCoin sdk.DecCoin, askD
return offerCoin, nil
}

offerRate, err := k.oracleKeeper.GetLunaPrice(ctx, offerCoin.Denom)
offerRate, err := k.oracleKeeper.GetLunaExchangeRate(ctx, offerCoin.Denom)
if err != nil {
return sdk.DecCoin{}, types.ErrNoEffectivePrice(types.DefaultCodespace, offerCoin.Denom)
}

askRate, err := k.oracleKeeper.GetLunaPrice(ctx, askDenom)
askRate, err := k.oracleKeeper.GetLunaExchangeRate(ctx, askDenom)
if err != nil {
return sdk.DecCoin{}, types.ErrNoEffectivePrice(types.DefaultCodespace, askDenom)
}
Expand Down
Loading

0 comments on commit 543d1d2

Please sign in to comment.