Skip to content
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

Fede+Fabo/1472 Add begin redelegation to lcdClientMock #1490

Merged
merged 15 commits into from
Nov 7, 2018
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* #1436 governance endpoints and vuex module @fedekunze
* [\#1482](https://github.com/cosmos/voyager/issues/1482) Added ESLint errors: no-var. @sgobotta
* [\#1449](https://github.com/cosmos/voyager/issues/1449) shortNumber to num scripts for more readable numbers. @jbibla
* [\#1472](https://github.com/cosmos/voyager/issues/1472) Added mock functionality for redelegation @fedekunze + @faboweb

### Changed

Expand Down
232 changes: 150 additions & 82 deletions app/src/renderer/connectors/lcdClientMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ let state = {
height: 123
}
],
unbonding_delegations: []
unbonding_delegations: [],
redelegations: []
}
},
candidates: [
Expand Down Expand Up @@ -405,15 +406,20 @@ module.exports = {
return state.accounts[address]
},
async txs(address) {
// filter the txs for the ones for this account
return state.txs.filter(tx => {
return (
tx.tx.value.msg[0].value.inputs.find(
input => input.address === address
) ||
tx.tx.value.msg[0].value.outputs.find(
output => output.address === address
let type = tx.tx.value.msg[0].type
if (type === `cosmos-sdk/Send`) {
return (
tx.tx.value.msg[0].value.inputs.find(
input => input.address === address
) ||
tx.tx.value.msg[0].value.outputs.find(
output => output.address === address
)
)
)
}
return false // only return bank transactions
})
},
async tx(hash) {
Expand All @@ -435,13 +441,20 @@ module.exports = {
base_req: { name, sequence },
delegations = [],
begin_unbondings = [],
complete_unbondings = []
begin_redelegates = []
}
) {
let results = []
let fromKey = state.keys.find(a => a.name === name)
let fromAccount = state.accounts[fromKey.address]
let delegator = state.stake[fromKey.address]
if (!delegator) {
state.stake[fromKey.address] = {
delegations: [],
unbonding_delegations: []
}
delegator = state.stake[fromKey.address]
}
if (fromAccount == null) {
results.push(txResult(1, `Nonexistent account`))
return results
Expand Down Expand Up @@ -472,13 +485,6 @@ module.exports = {
fromAccount.coins.find(c => c.denom === denom).amount -= amount

// update stake
if (!delegator) {
state.stake[fromKey.address] = {
delegations: [],
unbonding_delegations: []
}
delegator = state.stake[fromKey.address]
}
let delegation = delegator.delegations.find(
d => d.validator_addr === tx.validator_addr
)
Expand Down Expand Up @@ -513,10 +519,7 @@ module.exports = {
=== /ABCI Log ===`)
}

candidate.tokens = (parseInt(candidate.tokens) + amount).toString()
candidate.delegator_shares = (
parseInt(candidate.delegator_shares) + amount
).toString()
updateValidatorShares(state, tx.validator_addr, amount)

storeTx(`cosmos-sdk/MsgDelegate`, tx)
results.push(txResult(0))
Expand All @@ -527,15 +530,7 @@ module.exports = {

let amount = parseInt(tx.shares)

// update sender balance
let coinBalance = fromAccount.coins.find(c => c.denom === `steak`)
coinBalance.amount = String(parseInt(coinBalance) + amount)

// update stake
if (!delegator) {
results.push(txResult(2, `Nonexistent delegator`))
return results
}
let delegation = delegator.delegations.find(
d => d.validator_addr === tx.validator_addr
)
Expand All @@ -544,17 +539,18 @@ module.exports = {
return results
}

// update sender balance
let coinBalance = fromAccount.coins.find(c => c.denom === `steak`)
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
coinBalance.amount = String(parseInt(coinBalance.amount) + amount)

// TODO remove after sdk.Dec parsing is fixed
amount = amount * 10000000000

let shares = parseInt(delegation.shares)
delegation.shares = (+shares - amount).toString()

let candidate = state.candidates.find(
c => c.operator_address === tx.validator_addr
)
shares = parseInt(candidate.tokens)
candidate.tokens = (+shares - amount).toString()
updateValidatorShares(state, tx.validator_addr, amount)

delegator.unbonding_delegations.push(
Object.assign({}, tx, {
balance: {
Expand All @@ -567,31 +563,107 @@ module.exports = {
results.push(txResult(0))
}

for (let tx of complete_unbondings) {
for (let tx of begin_redelegates) {
incrementSequence(fromAccount)

if (!delegator) {
results.push(txResult(2, `Nonexistent delegator`))
// check if source validator exist
let srcValidator = state.candidates.find(
c => c.operator_address === tx.validator_src_addr
)

if (!srcValidator) {
results.push(txResult(3, `Nonexistent source validator`))
return results
}

// remove undelegation
let unbondingDelegation = delegator.unbonding_delegations.find(
({ validator_addr }) => validator_addr === tx.validator_addr
// check if dest validator exist
let dstValidator = state.candidates.find(
c => c.operator_address === tx.validator_dst_addr
)
delegator.unbonding_delegations = delegator.unbonding_delegations.filter(
d => d !== unbondingDelegation

if (dstValidator.revoked) {
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(`checkTx failed: (262245) Msg 0 failed: === ABCI Log ===
Codespace: 4
Code: 101
ABCICode: 262245
Error: --= Error =--
Data: common.FmtError{format:"validator for this address is currently revoked", args:[]interface {}(nil)}
Msg Traces:
--= /Error =--

=== /ABCI Log ===`)
}

// check if delegation exists
let src_delegation = delegator.delegations.find(
d => d.validator_addr === tx.validator_src_addr
)
if (!src_delegation) {
results.push(
txResult(3, `Nonexistent delegation with source validator`)
)
return results
}

// put money back in the account
// we treat shares as atoms (1:1)
let amount = unbondingDelegation.shares
// check if there's an existing redelegation
let summary = this.getDelegator(tx.delegator_addr)
let red = summary.redelegations.find(
red =>
red.validator_src_addr === tx.validator_src_addr &&
red.validator_dst_addr === tx.validator_dst_addr
)

// update sender balance
let coinBalance = fromAccount.coins.find(c => c.denom === `steak`)
coinBalance.amount = String(parseInt(coinBalance) + amount)
if (red) {
results.push(txResult(3, `conflicting redelegation`))
return results
}

let height = getHeight()
// unbond shares from source validator
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
src_delegation.shares = String(Number(src_delegation.shares) - tx.shares)
src_delegation.height = height

// delegate to dst validator
let dst_delegation = delegator.delegations.find(
d => d.validator_addr === tx.validator_dst_addr
)
if (dst_delegation) {
dst_delegation.shares = String(
Number(dst_delegation.shares) + tx.shares
)
} else {
delegator.delegations.push({
delegator_addr: tx.delegator_addr,
validator_addr: tx.validator_dst_addr,
shares: tx.shares,
height
})
}

// add redelegation object
let coins = {
amount: tx.shares, // in mock mode we assume 1 share = 1 token
denom: `steak`
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
}
let minTime = Date.now()
red = {
delegator_addr: tx.delegator_addr,
validator_src_addr: tx.validator_src_addr,
validator_dst_addr: tx.validator_dst_addr,
shares_src: tx.shares,
shares_dst: tx.shares,
min_time: new Date(minTime + 10 * 60 * 1000).getTime(), // uses a 10 min unbonding period
creation_height: height,
initial_balance: coins,
balance: coins
}
delegator.redelegations.push(red)

storeTx(`cosmos-sdk/CompleteUnbonding`, tx)
// update validator shares
updateValidatorShares(state, tx.validator_src_addr, -tx.shares)
updateValidatorShares(state, tx.validator_dst_addr, tx.shares)

storeTx(`cosmos-sdk/BeginRedelegate`, tx)
results.push(txResult(0))
}

Expand Down Expand Up @@ -619,13 +691,31 @@ module.exports = {
let delegator = state.stake[delegatorAddress] || {}
return delegator
},
getDelegatorTxs(addr, types = []) {
if (types.length === 0) types = [`bonding`, `unbonding`]
async getDelegatorTxs(addr, types = []) {
// filter for transactions belonging to that delegator and are staking
let delegatorTxs = state.txs.filter(tx => {
let type = tx.tx.value.msg[0].type
if (
type === `cosmos-sdk/MsgDelegate` ||
type === `cosmos-sdk/BeginRedelegate` ||
type === `cosmos-sdk/BeginUnbonding`
) {
return tx.tx.value.msg[0].value.delegator_addr === addr
}
return false
})

// map REST filters to tx types
if (types.length === 0) types = [`bonding`, `unbonding`, `redelegate`]
types = types.map(type => {
if (type === `bonding`) return `cosmos-sdk/MsgDelegate`
if (type === `unbonding`) return `cosmos-sdk/BeginUnbonding`
if (type === `redelegate`) return `cosmos-sdk/BeginRedelegate`
})
return getTxs(types)

return delegatorTxs.filter(
tx => types.indexOf(tx.tx.value.msg[0].type) !== -1
)
},
async getCandidates() {
return state.candidates
Expand Down Expand Up @@ -786,37 +876,6 @@ function storeTx(type, body) {
})
}

function getTxs(types) {
return state.txs.filter(tx => types.indexOf(tx.tx.value.msg[0].type) !== -1)
}

// function delegate (sender, { pub_key: { data: pubKey }, amount: delegation }) {
// let delegate = state.delegates.find(d => d.pub_key.data === pubKey)
// if (!delegate) {
// return txResult(1, 'Delegator does not exist')
// }
//
// let fermions = state.accounts[sender].coins.find(c => c.denom === 'fermion')
// if (!fermions) {
// state.accounts[sender].coins.push({ denom: 'fermion', amount: 0 })
// fermions = state.accounts[sender].coins.find(c => c.denom === 'fermion')
// }
// if (fermions.amount < delegation.amount) {
// return txResult(1, 'Not enought fermions to stake')
// }
//
// // execute
// fermions.amount -= delegation.amount
// delegate.voting_power += delegation.amount
// state.stake[sender][pubKey] = state.stake[sender][pubKey] || {
// PubKey: { data: pubKey },
// Shares: 0
// }
// state.stake[sender][pubKey].Shares += delegation.amount
//
// return txResult()
// }

function txResult(code = 0, message = ``) {
return {
check_tx: {
Expand All @@ -840,3 +899,12 @@ function txResult(code = 0, message = ``) {
function incrementSequence(account) {
account.sequence = (parseInt(account.sequence) + 1).toString()
}

function updateValidatorShares(state, operator_address, amount) {
let candidate = state.candidates.find(
c => c.operator_address === operator_address
)
let shares = parseInt(candidate.tokens)
candidate.tokens = (+shares - amount).toString()
candidate.delegator_shares = (+shares - amount).toString()
}
18 changes: 11 additions & 7 deletions app/src/renderer/scripts/tx-utils.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
"use strict"

import crypto from "crypto"

export function getTxHash(txstring) {
let s256Buffer = crypto
.createHash(`sha256`)
.update(Buffer.from(txstring, `base64`))
.digest()
let txbytes = new Uint8Array(s256Buffer)
return Buffer.from(txbytes.slice(0, 20)).toString(`hex`)
module.exports = {
getTxHash(txstring) {
let s256Buffer = crypto
.createHash(`sha256`)
.update(Buffer.from(txstring, `base64`))
.digest()
let txbytes = new Uint8Array(s256Buffer)
return Buffer.from(txbytes.slice(0, 20)).toString(`hex`)
}
}
Loading