Skip to content

Commit 65bf4db

Browse files
committed
Add storageGetter and tests
1 parent 4025f4f commit 65bf4db

File tree

2 files changed

+234
-9
lines changed

2 files changed

+234
-9
lines changed

rocketpool/watchtower/submit-network-balances.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ const (
3838
networkBalanceSubmissionKey string = "network.balances.submitted.node"
3939
)
4040

41+
// storageGetter is an interface that allows us to inject a stub storage getter
42+
type storageGetter interface {
43+
GetBool(opts *bind.CallOpts, _key [32]byte) (bool, error)
44+
}
45+
4146
// Submit network balances task
4247
type submitNetworkBalances struct {
4348
c *cli.Command
@@ -48,6 +53,7 @@ type submitNetworkBalances struct {
4853
ec rocketpool.ExecutionClient
4954
rp *rocketpool.RocketPool
5055
bc beacon.Client
56+
storage storageGetter
5157
lock *sync.Mutex
5258
isRunning bool
5359
}
@@ -121,6 +127,7 @@ func newSubmitNetworkBalances(c *cli.Command, logger log.ColorLogger, errorLogge
121127
ec: ec,
122128
rp: rp,
123129
bc: bc,
130+
storage: rp.RocketStorage,
124131
lock: lock,
125132
isRunning: false,
126133
}, nil
@@ -317,18 +324,13 @@ func (t *submitNetworkBalances) handleError(err error) {
317324
t.lock.Unlock()
318325
}
319326

320-
// Check whether balances for a block has already been submitted by the node
321-
func (t *submitNetworkBalances) hasSubmittedBlockBalances(nodeAddress common.Address, blockNumber uint64) (bool, error) {
322-
327+
func blockBalancesKey(nodeAddress common.Address, blockNumber uint64) [32]byte {
323328
blockNumberBuf := make([]byte, 32)
324329
big.NewInt(int64(blockNumber)).FillBytes(blockNumberBuf)
325-
return t.rp.RocketStorage.GetBool(nil, crypto.Keccak256Hash([]byte(networkBalanceSubmissionKey), nodeAddress.Bytes(), blockNumberBuf))
326-
330+
return crypto.Keccak256Hash([]byte(networkBalanceSubmissionKey), nodeAddress.Bytes(), blockNumberBuf)
327331
}
328332

329-
// Check whether specific balances for a block has already been submitted by the node
330-
func (t *submitNetworkBalances) hasSubmittedSpecificBlockBalances(nodeAddress common.Address, blockNumber uint64, balances networkBalances) (bool, error) {
331-
333+
func specificBlockBalancesKey(nodeAddress common.Address, blockNumber uint64, balances networkBalances) [32]byte {
332334
blockNumberBuf := make([]byte, 32)
333335
big.NewInt(int64(blockNumber)).FillBytes(blockNumberBuf)
334336

@@ -344,7 +346,20 @@ func (t *submitNetworkBalances) hasSubmittedSpecificBlockBalances(nodeAddress co
344346
rethSupplyBuf := make([]byte, 32)
345347
balances.RETHSupply.FillBytes(rethSupplyBuf)
346348

347-
return t.rp.RocketStorage.GetBool(nil, crypto.Keccak256Hash([]byte(networkBalanceSubmissionKey), nodeAddress.Bytes(), blockNumberBuf, slotTimestampBuf, totalEthBuf, stakingBuf, rethSupplyBuf))
349+
return crypto.Keccak256Hash([]byte(networkBalanceSubmissionKey), nodeAddress.Bytes(), blockNumberBuf, slotTimestampBuf, totalEthBuf, stakingBuf, rethSupplyBuf)
350+
}
351+
352+
// Check whether balances for a block has already been submitted by the node
353+
func (t *submitNetworkBalances) hasSubmittedBlockBalances(nodeAddress common.Address, blockNumber uint64) (bool, error) {
354+
355+
return t.storage.GetBool(nil, blockBalancesKey(nodeAddress, blockNumber))
356+
357+
}
358+
359+
// Check whether specific balances for a block has already been submitted by the node
360+
func (t *submitNetworkBalances) hasSubmittedSpecificBlockBalances(nodeAddress common.Address, blockNumber uint64, balances networkBalances) (bool, error) {
361+
362+
return t.storage.GetBool(nil, specificBlockBalancesKey(nodeAddress, blockNumber, balances))
348363

349364
}
350365

rocketpool/watchtower/submit-network-balances_test.go

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package watchtower
22

33
import (
44
"context"
5+
"fmt"
56
"math/big"
67
"strconv"
78
"testing"
89
"time"
910

1011
"github.com/ethereum/go-ethereum"
12+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1113
"github.com/ethereum/go-ethereum/common"
1214
"github.com/ethereum/go-ethereum/core/types"
1315
"github.com/rocket-pool/smartnode/bindings/rocketpool"
@@ -771,3 +773,211 @@ func TestFindNextSubmissionTarget_AlreadySubmittedForBlock(t *testing.T) {
771773
t.Error("expected valid=false when targetBlockNumber <= lastSubmissionBlock")
772774
}
773775
}
776+
777+
// ============================================================
778+
// hasSubmittedBlockBalances / hasSubmittedSpecificBlockBalances
779+
// ============================================================
780+
781+
// stubStorage is an in-memory storageGetter used by tests.
782+
type stubStorage struct {
783+
values map[[32]byte]bool
784+
err error
785+
}
786+
787+
func (s *stubStorage) GetBool(_ *bind.CallOpts, key [32]byte) (bool, error) {
788+
if s.err != nil {
789+
return false, s.err
790+
}
791+
return s.values[key], nil
792+
}
793+
794+
func TestHasSubmittedBlockBalances_NotSubmitted(t *testing.T) {
795+
storage := &stubStorage{values: map[[32]byte]bool{}}
796+
task := &submitNetworkBalances{storage: storage}
797+
798+
got, err := task.hasSubmittedBlockBalances(common.HexToAddress("0x1234"), 12345)
799+
if err != nil {
800+
t.Fatalf("unexpected error: %v", err)
801+
}
802+
if got {
803+
t.Error("expected false when the block has not been submitted")
804+
}
805+
}
806+
807+
func TestHasSubmittedBlockBalances_Submitted(t *testing.T) {
808+
nodeAddr := common.HexToAddress("0x1234")
809+
blockNumber := uint64(12345)
810+
key := blockBalancesKey(nodeAddr, blockNumber)
811+
812+
storage := &stubStorage{values: map[[32]byte]bool{key: true}}
813+
task := &submitNetworkBalances{storage: storage}
814+
815+
got, err := task.hasSubmittedBlockBalances(nodeAddr, blockNumber)
816+
if err != nil {
817+
t.Fatalf("unexpected error: %v", err)
818+
}
819+
if !got {
820+
t.Error("expected true when the block has been submitted")
821+
}
822+
}
823+
824+
func TestHasSubmittedBlockBalances_DifferentNodeOrBlock(t *testing.T) {
825+
nodeAddr := common.HexToAddress("0xAAAA")
826+
blockNumber := uint64(500)
827+
key := blockBalancesKey(nodeAddr, blockNumber)
828+
829+
storage := &stubStorage{values: map[[32]byte]bool{key: true}}
830+
task := &submitNetworkBalances{storage: storage}
831+
832+
// Same block, different node → should not match
833+
gotOtherNode, err := task.hasSubmittedBlockBalances(common.HexToAddress("0xBBBB"), blockNumber)
834+
if err != nil {
835+
t.Fatalf("unexpected error: %v", err)
836+
}
837+
if gotOtherNode {
838+
t.Error("expected false for a different node address")
839+
}
840+
841+
// Same node, different block → should not match
842+
gotOtherBlock, err := task.hasSubmittedBlockBalances(nodeAddr, blockNumber+1)
843+
if err != nil {
844+
t.Fatalf("unexpected error: %v", err)
845+
}
846+
if gotOtherBlock {
847+
t.Error("expected false for a different block number")
848+
}
849+
}
850+
851+
func TestHasSubmittedBlockBalances_PropagatesError(t *testing.T) {
852+
storage := &stubStorage{err: fmt.Errorf("storage unavailable")}
853+
task := &submitNetworkBalances{storage: storage}
854+
855+
_, err := task.hasSubmittedBlockBalances(common.HexToAddress("0x1"), 1)
856+
if err == nil {
857+
t.Error("expected error to be propagated from storage")
858+
}
859+
}
860+
861+
func TestHasSubmittedSpecificBlockBalances_NotSubmitted(t *testing.T) {
862+
storage := &stubStorage{values: map[[32]byte]bool{}}
863+
task := &submitNetworkBalances{storage: storage}
864+
865+
b := newNetworkBalances()
866+
b.ClampedTotalBalanceWei = ethToWei(500)
867+
b.TotalStaking = ethToWei(300)
868+
b.SlotTimestamp = 1_234_567_890
869+
870+
got, err := task.hasSubmittedSpecificBlockBalances(common.HexToAddress("0xabcd"), 99, b)
871+
if err != nil {
872+
t.Fatalf("unexpected error: %v", err)
873+
}
874+
if got {
875+
t.Error("expected false when specific balances have not been submitted")
876+
}
877+
}
878+
879+
func TestHasSubmittedSpecificBlockBalances_Submitted(t *testing.T) {
880+
nodeAddr := common.HexToAddress("0xabcd")
881+
blockNumber := uint64(99)
882+
883+
b := newNetworkBalances()
884+
b.ClampedTotalBalanceWei = ethToWei(500)
885+
b.TotalStaking = ethToWei(300)
886+
b.SlotTimestamp = 1_234_567_890
887+
888+
key := specificBlockBalancesKey(nodeAddr, blockNumber, b)
889+
storage := &stubStorage{values: map[[32]byte]bool{key: true}}
890+
task := &submitNetworkBalances{storage: storage}
891+
892+
got, err := task.hasSubmittedSpecificBlockBalances(nodeAddr, blockNumber, b)
893+
if err != nil {
894+
t.Fatalf("unexpected error: %v", err)
895+
}
896+
if !got {
897+
t.Error("expected true when the exact same balances were previously submitted")
898+
}
899+
}
900+
901+
func TestHasSubmittedSpecificBlockBalances_DifferentValues(t *testing.T) {
902+
nodeAddr := common.HexToAddress("0xabcd")
903+
blockNumber := uint64(99)
904+
905+
submitted := newNetworkBalances()
906+
submitted.ClampedTotalBalanceWei = ethToWei(500)
907+
submitted.TotalStaking = ethToWei(300)
908+
submitted.RETHSupply = ethToWei(400)
909+
submitted.SlotTimestamp = 1_000
910+
911+
// Store the specific key for the original submitted values.
912+
submittedKey := specificBlockBalancesKey(nodeAddr, blockNumber, submitted)
913+
storage := &stubStorage{values: map[[32]byte]bool{submittedKey: true}}
914+
task := &submitNetworkBalances{storage: storage}
915+
916+
// Confirm the original values return true (sanity check).
917+
gotOriginal, err := task.hasSubmittedSpecificBlockBalances(nodeAddr, blockNumber, submitted)
918+
if err != nil {
919+
t.Fatalf("unexpected error on original: %v", err)
920+
}
921+
if !gotOriginal {
922+
t.Fatal("sanity check failed: expected true for the originally submitted values")
923+
}
924+
925+
// Change TotalStaking → key no longer matches.
926+
altered := submitted
927+
altered.TotalStaking = new(big.Int).Set(ethToWei(301))
928+
929+
got, err := task.hasSubmittedSpecificBlockBalances(nodeAddr, blockNumber, altered)
930+
if err != nil {
931+
t.Fatalf("unexpected error: %v", err)
932+
}
933+
if got {
934+
t.Error("expected false when TotalStaking differs from what was submitted")
935+
}
936+
}
937+
938+
// TestHasSubmittedSpecificVsBlock verifies that the two functions use distinct
939+
// storage keys: setting the block-level key does not satisfy the specific check.
940+
func TestHasSubmittedSpecificVsBlock(t *testing.T) {
941+
nodeAddr := common.HexToAddress("0x5678")
942+
blockNumber := uint64(42)
943+
944+
b := newNetworkBalances()
945+
b.ClampedTotalBalanceWei = ethToWei(100)
946+
b.TotalStaking = ethToWei(50)
947+
b.SlotTimestamp = 9999
948+
949+
// Only set the block-level key.
950+
blockKey := blockBalancesKey(nodeAddr, blockNumber)
951+
storage := &stubStorage{values: map[[32]byte]bool{blockKey: true}}
952+
task := &submitNetworkBalances{storage: storage}
953+
954+
hasBlock, err := task.hasSubmittedBlockBalances(nodeAddr, blockNumber)
955+
if err != nil {
956+
t.Fatalf("unexpected error: %v", err)
957+
}
958+
hasSpecific, err := task.hasSubmittedSpecificBlockBalances(nodeAddr, blockNumber, b)
959+
if err != nil {
960+
t.Fatalf("unexpected error: %v", err)
961+
}
962+
963+
if !hasBlock {
964+
t.Error("expected hasSubmittedBlockBalances=true")
965+
}
966+
if hasSpecific {
967+
t.Error("expected hasSubmittedSpecificBlockBalances=false (key is different from block-level key)")
968+
}
969+
}
970+
971+
func TestHasSubmittedSpecificBlockBalances_PropagatesError(t *testing.T) {
972+
storage := &stubStorage{err: fmt.Errorf("storage unavailable")}
973+
task := &submitNetworkBalances{storage: storage}
974+
975+
b := newNetworkBalances()
976+
b.ClampedTotalBalanceWei = big.NewInt(1)
977+
b.TotalStaking = big.NewInt(0)
978+
979+
_, err := task.hasSubmittedSpecificBlockBalances(common.HexToAddress("0x1"), 1, b)
980+
if err == nil {
981+
t.Error("expected error to be propagated from storage")
982+
}
983+
}

0 commit comments

Comments
 (0)