Merge pull request #902 from cosmos/cwgoes/expose-bonding-unbonding-blocks
Expose last bonding/unbonding block
This commit is contained in:
commit
16f43fcb87
|
@ -16,6 +16,7 @@ BREAKING CHANGES
|
|||
|
||||
* Remove go-wire, use go-amino
|
||||
* Gaia simple-staking bond and unbond functions replaced
|
||||
* [stake] Delegator bonds now store the height at which they were updated
|
||||
* [store] Add `SubspaceIterator` and `ReverseSubspaceIterator` to `KVStore` interface
|
||||
* [basecoin] NewBasecoinApp takes a `dbm.DB` and uses namespaced DBs for substores
|
||||
* All module keepers now require a codespace, see basecoin or democoin for usage
|
||||
|
|
|
@ -158,6 +158,9 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
|||
pool, candidate, newShares := pool.candidateAddTokens(candidate, bondAmt.Amount)
|
||||
bond.Shares = bond.Shares.Add(newShares)
|
||||
|
||||
// Update bond height
|
||||
bond.Height = ctx.BlockHeight()
|
||||
|
||||
k.setDelegatorBond(ctx, bond)
|
||||
k.setCandidate(ctx, candidate)
|
||||
k.setPool(ctx, pool)
|
||||
|
@ -226,6 +229,8 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
|||
|
||||
k.removeDelegatorBond(ctx, bond)
|
||||
} else {
|
||||
// Update bond height
|
||||
bond.Height = ctx.BlockHeight()
|
||||
k.setDelegatorBond(ctx, bond)
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,10 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
|
||||
// just send the same msgbond multiple times
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, bondAmount)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
ctx = ctx.WithBlockHeight(int64(i))
|
||||
|
||||
got := handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
|
@ -90,6 +93,8 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
expLiabilities := int64(i+2) * bondAmount // (1 self delegation)
|
||||
expDelegatorAcc := initBond - expBond
|
||||
|
||||
require.Equal(t, bond.Height, int64(i), "Incorrect bond height")
|
||||
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotLiabilities := candidate.Liabilities.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
|
|
@ -354,7 +354,7 @@ func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) {
|
|||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// load a delegator bong
|
||||
// load a delegator bond
|
||||
func (k Keeper) GetDelegatorBond(ctx sdk.Context,
|
||||
delegatorAddr, candidateAddr sdk.Address) (bond DelegatorBond, found bool) {
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ func TestBond(t *testing.T) {
|
|||
bondsEqual := func(b1, b2 DelegatorBond) bool {
|
||||
return bytes.Equal(b1.DelegatorAddr, b2.DelegatorAddr) &&
|
||||
bytes.Equal(b1.CandidateAddr, b2.CandidateAddr) &&
|
||||
b1.Height == b2.Height &&
|
||||
b1.Shares == b2.Shares
|
||||
}
|
||||
|
||||
|
@ -145,11 +146,11 @@ func TestBond(t *testing.T) {
|
|||
// add some more records
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
keeper.setCandidate(ctx, candidates[2])
|
||||
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9)}
|
||||
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9)}
|
||||
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9)}
|
||||
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9)}
|
||||
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9)}
|
||||
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
|
||||
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
|
||||
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
|
||||
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
|
||||
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
|
||||
keeper.setDelegatorBond(ctx, bond1to2)
|
||||
keeper.setDelegatorBond(ctx, bond1to3)
|
||||
keeper.setDelegatorBond(ctx, bond2to1)
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
// RegisterRoutes - Central function to define routes that get registered by the main application
|
||||
func RegisterRoutes(r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
|
||||
r.HandleFunc("/stake/{delegator}/bonding_status/{candidate}", BondingStatusHandler("stake", cdc, kb)).Methods("GET")
|
||||
}
|
||||
|
||||
// BondingStatusHandler - http request handler to query delegator bonding status
|
||||
func BondingStatusHandler(storeName string, cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// read parameters
|
||||
vars := mux.Vars(r)
|
||||
delegator := vars["delegator"]
|
||||
validator := vars["validator"]
|
||||
|
||||
bz, err := hex.DecodeString(delegator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
delegatorAddr := sdk.Address(bz)
|
||||
|
||||
bz, err = hex.DecodeString(validator)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
candidateAddr := sdk.Address(bz)
|
||||
|
||||
key := stake.GetDelegatorBondKey(delegatorAddr, candidateAddr, cdc)
|
||||
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't query bond. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// the query will return empty if there is no data for this bond
|
||||
if len(res) == 0 {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
var bond stake.DelegatorBond
|
||||
err = cdc.UnmarshalBinary(res, &bond)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't decode bond. Error: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
output, err := cdc.MarshalJSON(bond)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
|
@ -164,4 +164,5 @@ type DelegatorBond struct {
|
|||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
CandidateAddr sdk.Address `json:"candidate_addr"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
Height int64 `json:"height"` // Last height bond updated
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// keeper to view information & slash validators
|
||||
// will be used by governance module
|
||||
type ViewSlashKeeper struct {
|
||||
keeper Keeper
|
||||
}
|
||||
|
||||
// NewViewSlashKeeper creates a keeper restricted to
|
||||
// viewing information & slashing validators
|
||||
func NewViewSlashKeeper(k Keeper) ViewSlashKeeper {
|
||||
return ViewSlashKeeper{k}
|
||||
}
|
||||
|
||||
// load a delegator bond
|
||||
func (v ViewSlashKeeper) GetDelegatorBond(ctx sdk.Context,
|
||||
delegatorAddr, candidateAddr sdk.Address) (bond DelegatorBond, found bool) {
|
||||
return v.keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||
}
|
||||
|
||||
// load n delegator bonds
|
||||
func (v ViewSlashKeeper) GetDelegatorBonds(ctx sdk.Context,
|
||||
delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) {
|
||||
return v.keeper.GetDelegatorBonds(ctx, delegator, maxRetrieve)
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// tests GetDelegatorBond, GetDelegatorBonds
|
||||
func TestViewSlashBond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
//construct the candidates
|
||||
amts := []int64{9, 8, 7}
|
||||
var candidates [3]Candidate
|
||||
for i, amt := range amts {
|
||||
candidates[i] = Candidate{
|
||||
Address: addrVals[i],
|
||||
PubKey: pks[i],
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
}
|
||||
}
|
||||
|
||||
// first add a candidates[0] to delegate too
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
|
||||
bond1to1 := DelegatorBond{
|
||||
DelegatorAddr: addrDels[0],
|
||||
CandidateAddr: addrVals[0],
|
||||
Shares: sdk.NewRat(9),
|
||||
}
|
||||
|
||||
viewSlashKeeper := NewViewSlashKeeper(keeper)
|
||||
|
||||
bondsEqual := func(b1, b2 DelegatorBond) bool {
|
||||
return bytes.Equal(b1.DelegatorAddr, b2.DelegatorAddr) &&
|
||||
bytes.Equal(b1.CandidateAddr, b2.CandidateAddr) &&
|
||||
b1.Height == b2.Height &&
|
||||
b1.Shares == b2.Shares
|
||||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.setDelegatorBond(ctx, bond1to1)
|
||||
resBond, found := viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bondsEqual(bond1to1, resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
bond1to1.Shares = sdk.NewRat(99)
|
||||
keeper.setDelegatorBond(ctx, bond1to1)
|
||||
resBond, found = viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bondsEqual(bond1to1, resBond))
|
||||
|
||||
// add some more records
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
keeper.setCandidate(ctx, candidates[2])
|
||||
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
|
||||
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
|
||||
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
|
||||
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
|
||||
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
|
||||
keeper.setDelegatorBond(ctx, bond1to2)
|
||||
keeper.setDelegatorBond(ctx, bond1to3)
|
||||
keeper.setDelegatorBond(ctx, bond2to1)
|
||||
keeper.setDelegatorBond(ctx, bond2to2)
|
||||
keeper.setDelegatorBond(ctx, bond2to3)
|
||||
|
||||
// test all bond retrieve capabilities
|
||||
resBonds := viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bondsEqual(bond1to1, resBonds[0]))
|
||||
assert.True(t, bondsEqual(bond1to2, resBonds[1]))
|
||||
assert.True(t, bondsEqual(bond1to3, resBonds[2]))
|
||||
resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 3)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 2)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
||||
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
||||
assert.True(t, bondsEqual(bond2to3, resBonds[2]))
|
||||
|
||||
}
|
Loading…
Reference in New Issue