Skip to content

Commit

Permalink
Initialize round by any B/O who has the initializeRound flag set to true
Browse files Browse the repository at this point in the history
  • Loading branch information
leszko committed Apr 22, 2024
1 parent 9305333 commit 4b6368e
Show file tree
Hide file tree
Showing 2 changed files with 2 additions and 177 deletions.
69 changes: 0 additions & 69 deletions eth/roundinitializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"sync"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
"github.com/golang/glog"
)
Expand Down Expand Up @@ -104,23 +103,7 @@ func (r *RoundInitializer) tryInitialize() error {
r.mu.Lock()
defer r.mu.Unlock()

currentL1Blk := r.tw.LastSeenL1Block()
lastInitializedL1BlkHash := r.tw.LastInitializedL1BlockHash()

epochSeed := r.currentEpochSeed(currentL1Blk, r.nextRoundStartL1Block, lastInitializedL1BlkHash)

ok, err := r.shouldInitialize(epochSeed)
if err != nil {
return err
}

// Noop if the caller should not initialize the round
if !ok {
return nil
}

currentRound := new(big.Int).Add(r.tw.LastInitializedRound(), big.NewInt(1))

glog.Infof("New round - preparing to initialize round to join active set, current round is %d", currentRound)

tx, err := r.client.InitializeRound()
Expand All @@ -136,55 +119,3 @@ func (r *RoundInitializer) tryInitialize() error {

return nil
}

func (r *RoundInitializer) shouldInitialize(epochSeed *big.Int) (bool, error) {
transcoders, err := r.client.TranscoderPool()
if err != nil {
return false, err
}

numActive := big.NewInt(int64(len(transcoders)))

// Should not initialize if the upcoming active set is empty
if numActive.Cmp(big.NewInt(0)) == 0 {
return false, nil
}

// Find the caller's rank in the upcoming active set
rank := int64(-1)
maxRank := numActive.Int64()
caller := r.client.Account().Address
for i := int64(0); i < maxRank; i++ {
if transcoders[i].Address == caller {
rank = i
break
}
}

// Should not initialize if the caller is not in the upcoming active set
if rank == -1 {
return false, nil
}

// Use the seed to select a position within the active set
selection := new(big.Int).Mod(epochSeed, numActive)
// Should not initialize if the selection does not match the caller's rank in the active set
if selection.Int64() != int64(rank) {
return false, nil
}

// If the selection matches the caller's rank the caller should initialize the round
return true, nil
}

// Returns the seed used to select a round initializer in the current epoch for the current round
// This seed is not meant to be unpredictable. The only requirement for the seed is that it is calculated the same way for each
// party running the round initializer
func (r *RoundInitializer) currentEpochSeed(currentL1Block, roundStartL1Block *big.Int, lastInitializedL1BlkHash [32]byte) *big.Int {
epochNum := new(big.Int).Sub(currentL1Block, roundStartL1Block)
epochNum.Div(epochNum, epochL1Blocks)

// The seed for the current epoch is calculated as:
// keccak256(lastInitializedL1BlkHash | epochNum)
return crypto.Keccak256Hash(append(lastInitializedL1BlkHash[:], epochNum.Bytes()...)).Big()
}
110 changes: 2 additions & 108 deletions eth/roundinitializer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,84 +16,6 @@ import (
"github.com/stretchr/testify/mock"
)

func TestRoundInitializer_CurrentEpochSeed(t *testing.T) {
initializer := NewRoundInitializer(nil, nil)

assert := assert.New(t)

// Test epochNum = 0
blkHash := [32]byte{123}

epochSeed := initializer.currentEpochSeed(big.NewInt(5), big.NewInt(5), blkHash)
// epochNum = (5 - 5) / 5 = 0
// epochSeed = keccak256(blkHash | 0) = 53205358842179480591542570540016728811976439286094436690881169143335261643310
expEpochSeed, _ := new(big.Int).SetString("53205358842179480591542570540016728811976439286094436690881169143335261643310", 10)
assert.Equal(expEpochSeed, epochSeed)

// Test epochNum > 0
epochSeed = initializer.currentEpochSeed(big.NewInt(20), big.NewInt(5), blkHash)
// epochNum = (20 - 5) / 5 = 3
// epochSeed = keccak256(blkHash | 3) = 42541119854153860846042329644941941146216657514071318786342840580076059276721
expEpochSeed.SetString("42541119854153860846042329644941941146216657514071318786342840580076059276721", 10)
assert.Equal(expEpochSeed, epochSeed)

// Test epochNum > 0 with some # of blocks into the epoch
epochSeed = initializer.currentEpochSeed(big.NewInt(20), big.NewInt(4), blkHash)
// epochNum = (20 - 4) / 5 = 3.2 -> 3
assert.Equal(expEpochSeed, epochSeed)
}

func TestRoundInitializer_ShouldInitialize(t *testing.T) {
client := &MockClient{}
tw := &stubTimeWatcher{}
initializer := NewRoundInitializer(client, tw)

assert := assert.New(t)

// Test error getting transcoders
expErr := errors.New("TranscoderPool error")
client.On("TranscoderPool").Return(nil, expErr).Once()

ok, err := initializer.shouldInitialize(nil)
assert.EqualError(err, expErr.Error())
assert.False(ok)

// Test active set is empty because no registered transcoders
client.On("TranscoderPool").Return([]*lpTypes.Transcoder{}, nil).Once()
ok, err = initializer.shouldInitialize(nil)
assert.Nil(err)
assert.False(ok)

// Test that caller is not in active set because it is not registered
caller := ethcommon.BytesToAddress([]byte("foo"))
client.On("Account").Return(accounts.Account{Address: caller})

registered := []*lpTypes.Transcoder{
{Address: ethcommon.BytesToAddress([]byte("jar"))},
{Address: ethcommon.BytesToAddress([]byte("bar"))},
}
client.On("TranscoderPool").Return(registered, nil).Once()

ok, err = initializer.shouldInitialize(nil)
assert.Nil(err)
assert.False(ok)

// Test not selected
registered = append(registered, &lpTypes.Transcoder{Address: caller})
client.On("TranscoderPool").Return(registered, nil)

seed := big.NewInt(3)
ok, err = initializer.shouldInitialize(seed)
assert.Nil(err)
assert.False(ok)

// Test caller selected
seed = big.NewInt(5)
ok, err = initializer.shouldInitialize(seed)
assert.Nil(err)
assert.True(ok)
}

func TestRoundInitializer_TryInitialize(t *testing.T) {
client := &MockClient{}
tw := &stubTimeWatcher{
Expand All @@ -105,41 +27,13 @@ func TestRoundInitializer_TryInitialize(t *testing.T) {
initializer.nextRoundStartL1Block = big.NewInt(5)
assert := assert.New(t)

// Test error checking should initialize
expErr := errors.New("shouldInitialize error")
client.On("TranscoderPool").Return(nil, expErr).Once()

err := initializer.tryInitialize()
assert.EqualError(err, expErr.Error())

// Test should not initialize
caller := ethcommon.BytesToAddress([]byte("foo"))
client.On("Account").Return(accounts.Account{Address: caller})

registered := []*lpTypes.Transcoder{
{Address: ethcommon.BytesToAddress([]byte("jar"))},
}
client.On("TranscoderPool").Return(registered, nil).Once()

err = initializer.tryInitialize()
assert.Nil(err)

// Test error when submitting initialization tx
registered = []*lpTypes.Transcoder{{Address: caller}}
client.On("TranscoderPool").Return(registered, nil)
expErr = errors.New("InitializeRound error")
client.On("InitializeRound").Return(nil, expErr).Once()

err = initializer.tryInitialize()
assert.EqualError(err, expErr.Error())

// Test error checking initialization tx
tx := &types.Transaction{}
client.On("InitializeRound").Return(tx, nil)
expErr = errors.New("CheckTx error")
expErr := errors.New("CheckTx error")
client.On("CheckTx", mock.Anything).Return(expErr).Once()

err = initializer.tryInitialize()
err := initializer.tryInitialize()
assert.EqualError(err, expErr.Error())

// Test success
Expand Down

0 comments on commit 4b6368e

Please sign in to comment.