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

refactor: optimization of total power calculation #518

Merged
merged 1 commit into from
Jun 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion execution/execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func TestLockTime(t *testing.T) {
val := sb.MakeNewValidator(pub)
sb.UpdateValidator(val)

sb.AcceptTestSortition = true
sb.TestAcceptSortition = true
pld := &payload.SortitionPayload{
Address: pub.Address(),
Proof: sortition.GenerateRandomProof(),
Expand Down
1 change: 1 addition & 0 deletions execution/executor/bond.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func (e *BondExecutor) Execute(trx *tx.Tx, sb sandbox.Sandbox) error {
receiverVal.AddToStake(pld.Stake)
receiverVal.UpdateLastBondingHeight(sb.CurrentHeight())

sb.UpdatePowerDelta(pld.Stake)
sb.UpdateAccount(pld.Sender, senderAcc)
sb.UpdateValidator(receiverVal)

Expand Down
23 changes: 11 additions & 12 deletions execution/executor/bond_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ func TestExecuteBondTx(t *testing.T) {
senderAddr, senderAcc := tSandbox.TestStore.RandomTestAcc()
senderBalance := senderAcc.Balance()
pub, _ := bls.GenerateTestKeyPair()
receiverAddr := pub.Address()
fee, amt := randomAmountAndFee(senderBalance / 2)

t.Run("Should fail, invalid sender", func(t *testing.T) {
trx := tx.NewBondTx(tStamp500000, 1, crypto.GenerateTestAddress(),
pub.Address(), pub, amt, fee, "invalid sender")
receiverAddr, pub, amt, fee, "invalid sender")

err := exe.Execute(trx, tSandbox)
assert.Equal(t, errors.Code(err), errors.ErrInvalidAddress)
Expand All @@ -37,15 +38,15 @@ func TestExecuteBondTx(t *testing.T) {

t.Run("Should fail, invalid sequence", func(t *testing.T) {
trx := tx.NewBondTx(tStamp500000, senderAcc.Sequence()+2, senderAddr,
pub.Address(), pub, amt, fee, "invalid sequence")
receiverAddr, pub, amt, fee, "invalid sequence")

err := exe.Execute(trx, tSandbox)
assert.Equal(t, errors.Code(err), errors.ErrInvalidSequence)
})

t.Run("Should fail, insufficient balance", func(t *testing.T) {
trx := tx.NewBondTx(tStamp500000, senderAcc.Sequence()+1, senderAddr,
pub.Address(), pub, senderBalance+1, 0, "insufficient balance")
receiverAddr, pub, senderBalance+1, 0, "insufficient balance")

err := exe.Execute(trx, tSandbox)
assert.Equal(t, errors.Code(err), errors.ErrInsufficientFunds)
Expand Down Expand Up @@ -74,35 +75,33 @@ func TestExecuteBondTx(t *testing.T) {

t.Run("Should fail, public key is not set", func(t *testing.T) {
trx := tx.NewBondTx(tStamp500000, senderAcc.Sequence()+1, senderAddr,
pub.Address(), nil, amt, fee, "no public key")
receiverAddr, nil, amt, fee, "no public key")

err := exe.Execute(trx, tSandbox)
assert.Equal(t, errors.Code(err), errors.ErrInvalidPublicKey)
})

t.Run("Ok", func(t *testing.T) {
trx := tx.NewBondTx(tStamp500000, senderAcc.Sequence()+1, senderAddr,
pub.Address(), pub, amt, fee, "ok")
receiverAddr, pub, amt, fee, "ok")

assert.NoError(t, exe.Execute(trx, tSandbox), "Ok")
assert.Error(t, exe.Execute(trx, tSandbox), "Execute again, should fail")
})

t.Run("Should fail, public key should not set for existing validators", func(t *testing.T) {
trx := tx.NewBondTx(tStamp500000, senderAcc.Sequence()+2, senderAddr,
pub.Address(), pub, amt, fee, "with public key")
receiverAddr, pub, amt, fee, "with public key")

err := exe.Execute(trx, tSandbox)
assert.Equal(t, errors.Code(err), errors.ErrInvalidPublicKey)
})

assert.Equal(t, tSandbox.Account(senderAddr).Balance(),
senderBalance-(amt+fee))
assert.Equal(t, tSandbox.Validator(pub.Address()).Stake(), amt)
assert.Equal(t, tSandbox.Validator(pub.Address()).LastBondingHeight(),
tSandbox.CurrentHeight())
assert.Equal(t, tSandbox.Account(senderAddr).Balance(), senderBalance-(amt+fee))
assert.Equal(t, tSandbox.Validator(receiverAddr).Stake(), amt)
assert.Equal(t, tSandbox.Validator(receiverAddr).LastBondingHeight(), tSandbox.CurrentHeight())
assert.Equal(t, tSandbox.PowerDelta(), amt)
assert.Equal(t, exe.Fee(), fee)

checkTotalCoin(t, fee)
}

Expand Down
20 changes: 10 additions & 10 deletions execution/executor/sortition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,41 @@ func TestExecuteSortitionTx(t *testing.T) {

t.Run("Should fail, Invalid address", func(t *testing.T) {
trx := tx.NewSortitionTx(tStamp500000, 1, crypto.GenerateTestAddress(), proof)
tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true
assert.Equal(t, errors.Code(exe.Execute(trx, tSandbox)), errors.ErrInvalidAddress)
})

newVal.UpdateLastBondingHeight(tSandbox.CurrentHeight() - tSandbox.Params().BondInterval + 1)
tSandbox.UpdateValidator(newVal)
t.Run("Should fail, Bonding period", func(t *testing.T) {
trx := tx.NewSortitionTx(tStamp500000, newVal.Sequence()+1, newVal.Address(), proof)
tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true
assert.Equal(t, errors.Code(exe.Execute(trx, tSandbox)), errors.ErrInvalidHeight)
})

tSandbox.TestStore.AddTestBlock(500001)

t.Run("Should fail, Invalid sequence", func(t *testing.T) {
trx := tx.NewSortitionTx(tStamp500000, newVal.Sequence()+2, newVal.Address(), proof)
tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true
assert.Equal(t, errors.Code(exe.Execute(trx, tSandbox)), errors.ErrInvalidSequence)
})

t.Run("Should fail, Invalid proof", func(t *testing.T) {
trx := tx.NewSortitionTx(tStamp500000, newVal.Sequence()+1, newVal.Address(), proof)
tSandbox.AcceptTestSortition = false
tSandbox.TestAcceptSortition = false
assert.Equal(t, errors.Code(exe.Execute(trx, tSandbox)), errors.ErrInvalidProof)
})

t.Run("Should fail, Committee has free seats and validator is in the committee", func(t *testing.T) {
trx := tx.NewSortitionTx(tStamp500000, existingVal.Sequence()+1, existingVal.Address(), proof)
tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true
assert.Equal(t, errors.Code(exe.Execute(trx, tSandbox)), errors.ErrInvalidTx)
})

t.Run("Should be ok", func(t *testing.T) {
trx := tx.NewSortitionTx(tStamp500000, newVal.Sequence()+1, newVal.Address(), proof)
tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true
assert.NoError(t, exe.Execute(trx, tSandbox))

// Execute again, should fail
Expand All @@ -88,7 +88,7 @@ func TestSortitionNonStrictMode(t *testing.T) {
val := tSandbox.TestStore.RandomTestVal()
proof := sortition.GenerateRandomProof()

tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true
trx := tx.NewSortitionTx(tStamp500000, val.Sequence(), val.Address(), proof)
assert.Error(t, exe1.Execute(trx, tSandbox))
assert.NoError(t, exe2.Execute(trx, tSandbox))
Expand Down Expand Up @@ -119,7 +119,7 @@ func TestChangePower1(t *testing.T) {
proof3 := sortition.GenerateRandomProof()

tSandbox.TestParams.CommitteeSize = 4
tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true
trx1 := tx.NewSortitionTx(tStamp500000, val1.Sequence()+1, val1.Address(), proof1)
assert.NoError(t, exe.Execute(trx1, tSandbox))

Expand Down Expand Up @@ -162,7 +162,7 @@ func TestChangePower2(t *testing.T) {
proof4 := sortition.GenerateRandomProof()

tSandbox.TestParams.CommitteeSize = 7
tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true
trx1 := tx.NewSortitionTx(tStamp500000, val1.Sequence()+1, val1.Address(), proof1)
assert.NoError(t, exe.Execute(trx1, tSandbox))

Expand Down Expand Up @@ -196,7 +196,7 @@ func TestOldestDidNotPropose(t *testing.T) {
}

tSandbox.TestParams.CommitteeSize = 7
tSandbox.AcceptTestSortition = true
tSandbox.TestAcceptSortition = true

stamp := tStamp500000
for i := 0; i < 8; i = i + 2 {
Expand Down
5 changes: 5 additions & 0 deletions execution/executor/unbond.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ func (e *UnbondExecutor) Execute(trx *tx.Tx, sb sandbox.Sandbox) error {

val.IncSequence()
val.UpdateUnbondingHeight(sb.CurrentHeight())

// At this point, the validator's power is zero.
// However, we know the validator's stake.
// So, we can update the power delta with the negative of the validator's stake.
sb.UpdatePowerDelta(-1 * val.Power())
sb.UpdateValidator(val)

return nil
Expand Down
12 changes: 7 additions & 5 deletions execution/executor/unbond_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func TestExecuteUnbondTx(t *testing.T) {
exe := NewUnbondExecutor(true)

pub, _ := bls.GenerateTestKeyPair()
valAddr := pub.Address()
val := tSandbox.MakeNewValidator(pub)
tSandbox.UpdateValidator(val)

Expand All @@ -24,7 +25,7 @@ func TestExecuteUnbondTx(t *testing.T) {
})

t.Run("Should fail, Invalid sequence", func(t *testing.T) {
trx := tx.NewUnbondTx(tStamp500000, val.Sequence()+2, pub.Address(), "invalid sequence")
trx := tx.NewUnbondTx(tStamp500000, val.Sequence()+2, valAddr, "invalid sequence")
assert.Equal(t, errors.Code(exe.Execute(trx, tSandbox)), errors.ErrInvalidSequence)
})

Expand All @@ -45,17 +46,18 @@ func TestExecuteUnbondTx(t *testing.T) {
})

t.Run("Ok", func(t *testing.T) {
trx := tx.NewUnbondTx(tStamp500000, val.Sequence()+1, pub.Address(), "Ok")
trx := tx.NewUnbondTx(tStamp500000, val.Sequence()+1, valAddr, "Ok")

assert.NoError(t, exe.Execute(trx, tSandbox))

// Execute again, should fail
assert.Error(t, exe.Execute(trx, tSandbox))
})

assert.Zero(t, tSandbox.Validator(pub.Address()).Stake())
assert.Zero(t, tSandbox.Validator(pub.Address()).Power())
assert.Equal(t, tSandbox.Validator(pub.Address()).UnbondingHeight(), tSandbox.CurrentHeight())
assert.Zero(t, tSandbox.Validator(valAddr).Stake())
assert.Zero(t, tSandbox.Validator(valAddr).Power())
assert.Equal(t, tSandbox.Validator(valAddr).UnbondingHeight(), tSandbox.CurrentHeight())
assert.Equal(t, tSandbox.PowerDelta(), -1*val.Stake())
assert.Zero(t, exe.Fee())

checkTotalCoin(t, 0)
Expand Down
2 changes: 2 additions & 0 deletions sandbox/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type Sandbox interface {
Validator(crypto.Address) *validator.Validator
MakeNewValidator(*bls.PublicKey) *validator.Validator
UpdateValidator(*validator.Validator)
UpdatePowerDelta(delta int64)
PowerDelta() int64

VerifyProof(hash.Stamp, sortition.Proof, *validator.Validator) bool
Committee() committee.Reader
Expand Down
12 changes: 9 additions & 3 deletions sandbox/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ type MockSandbox struct {
TestStore *store.MockStore
TestCommittee committee.Committee
TestCommitteeSigners []crypto.Signer
AcceptTestSortition bool
TestAcceptSortition bool
TestPowerDelta int64
}

func MockingSandbox() *MockSandbox {
Expand Down Expand Up @@ -97,7 +98,12 @@ func (m *MockSandbox) IterateValidators(consumer func(*validator.Validator, bool
func (m *MockSandbox) Committee() committee.Reader {
return m.TestCommittee
}

func (m *MockSandbox) UpdatePowerDelta(delta int64) {
m.TestPowerDelta += delta
}
func (m *MockSandbox) PowerDelta() int64 {
return m.TestPowerDelta
}
func (m *MockSandbox) VerifyProof(hash.Stamp, sortition.Proof, *validator.Validator) bool {
return m.AcceptTestSortition
return m.TestAcceptSortition
}
36 changes: 25 additions & 11 deletions sandbox/sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type sandbox struct {
params param.Params
totalAccounts int32
totalValidators int32
totalPower int64
powerDelta int64
}

type sandboxValidator struct {
Expand All @@ -40,11 +42,13 @@ type sandboxAccount struct {
updated bool
}

func NewSandbox(store store.Reader, params param.Params, committee committee.Reader) Sandbox {
func NewSandbox(store store.Reader, params param.Params,
committee committee.Reader, totalPower int64) Sandbox {
sb := &sandbox{
store: store,
committee: committee,
params: params,
store: store,
committee: committee,
totalPower: totalPower,
params: params,
}

sb.accounts = make(map[crypto.Address]*sandboxAccount)
Expand Down Expand Up @@ -223,18 +227,28 @@ func (sb *sandbox) Committee() committee.Reader {
return sb.committee
}

// TODO: write test for me.
// UpdatePowerDelta updates the change in the total power of the blockchain.
// The delta is the amount of change in the total power and can be either positive or negative.
func (sb *sandbox) UpdatePowerDelta(delta int64) {
sb.lk.Lock()
defer sb.lk.Unlock()

sb.powerDelta += delta
}

func (sb *sandbox) PowerDelta() int64 {
sb.lk.RLock()
defer sb.lk.RUnlock()

return sb.powerDelta
}

// VerifyProof verifies proof of a sortition transaction.
func (sb *sandbox) VerifyProof(stamp hash.Stamp, proof sortition.Proof, val *validator.Validator) bool {
_, b := sb.store.RecentBlockByStamp(stamp)
if b == nil {
return false
}
seed := b.Header().SortitionSeed()
total := int64(0) // TODO: we can get it from state
sb.store.IterateValidators(func(val *validator.Validator) bool {
total += val.Power()
return false
})
return sortition.VerifyProof(seed, proof, val.PublicKey(), total, val.Power())
return sortition.VerifyProof(seed, proof, val.PublicKey(), sb.totalPower, val.Power())
}
Loading