From f3548c1d7c016adc04c4b0812efffded5a0058e5 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Fri, 16 Aug 2019 08:39:08 +0200 Subject: [PATCH 01/38] eth: senderwatcher with cache Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 341b39a0c53c05203d4e7b01d887a12ace1ebd89 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 218 +++++++++++ eth/watchers/senderwatcher_test.go | 576 +++++++++++++++++++++++++++++ 2 files changed, 794 insertions(+) create mode 100644 eth/watchers/senderwatcher.go create mode 100644 eth/watchers/senderwatcher_test.go diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go new file mode 100644 index 0000000..931889f --- /dev/null +++ b/eth/watchers/senderwatcher.go @@ -0,0 +1,218 @@ +package watchers + +import ( + "fmt" + "math/big" + "sync" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/golang/glog" + "github.com/livepeer/go-livepeer/eth" + "github.com/livepeer/go-livepeer/eth/blockwatch" + "github.com/livepeer/go-livepeer/eth/contracts" + "github.com/livepeer/go-livepeer/pm" +) + +// SenderWatcher maintains a concurrency-safe map with SenderInfo +type SenderWatcher struct { + senders map[ethcommon.Address]*pm.SenderInfo + mu sync.RWMutex + quit chan struct{} + watcher BlockWatcher + lpEth eth.LivepeerEthClient + dec *EventDecoder +} + +// NewSenderWatcher initiates a new SenderWatcher +func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient) (*SenderWatcher, error) { + dec, err := NewEventDecoder(ticketBrokerAddr, contracts.TicketBrokerABI) + if err != nil { + return nil, err + } + return &SenderWatcher{ + quit: make(chan struct{}), + watcher: watcher, + lpEth: lpEth, + senders: make(map[ethcommon.Address]*pm.SenderInfo), + dec: dec, + }, nil +} + +// GetSenderInfo returns the senderInfo for a sender +// if values for a sender are not cached an RPC call to a remote ethereum node will be made to initialize the cache +func (sw *SenderWatcher) GetSenderInfo(addr ethcommon.Address) (*pm.SenderInfo, error) { + sw.mu.RLock() + cache := sw.senders[addr] + sw.mu.RUnlock() + if cache == nil { + info, err := sw.lpEth.GetSenderInfo(addr) + if err != nil { + return nil, fmt.Errorf("GetSenderInfo RPC call to remote node failed: %v", err) + } + sw.setSenderInfo(addr, info) + return info, nil + } + return cache, nil +} + +func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderInfo) { + sw.mu.Lock() + defer sw.mu.Unlock() + sw.senders[addr] = info +} + +// Watch starts the event watching loop +func (sw *SenderWatcher) Watch() { + events := make(chan []*blockwatch.Event, 10) + sub := sw.watcher.Subscribe(events) + defer sub.Unsubscribe() + for { + select { + case <-sw.quit: + return + case err := <-sub.Err(): + glog.Error(err) + case events := <-events: + sw.handleBlockEvents(events) + } + } +} + +// Stop watching for events +func (sw *SenderWatcher) Stop() { + close(sw.quit) +} + +// Clear removes a key-value pair from the map +func (sw *SenderWatcher) Clear(addr ethcommon.Address) { + sw.mu.Lock() + defer sw.mu.Unlock() + if _, ok := sw.senders[addr]; ok { + delete(sw.senders, addr) + } +} + +func (sw *SenderWatcher) handleBlockEvents(events []*blockwatch.Event) { + for _, event := range events { + for _, log := range event.BlockHeader.Logs { + if event.Type == blockwatch.Removed { + log.Removed = true + } + if err := sw.handleLog(log); err != nil { + glog.Error(err) + } + } + } +} + +func (sw *SenderWatcher) handleLog(log types.Log) error { + eventName, err := sw.dec.FindEventName(log) + if err != nil { + // Noop if we cannot find the event name + return nil + } + + sw.mu.Lock() + defer sw.mu.Unlock() + + var sender ethcommon.Address + switch eventName { + case "DepositFunded": + var depositFunded contracts.TicketBrokerDepositFunded + if err := sw.dec.Decode("DepositFunded", log, &depositFunded); err != nil { + return fmt.Errorf("failed to decode DepositFunded event: %v", err) + } + sender = depositFunded.Sender + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.Deposit.Add(info.Deposit, depositFunded.Amount) + } + case "ReserveFunded": + var reserveFunded contracts.TicketBrokerReserveFunded + if err := sw.dec.Decode("ReserveFunded", log, &reserveFunded); err != nil { + return fmt.Errorf("failed to decode ReserveFunded event: %v", err) + } + sender = reserveFunded.ReserveHolder + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.Reserve.Add(info.Reserve, reserveFunded.Amount) + } + case "Withdrawal": + var withdrawal contracts.TicketBrokerWithdrawal + if err := sw.dec.Decode("Withdrawal", log, &withdrawal); err != nil { + return fmt.Errorf("failed to decode Withdrawal event: %v", err) + } + sender = withdrawal.Sender + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.Deposit = big.NewInt(0) + info.Reserve = big.NewInt(0) + } + case "WinningTicketTransfer": + var winningTicketTransfer contracts.TicketBrokerWinningTicketTransfer + if err := sw.dec.Decode("WinningTicketTransfer", log, &winningTicketTransfer); err != nil { + return fmt.Errorf("failed to decode WinningTicketTransfer event: %v", err) + } + amount := winningTicketTransfer.Amount + sender = winningTicketTransfer.Sender + + if info, ok := sw.senders[sender]; ok && !log.Removed { + // See if amount > deposit + if info.Deposit.Cmp(amount) < 0 { + // Draw from reserve + // ReserveFrozen will be handled in it's own event log handler + diff := new(big.Int).Sub(amount, info.Deposit) + info.Deposit = big.NewInt(0) + info.Reserve.Sub(info.Reserve, diff) + } else { + // Draw from deposit + info.Deposit.Sub(info.Deposit, amount) + } + } + case "ReserveFrozen": + // Set reserveStatus and thawround + var reserveFrozen contracts.TicketBrokerReserveFrozen + if err := sw.dec.Decode("ReserveFrozen", log, &reserveFrozen); err != nil { + return fmt.Errorf("failed to decode ReserveFrozen event: %v", err) + } + sender = reserveFrozen.ReserveHolder + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.ReserveState = pm.Frozen + // TODO: fetch freezePeriod instead of hardcoding or use a const + // TODO: how to handle unthaw + // frozen checks will have to be against ReserveFrozen and ThawRound < CurrentRound + // But how do we handle state updates when the reserve thaws? + info.ThawRound.Add(reserveFrozen.FreezeRound, big.NewInt(2)) + } + case "Unlock": + // Set withdraw block + var unlock contracts.TicketBrokerUnlock + if err := sw.dec.Decode("Unlock", log, &unlock); err != nil { + return fmt.Errorf("failed to decode Unlock event: %v", err) + } + sender = unlock.Sender + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.WithdrawBlock = unlock.EndBlock + } + case "UnlockCancelled": + // Unset withdrawblock + var unlockCancelled contracts.TicketBrokerUnlockCancelled + if err := sw.dec.Decode("UnlockCancelled", log, &unlockCancelled); err != nil { + return fmt.Errorf("failed to decode UnlockCancelled event: %v", err) + } + sender = unlockCancelled.Sender + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.WithdrawBlock = big.NewInt(0) + } + default: + return nil + } + + if _, ok := sw.senders[sender]; ok && log.Removed { + info, err := sw.lpEth.GetSenderInfo(sender) + if err != nil { + return fmt.Errorf("GetSenderInfo RPC call to remote node failed: %v", err) + } + sw.senders[sender] = info + } + + return nil +} diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go new file mode 100644 index 0000000..65a19d2 --- /dev/null +++ b/eth/watchers/senderwatcher_test.go @@ -0,0 +1,576 @@ +package watchers + +import ( + "math/big" + "testing" + "time" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/livepeer/go-livepeer/eth" + "github.com/livepeer/go-livepeer/eth/blockwatch" + "github.com/livepeer/go-livepeer/pm" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetAndSetSenderInfo(t *testing.T) { + assert := assert.New(t) + sw := &SenderWatcher{ + senders: make(map[ethcommon.Address]*pm.SenderInfo), + lpEth: ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + }, + }, + } + + info := &pm.SenderInfo{ + Deposit: big.NewInt(1000000000000000000), + Reserve: big.NewInt(1000000000000000000), + ReserveState: pm.NotFrozen, + } + sender := pm.RandAddress() + + sw.setSenderInfo(sender, info) + entry, err := sw.GetSenderInfo(sender) + assert.Nil(err) + assert.Equal(sw.senders[sender], info) + assert.Equal(info, entry) + + // If map entry is nil and we call get, make RPC call and set the returned data + sw.Clear(sender) + info, err = sw.GetSenderInfo(sender) + assert.Nil(err) + assert.Zero(info.Deposit.Cmp(big.NewInt(10))) + assert.Zero(info.Reserve.Cmp(big.NewInt(5))) + info = sw.senders[sender] + assert.Zero(info.Deposit.Cmp(big.NewInt(10))) + assert.Zero(info.Reserve.Cmp(big.NewInt(5))) +} + +func TestSenderWatcher_Clear(t *testing.T) { + assert := assert.New(t) + stubClientSenderInfo := &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + } + sw := &SenderWatcher{ + senders: make(map[ethcommon.Address]*pm.SenderInfo), + lpEth: ð.StubClient{ + SenderInfo: stubClientSenderInfo, + }, + } + info := &pm.SenderInfo{ + Deposit: big.NewInt(1000000000000000000), + Reserve: big.NewInt(1000000000000000000), + ReserveState: pm.NotFrozen, + } + sender := pm.RandAddress() + + sw.setSenderInfo(sender, info) + + getInfo, err := sw.GetSenderInfo(sender) + assert.Nil(err) + assert.Equal(info, getInfo) + + sw.Clear(sender) + + getInfo = sw.senders[sender] + assert.Nil(getInfo) + // on calling GetSenderInfo should be back to RPC stub values + getInfo, err = sw.GetSenderInfo(sender) + assert.Nil(err) + assert.Equal(getInfo, stubClientSenderInfo) +} + +func TestSenderWatcher_WatchAndStop(t *testing.T) { + assert := assert.New(t) + lpEth := ð.StubClient{} + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + go sw.Watch() + time.Sleep(2 * time.Millisecond) + sw.Stop() + time.Sleep(2 * time.Millisecond) + assert.True(watcher.sub.unsubscribed) +} + +func TestSenderWatcher_HandleLog(t *testing.T) { + lpEth := ð.StubClient{} + watcher := &stubBlockWatcher{} + rw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + require.Nil(t, err) + + assert := assert.New(t) + + // Test unknown event + log := newStubBaseLog() + log.Topics = []ethcommon.Hash{ethcommon.BytesToHash([]byte("foo"))} + + err = rw.handleLog(log) + assert.Nil(err) +} + +func TestFundDepositEvent(t *testing.T) { + assert := assert.New(t) + startDeposit := big.NewInt(10) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newDepositEvent := newStubDepositFundedLog() + header.Logs = append(header.Logs, newDepositEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Zero(info.Deposit.Cmp(startDeposit)) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + expectedDeposit := new(big.Int).Add(startDeposit, new(big.Int).SetBytes(newDepositEvent.Data)) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Deposit.Cmp(expectedDeposit)) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newDepositEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestFundReserveEvent(t *testing.T) { + assert := assert.New(t) + startDeposit := big.NewInt(10) + startReserve := big.NewInt(5) + startThawR := big.NewInt(1) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + ThawRound: big.NewInt(1), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newReserveEvent := newStubReserveFundedLog() + header.Logs = append(header.Logs, newReserveEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Equal(info.Deposit, startDeposit) + assert.Equal(info.Reserve, startReserve) + assert.Equal(info.ThawRound, startThawR) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + expectedReserve := new(big.Int).Add(startReserve, new(big.Int).SetBytes(newReserveEvent.Data)) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Reserve.Cmp(expectedReserve)) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newReserveEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestWithdrawalEvent(t *testing.T) { + assert := assert.New(t) + startDeposit := big.NewInt(10) + startReserve := big.NewInt(5) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newWithdrawalEvent := newStubWithdrawalLog() + header.Logs = append(header.Logs, newWithdrawalEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Zero(info.Deposit.Cmp(startDeposit)) + assert.Zero(info.Reserve.Cmp(startReserve)) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + time.Sleep(2 * time.Millisecond) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Deposit.Cmp(big.NewInt(0))) + assert.Zero(info.Reserve.Cmp(big.NewInt(0))) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newWithdrawalEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestWinningTicketTransferEvent(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + deposit, _ := new(big.Int).SetString("10000000000000000000", 10) + reserve, _ := new(big.Int).SetString("5000000000000000000", 10) + startDeposit, _ := new(big.Int).SetString("10000000000000000000", 10) + startReserve, _ := new(big.Int).SetString("5000000000000000000", 10) + senderInfo := &pm.SenderInfo{ + Deposit: deposit, + Reserve: reserve, + } + lpEth := ð.StubClient{ + SenderInfo: senderInfo, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + require.Nil(err) + + header := defaultMiniHeader() + newWinningTicketEvent := newStubWinningTicketLog() + header.Logs = append(header.Logs, newWinningTicketEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok = sw.senders[stubSender] + assert.True(ok) + assert.Equal(info.Deposit, startDeposit) + assert.Equal(info.Reserve, startReserve) + + // If map entry exists parse event log + // ticket amount is 200 gwei + // init the map entry + blockEvent.Type = blockwatch.Added + faceValue := big.NewInt(200000000000) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(startDeposit.Sub(startDeposit, faceValue).Cmp(info.Deposit)) + assert.Zero(startReserve.Cmp(info.Reserve)) + + // Test facevalue > deposit + senderInfo.Deposit = big.NewInt(100000000000) + info, err = sw.GetSenderInfo(stubSender) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Deposit.Int64()) + diff := new(big.Int).Sub(faceValue, big.NewInt(100000000000)) + expectedReserve := new(big.Int).Sub(startReserve, diff) + assert.Zero(expectedReserve.Cmp(info.Reserve)) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newWinningTicketEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestReserveFrozenEvent(t *testing.T) { + assert := assert.New(t) + startThawR := big.NewInt(10) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + ThawRound: big.NewInt(10), + ReserveState: pm.Frozen, + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newReserveFrozenEvent := newStubReserveFrozenLog() + header.Logs = append(header.Logs, newReserveFrozenEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Equal(info.ReserveState, pm.Frozen) + assert.Zero(startThawR.Cmp(info.ThawRound)) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Equal(info.ReserveState, pm.Frozen) + expectedThawRound := new(big.Int).Add(new(big.Int).SetBytes(newReserveFrozenEvent.Data[:32]), big.NewInt(2)) + assert.Zero(expectedThawRound.Cmp(info.ThawRound)) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newReserveFrozenEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestUnlockEvent(t *testing.T) { + assert := assert.New(t) + startWithdrawBlock := big.NewInt(5) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + WithdrawBlock: big.NewInt(5), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newUnlockEvent := newStubUnlockLog() + header.Logs = append(header.Logs, newUnlockEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Zero(info.WithdrawBlock.Cmp(startWithdrawBlock)) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.WithdrawBlock.Cmp(big.NewInt(150))) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newUnlockEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestUnlockCancelledEvent(t *testing.T) { + assert := assert.New(t) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + WithdrawBlock: big.NewInt(10), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newUnlockCancelledEvent := newStubUnlockCancelledLog() + header.Logs = append(header.Logs, newUnlockCancelledEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Zero(info.WithdrawBlock.Cmp(big.NewInt(10))) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.WithdrawBlock.Int64()) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newUnlockCancelledEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} From e9ce98442f5e7cf86d0937a0ae7f86c3c1799407 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 29 Aug 2019 21:51:47 +0200 Subject: [PATCH 02/38] pm, eth: calculate reserveAlloc from SenderWatcher cache Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 6a283a135e40e6738b06b4152ee56c44b75ab32d Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 40 +++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 931889f..9ca5dc2 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -16,12 +16,13 @@ import ( // SenderWatcher maintains a concurrency-safe map with SenderInfo type SenderWatcher struct { - senders map[ethcommon.Address]*pm.SenderInfo - mu sync.RWMutex - quit chan struct{} - watcher BlockWatcher - lpEth eth.LivepeerEthClient - dec *EventDecoder + senders map[ethcommon.Address]*pm.SenderInfo + claimedReserve map[ethcommon.Address]*big.Int // map representing how much a recipient has drawn from a sender's reserve + mu sync.RWMutex + quit chan struct{} + watcher BlockWatcher + lpEth eth.LivepeerEthClient + dec *EventDecoder } // NewSenderWatcher initiates a new SenderWatcher @@ -62,6 +63,13 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn sw.senders[addr] = info } +// ClaimedReserve returns the amount claimed from a sender's reserve +func (sw *SenderWatcher) ClaimedReserve(sender ethcommon.Address) *big.Int { + sw.mu.RLock() + defer sw.mu.RUnlock() + return sw.claimedReserve[sender] +} + // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { events := make(chan []*blockwatch.Event, 10) @@ -135,6 +143,19 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { sender = reserveFunded.ReserveHolder if info, ok := sw.senders[sender]; ok && !log.Removed { info.Reserve.Add(info.Reserve, reserveFunded.Amount) + // if a thawed reserve is funded we flush claimedReserve for a sender and set it to NotFrozen + // making an RPC call here is suboptimal but doesn't happen frequently + var currentRound *big.Int + if info.ThawRound.Int64() != 0 { + currentRound, err = sw.lpEth.CurrentRound() + if err != nil { + return err + } + if info.ThawRound.Cmp(currentRound) < 0 { + info.ReserveState = pm.NotFrozen + sw.claimedReserve[sender] = big.NewInt(0) + } + } } case "Withdrawal": var withdrawal contracts.TicketBrokerWithdrawal @@ -161,7 +182,12 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { // ReserveFrozen will be handled in it's own event log handler diff := new(big.Int).Sub(amount, info.Deposit) info.Deposit = big.NewInt(0) - info.Reserve.Sub(info.Reserve, diff) + // sw.senders[sender].Reserve.Sub(sw.senders[sender].Reserve, amount) + if claimed, ok := sw.claimedReserve[sender]; ok { + claimed.Add(claimed, diff) + } else { + claimed = diff + } } else { // Draw from deposit info.Deposit.Sub(info.Deposit, amount) From ca9d6df3efc65aa5666829a0e872a4e9278d4dd6 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Tue, 3 Sep 2019 14:01:18 +0200 Subject: [PATCH 03/38] eth/watchers: track claimedReserve on SenderWatcher Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 57e28870688c191b0d669d3a1776f848348ef470 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 32 ++++++++++++++----- eth/watchers/senderwatcher_test.go | 49 ++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 9ca5dc2..095c06a 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -32,11 +32,12 @@ func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, return nil, err } return &SenderWatcher{ - quit: make(chan struct{}), - watcher: watcher, - lpEth: lpEth, - senders: make(map[ethcommon.Address]*pm.SenderInfo), - dec: dec, + quit: make(chan struct{}), + watcher: watcher, + lpEth: lpEth, + senders: make(map[ethcommon.Address]*pm.SenderInfo), + claimedReserve: make(map[ethcommon.Address]*big.Int), + dec: dec, }, nil } @@ -64,10 +65,21 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn } // ClaimedReserve returns the amount claimed from a sender's reserve -func (sw *SenderWatcher) ClaimedReserve(sender ethcommon.Address) *big.Int { +func (sw *SenderWatcher) ClaimedReserve(sender ethcommon.Address, claimant ethcommon.Address) (*big.Int, error) { sw.mu.RLock() - defer sw.mu.RUnlock() - return sw.claimedReserve[sender] + claimed := sw.claimedReserve[sender] + sw.mu.RUnlock() + if claimed != nil { + return claimed, nil + } + claimed, err := sw.lpEth.ClaimedReserve(claimant, sender) + if err != nil { + return nil, fmt.Errorf("ClaimedReserve RPC call to remote node failed: %v", err) + } + sw.mu.Lock() + sw.claimedReserve[sender] = claimed + sw.mu.Unlock() + return claimed, nil } // Watch starts the event watching loop @@ -99,6 +111,9 @@ func (sw *SenderWatcher) Clear(addr ethcommon.Address) { if _, ok := sw.senders[addr]; ok { delete(sw.senders, addr) } + if _, ok := sw.claimedReserve[addr]; ok { + delete(sw.claimedReserve, addr) + } } func (sw *SenderWatcher) handleBlockEvents(events []*blockwatch.Event) { @@ -166,6 +181,7 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { if info, ok := sw.senders[sender]; ok && !log.Removed { info.Deposit = big.NewInt(0) info.Reserve = big.NewInt(0) + sw.claimedReserve[sender] = big.NewInt(0) } case "WinningTicketTransfer": var winningTicketTransfer contracts.TicketBrokerWinningTicketTransfer diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 65a19d2..4e9cb59 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -1,6 +1,7 @@ package watchers import ( + "errors" "math/big" "testing" "time" @@ -57,7 +58,8 @@ func TestSenderWatcher_Clear(t *testing.T) { Reserve: big.NewInt(5), } sw := &SenderWatcher{ - senders: make(map[ethcommon.Address]*pm.SenderInfo), + senders: make(map[ethcommon.Address]*pm.SenderInfo), + claimedReserve: make(map[ethcommon.Address]*big.Int), lpEth: ð.StubClient{ SenderInfo: stubClientSenderInfo, }, @@ -70,10 +72,15 @@ func TestSenderWatcher_Clear(t *testing.T) { sender := pm.RandAddress() sw.setSenderInfo(sender, info) + claimed := big.NewInt(100) + sw.claimedReserve[sender] = claimed getInfo, err := sw.GetSenderInfo(sender) assert.Nil(err) assert.Equal(info, getInfo) + getClaimed, err := sw.ClaimedReserve(sender, ethcommon.Address{}) + assert.Nil(err) + assert.Zero(claimed.Cmp(getClaimed)) sw.Clear(sender) @@ -83,6 +90,42 @@ func TestSenderWatcher_Clear(t *testing.T) { getInfo, err = sw.GetSenderInfo(sender) assert.Nil(err) assert.Equal(getInfo, stubClientSenderInfo) + getClaimed, err = sw.ClaimedReserve(sender, ethcommon.Address{}) + assert.Nil(err) + assert.Nil(getClaimed) + +} + +func TestSenderWatcher_ClaimedReserve(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + lpEth := ð.StubClient{} + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + require.Nil(err) + + // if not existant on map; make RPC call and init the map + _, ok := sw.claimedReserve[stubSender] + require.False(ok) + lpEth.ClaimedAmount = big.NewInt(5000) + claimed, err := sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(err) + assert.Zero(lpEth.ClaimedAmount.Cmp(claimed)) + + // if existant on map return the map value + sw.claimedReserve[stubSender] = big.NewInt(10000) + claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(err) + assert.Equal(big.NewInt(10000), claimed) + + // if RPC error return error + lpEth.ClaimedAmount = nil + sw.claimedReserve[stubSender] = nil + expectedErr := errors.New("ClaimedReserve RPC error") + lpEth.ClaimedReserveError = expectedErr + claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(claimed) + assert.Contains(err.Error(), expectedErr.Error()) } func TestSenderWatcher_WatchAndStop(t *testing.T) { @@ -374,11 +417,11 @@ func TestWinningTicketTransferEvent(t *testing.T) { watcher.sink <- []*blockwatch.Event{blockEvent} time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) + claimed, err := sw.ClaimedReserve(stubSender, stubClaimant) assert.Nil(err) assert.Zero(info.Deposit.Int64()) diff := new(big.Int).Sub(faceValue, big.NewInt(100000000000)) - expectedReserve := new(big.Int).Sub(startReserve, diff) - assert.Zero(expectedReserve.Cmp(info.Reserve)) + assert.Equal(claimed, diff) // If we don't care about the address, don't handle the event s := pm.RandAddress() From 73a7edfe348ce8ea1f4a793596301bc500ffa22b Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Tue, 3 Sep 2019 14:03:11 +0200 Subject: [PATCH 04/38] pm: updates to Broker and SenderManager interface Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 5b74602591c67645a422b5414d0fb052158248e4 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 095c06a..317575c 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -65,19 +65,19 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn } // ClaimedReserve returns the amount claimed from a sender's reserve -func (sw *SenderWatcher) ClaimedReserve(sender ethcommon.Address, claimant ethcommon.Address) (*big.Int, error) { +func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claimant ethcommon.Address) (*big.Int, error) { sw.mu.RLock() - claimed := sw.claimedReserve[sender] + claimed := sw.claimedReserve[reserveHolder] sw.mu.RUnlock() if claimed != nil { return claimed, nil } - claimed, err := sw.lpEth.ClaimedReserve(claimant, sender) + claimed, err := sw.lpEth.ClaimedReserve(reserveHolder, claimant) if err != nil { return nil, fmt.Errorf("ClaimedReserve RPC call to remote node failed: %v", err) } sw.mu.Lock() - sw.claimedReserve[sender] = claimed + sw.claimedReserve[reserveHolder] = claimed sw.mu.Unlock() return claimed, nil } @@ -198,11 +198,10 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { // ReserveFrozen will be handled in it's own event log handler diff := new(big.Int).Sub(amount, info.Deposit) info.Deposit = big.NewInt(0) - // sw.senders[sender].Reserve.Sub(sw.senders[sender].Reserve, amount) if claimed, ok := sw.claimedReserve[sender]; ok { claimed.Add(claimed, diff) } else { - claimed = diff + sw.claimedReserve[sender] = diff } } else { // Draw from deposit From 502a519fb448efd5b0ddc35fe839c6ba53d78388 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 3 Oct 2019 17:58:11 +0200 Subject: [PATCH 05/38] cmd, eth, pm, server: redefine pm.SenderInfo, remove frozen reserve logic, withdrawRound instead of withdrawBlock Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 93e7256c8ac37c35f8b48414a969003d90b8e76f Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 59 ++++------- eth/watchers/senderwatcher_test.go | 156 ++++++++++++----------------- 2 files changed, 83 insertions(+), 132 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 317575c..e6df673 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -41,7 +41,7 @@ func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, }, nil } -// GetSenderInfo returns the senderInfo for a sender +// GetSenderInfo returns information about a sender's deposit and reserve // if values for a sender are not cached an RPC call to a remote ethereum node will be made to initialize the cache func (sw *SenderWatcher) GetSenderInfo(addr ethcommon.Address) (*pm.SenderInfo, error) { sw.mu.RLock() @@ -64,7 +64,7 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn sw.senders[addr] = info } -// ClaimedReserve returns the amount claimed from a sender's reserve +// ClaimedReserve returns the amount claimed from a sender's reserve by the node operator func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claimant ethcommon.Address) (*big.Int, error) { sw.mu.RLock() claimed := sw.claimedReserve[reserveHolder] @@ -157,20 +157,7 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { } sender = reserveFunded.ReserveHolder if info, ok := sw.senders[sender]; ok && !log.Removed { - info.Reserve.Add(info.Reserve, reserveFunded.Amount) - // if a thawed reserve is funded we flush claimedReserve for a sender and set it to NotFrozen - // making an RPC call here is suboptimal but doesn't happen frequently - var currentRound *big.Int - if info.ThawRound.Int64() != 0 { - currentRound, err = sw.lpEth.CurrentRound() - if err != nil { - return err - } - if info.ThawRound.Cmp(currentRound) < 0 { - info.ReserveState = pm.NotFrozen - sw.claimedReserve[sender] = big.NewInt(0) - } - } + info.Reserve.FundsRemaining.Add(info.Reserve.FundsRemaining, reserveFunded.Amount) } case "Withdrawal": var withdrawal contracts.TicketBrokerWithdrawal @@ -180,7 +167,8 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { sender = withdrawal.Sender if info, ok := sw.senders[sender]; ok && !log.Removed { info.Deposit = big.NewInt(0) - info.Reserve = big.NewInt(0) + info.Reserve.FundsRemaining = big.NewInt(0) + info.Reserve.ClaimedInCurrentRound = big.NewInt(0) sw.claimedReserve[sender] = big.NewInt(0) } case "WinningTicketTransfer": @@ -195,34 +183,25 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { // See if amount > deposit if info.Deposit.Cmp(amount) < 0 { // Draw from reserve - // ReserveFrozen will be handled in it's own event log handler diff := new(big.Int).Sub(amount, info.Deposit) info.Deposit = big.NewInt(0) - if claimed, ok := sw.claimedReserve[sender]; ok { - claimed.Add(claimed, diff) - } else { - sw.claimedReserve[sender] = diff + // Substract the difference from the remaining reserve + info.Reserve.FundsRemaining.Sub(info.Reserve.FundsRemaining, diff) + // Add the difference to the amount claimed from the reserve in the current round + info.Reserve.ClaimedInCurrentRound.Add(info.Reserve.ClaimedInCurrentRound, diff) + // if ticket recipient is the node operator, add to amount the recipient has claimed from a reserve + if sw.lpEth.Account().Address == winningTicketTransfer.Recipient { + if claimed, ok := sw.claimedReserve[sender]; ok { + claimed.Add(claimed, diff) + } else { + sw.claimedReserve[sender] = diff + } } } else { // Draw from deposit info.Deposit.Sub(info.Deposit, amount) } } - case "ReserveFrozen": - // Set reserveStatus and thawround - var reserveFrozen contracts.TicketBrokerReserveFrozen - if err := sw.dec.Decode("ReserveFrozen", log, &reserveFrozen); err != nil { - return fmt.Errorf("failed to decode ReserveFrozen event: %v", err) - } - sender = reserveFrozen.ReserveHolder - if info, ok := sw.senders[sender]; ok && !log.Removed { - info.ReserveState = pm.Frozen - // TODO: fetch freezePeriod instead of hardcoding or use a const - // TODO: how to handle unthaw - // frozen checks will have to be against ReserveFrozen and ThawRound < CurrentRound - // But how do we handle state updates when the reserve thaws? - info.ThawRound.Add(reserveFrozen.FreezeRound, big.NewInt(2)) - } case "Unlock": // Set withdraw block var unlock contracts.TicketBrokerUnlock @@ -231,17 +210,17 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { } sender = unlock.Sender if info, ok := sw.senders[sender]; ok && !log.Removed { - info.WithdrawBlock = unlock.EndBlock + info.WithdrawRound = unlock.EndRound } case "UnlockCancelled": - // Unset withdrawblock + // Unset withdrawRound var unlockCancelled contracts.TicketBrokerUnlockCancelled if err := sw.dec.Decode("UnlockCancelled", log, &unlockCancelled); err != nil { return fmt.Errorf("failed to decode UnlockCancelled event: %v", err) } sender = unlockCancelled.Sender if info, ok := sw.senders[sender]; ok && !log.Removed { - info.WithdrawBlock = big.NewInt(0) + info.WithdrawRound = big.NewInt(0) } default: return nil diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 4e9cb59..eaaf3c6 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/blockwatch" @@ -22,15 +23,20 @@ func TestGetAndSetSenderInfo(t *testing.T) { lpEth: ð.StubClient{ SenderInfo: &pm.SenderInfo{ Deposit: big.NewInt(10), - Reserve: big.NewInt(5), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, }, }, } info := &pm.SenderInfo{ - Deposit: big.NewInt(1000000000000000000), - Reserve: big.NewInt(1000000000000000000), - ReserveState: pm.NotFrozen, + Deposit: big.NewInt(1000000000000000000), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(1000000000000000000), + ClaimedInCurrentRound: big.NewInt(0), + }, } sender := pm.RandAddress() @@ -45,17 +51,20 @@ func TestGetAndSetSenderInfo(t *testing.T) { info, err = sw.GetSenderInfo(sender) assert.Nil(err) assert.Zero(info.Deposit.Cmp(big.NewInt(10))) - assert.Zero(info.Reserve.Cmp(big.NewInt(5))) + assert.Zero(info.Reserve.FundsRemaining.Cmp(big.NewInt(5))) info = sw.senders[sender] assert.Zero(info.Deposit.Cmp(big.NewInt(10))) - assert.Zero(info.Reserve.Cmp(big.NewInt(5))) + assert.Zero(info.Reserve.FundsRemaining.Cmp(big.NewInt(5))) } func TestSenderWatcher_Clear(t *testing.T) { assert := assert.New(t) stubClientSenderInfo := &pm.SenderInfo{ Deposit: big.NewInt(10), - Reserve: big.NewInt(5), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, } sw := &SenderWatcher{ senders: make(map[ethcommon.Address]*pm.SenderInfo), @@ -65,9 +74,11 @@ func TestSenderWatcher_Clear(t *testing.T) { }, } info := &pm.SenderInfo{ - Deposit: big.NewInt(1000000000000000000), - Reserve: big.NewInt(1000000000000000000), - ReserveState: pm.NotFrozen, + Deposit: big.NewInt(1000000000000000000), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(1000000000000000000), + ClaimedInCurrentRound: big.NewInt(0), + }, } sender := pm.RandAddress() @@ -164,7 +175,10 @@ func TestFundDepositEvent(t *testing.T) { lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ Deposit: big.NewInt(10), - Reserve: big.NewInt(5), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, }, } watcher := &stubBlockWatcher{} @@ -224,12 +238,13 @@ func TestFundReserveEvent(t *testing.T) { assert := assert.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) - startThawR := big.NewInt(1) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ - Deposit: big.NewInt(10), - Reserve: big.NewInt(5), - ThawRound: big.NewInt(1), + Deposit: big.NewInt(10), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, }, } watcher := &stubBlockWatcher{} @@ -263,8 +278,7 @@ func TestFundReserveEvent(t *testing.T) { info, ok := sw.senders[stubSender] assert.True(ok) assert.Equal(info.Deposit, startDeposit) - assert.Equal(info.Reserve, startReserve) - assert.Equal(info.ThawRound, startThawR) + assert.Equal(info.Reserve.FundsRemaining, startReserve) // If map entry exists parse event log blockEvent.Type = blockwatch.Added @@ -273,7 +287,7 @@ func TestFundReserveEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) - assert.Zero(info.Reserve.Cmp(expectedReserve)) + assert.Zero(info.Reserve.FundsRemaining.Cmp(expectedReserve)) // If we don't care about the address, don't handle the event s := pm.RandAddress() @@ -294,7 +308,10 @@ func TestWithdrawalEvent(t *testing.T) { lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ Deposit: big.NewInt(10), - Reserve: big.NewInt(5), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, }, } watcher := &stubBlockWatcher{} @@ -328,7 +345,7 @@ func TestWithdrawalEvent(t *testing.T) { info, ok := sw.senders[stubSender] assert.True(ok) assert.Zero(info.Deposit.Cmp(startDeposit)) - assert.Zero(info.Reserve.Cmp(startReserve)) + assert.Zero(info.Reserve.FundsRemaining.Cmp(startReserve)) // If map entry exists parse event log blockEvent.Type = blockwatch.Added @@ -338,7 +355,7 @@ func TestWithdrawalEvent(t *testing.T) { info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) assert.Zero(info.Deposit.Cmp(big.NewInt(0))) - assert.Zero(info.Reserve.Cmp(big.NewInt(0))) + assert.Zero(info.Reserve.FundsRemaining.Cmp(big.NewInt(0))) // If we don't care about the address, don't handle the event s := pm.RandAddress() @@ -359,12 +376,18 @@ func TestWinningTicketTransferEvent(t *testing.T) { reserve, _ := new(big.Int).SetString("5000000000000000000", 10) startDeposit, _ := new(big.Int).SetString("10000000000000000000", 10) startReserve, _ := new(big.Int).SetString("5000000000000000000", 10) + faceValue := big.NewInt(200000000000) + senderInfo := &pm.SenderInfo{ Deposit: deposit, - Reserve: reserve, + Reserve: &pm.ReserveInfo{ + FundsRemaining: reserve, + ClaimedInCurrentRound: big.NewInt(0), + }, } lpEth := ð.StubClient{ - SenderInfo: senderInfo, + SenderInfo: senderInfo, + TranscoderAddress: stubClaimant, } watcher := &stubBlockWatcher{} sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) @@ -397,19 +420,18 @@ func TestWinningTicketTransferEvent(t *testing.T) { info, ok = sw.senders[stubSender] assert.True(ok) assert.Equal(info.Deposit, startDeposit) - assert.Equal(info.Reserve, startReserve) + assert.Equal(new(big.Int).Add(info.Reserve.FundsRemaining, info.Reserve.ClaimedInCurrentRound), startReserve) // If map entry exists parse event log // ticket amount is 200 gwei // init the map entry blockEvent.Type = blockwatch.Added - faceValue := big.NewInt(200000000000) watcher.sink <- []*blockwatch.Event{blockEvent} time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) assert.Zero(startDeposit.Sub(startDeposit, faceValue).Cmp(info.Deposit)) - assert.Zero(startReserve.Cmp(info.Reserve)) + assert.Zero(startReserve.Cmp(info.Reserve.FundsRemaining)) // Test facevalue > deposit senderInfo.Deposit = big.NewInt(100000000000) @@ -422,6 +444,7 @@ func TestWinningTicketTransferEvent(t *testing.T) { assert.Zero(info.Deposit.Int64()) diff := new(big.Int).Sub(faceValue, big.NewInt(100000000000)) assert.Equal(claimed, diff) + assert.Equal(diff, info.Reserve.ClaimedInCurrentRound) // If we don't care about the address, don't handle the event s := pm.RandAddress() @@ -433,78 +456,27 @@ func TestWinningTicketTransferEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) _, ok = sw.senders[s] assert.False(ok) -} - -func TestReserveFrozenEvent(t *testing.T) { - assert := assert.New(t) - startThawR := big.NewInt(10) - lpEth := ð.StubClient{ - SenderInfo: &pm.SenderInfo{ - ThawRound: big.NewInt(10), - ReserveState: pm.Frozen, - }, - } - watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) - assert.Nil(err) - - header := defaultMiniHeader() - newReserveFrozenEvent := newStubReserveFrozenLog() - header.Logs = append(header.Logs, newReserveFrozenEvent) - - blockEvent := &blockwatch.Event{ - Type: blockwatch.Added, - BlockHeader: header, - } - - go sw.Watch() - defer sw.Stop() - time.Sleep(2 * time.Millisecond) - - // If map entry is nil don't handle event - watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) - _, ok := sw.senders[stubSender] - assert.False(ok) - // if block removed, make RPC call - blockEvent.Type = blockwatch.Removed - sw.senders[stubSender] = &pm.SenderInfo{} + // If ticket recipient is not stubClaimant, don't alter ClaimedReserve for stubClaimant + beforeClaimed, err := sw.ClaimedReserve(stubSender, stubClaimant) + randRecipient := pm.RandAddress() + var randRecipientTopic [32]byte + copy(randRecipientTopic[:], common.LeftPadBytes(randRecipient.Bytes(), 32)[:]) + blockEvent.BlockHeader.Logs[1].Topics[2] = randRecipientTopic watcher.sink <- []*blockwatch.Event{blockEvent} time.Sleep(2 * time.Millisecond) - info, ok := sw.senders[stubSender] - assert.True(ok) - assert.Equal(info.ReserveState, pm.Frozen) - assert.Zero(startThawR.Cmp(info.ThawRound)) - - // If map entry exists parse event log - blockEvent.Type = blockwatch.Added - watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) - info, err = sw.GetSenderInfo(stubSender) + afterClaimed, err := sw.ClaimedReserve(stubSender, stubClaimant) assert.Nil(err) - assert.Equal(info.ReserveState, pm.Frozen) - expectedThawRound := new(big.Int).Add(new(big.Int).SetBytes(newReserveFrozenEvent.Data[:32]), big.NewInt(2)) - assert.Zero(expectedThawRound.Cmp(info.ThawRound)) + assert.Equal(beforeClaimed, afterClaimed) - // If we don't care about the address, don't handle the event - s := pm.RandAddress() - sender := ethcommon.LeftPadBytes(s.Bytes(), 32) - var senderTopic ethcommon.Hash - copy(senderTopic[:], sender[:]) - newReserveFrozenEvent.Topics[1] = senderTopic - watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) - _, ok = sw.senders[s] - assert.False(ok) } func TestUnlockEvent(t *testing.T) { assert := assert.New(t) - startWithdrawBlock := big.NewInt(5) + startWithdrawRound := big.NewInt(5) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ - WithdrawBlock: big.NewInt(5), + WithdrawRound: big.NewInt(5), }, } watcher := &stubBlockWatcher{} @@ -537,7 +509,7 @@ func TestUnlockEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, ok := sw.senders[stubSender] assert.True(ok) - assert.Zero(info.WithdrawBlock.Cmp(startWithdrawBlock)) + assert.Zero(info.WithdrawRound.Cmp(startWithdrawRound)) // If map entry exists parse event log blockEvent.Type = blockwatch.Added @@ -545,7 +517,7 @@ func TestUnlockEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) - assert.Zero(info.WithdrawBlock.Cmp(big.NewInt(150))) + assert.Zero(info.WithdrawRound.Cmp(big.NewInt(150))) // If we don't care about the address, don't handle the event s := pm.RandAddress() @@ -563,7 +535,7 @@ func TestUnlockCancelledEvent(t *testing.T) { assert := assert.New(t) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ - WithdrawBlock: big.NewInt(10), + WithdrawRound: big.NewInt(10), }, } watcher := &stubBlockWatcher{} @@ -596,7 +568,7 @@ func TestUnlockCancelledEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, ok := sw.senders[stubSender] assert.True(ok) - assert.Zero(info.WithdrawBlock.Cmp(big.NewInt(10))) + assert.Zero(info.WithdrawRound.Cmp(big.NewInt(10))) // If map entry exists parse event log blockEvent.Type = blockwatch.Added @@ -604,7 +576,7 @@ func TestUnlockCancelledEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) - assert.Zero(info.WithdrawBlock.Int64()) + assert.Zero(info.WithdrawRound.Int64()) // If we don't care about the address, don't handle the event s := pm.RandAddress() From 92d59db0b19e033f2519cba7587c1cdf368cdc8d Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Fri, 4 Oct 2019 06:01:01 +0200 Subject: [PATCH 06/38] cmd, eth: RoundsWatcher subscription for SenderWatcher Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 30942f652de14e5ac1eea7a7ea7e0ff7c6cc7f7d Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 42 ++++++++- eth/watchers/senderwatcher_test.go | 145 ++++++++++++++++++++++++++--- 2 files changed, 174 insertions(+), 13 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index e6df673..ea134ff 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -21,19 +21,22 @@ type SenderWatcher struct { mu sync.RWMutex quit chan struct{} watcher BlockWatcher + rw EventWatcher lpEth eth.LivepeerEthClient dec *EventDecoder } // NewSenderWatcher initiates a new SenderWatcher -func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient) (*SenderWatcher, error) { +func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient, rw EventWatcher) (*SenderWatcher, error) { dec, err := NewEventDecoder(ticketBrokerAddr, contracts.TicketBrokerABI) if err != nil { return nil, err } + return &SenderWatcher{ quit: make(chan struct{}), watcher: watcher, + rw: rw, lpEth: lpEth, senders: make(map[ethcommon.Address]*pm.SenderInfo), claimedReserve: make(map[ethcommon.Address]*big.Int), @@ -84,9 +87,13 @@ func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claiman // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { + roundEvents := make(chan types.Log, 10) + sw.rw.Subscribe(roundEvents) + events := make(chan []*blockwatch.Event, 10) sub := sw.watcher.Subscribe(events) defer sub.Unsubscribe() + for { select { case <-sw.quit: @@ -95,6 +102,8 @@ func (sw *SenderWatcher) Watch() { glog.Error(err) case events := <-events: sw.handleBlockEvents(events) + case roundEvent := <-roundEvents: + sw.handleRoundEvent(roundEvent) } } } @@ -236,3 +245,34 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { return nil } + +func (sw *SenderWatcher) handleRoundEvent(log types.Log) error { + sw.mu.Lock() + defer sw.mu.Unlock() + + for sender, info := range sw.senders { + if log.Removed { + i, err := sw.lpEth.GetSenderInfo(sender) + if err != nil { + return fmt.Errorf("GetSenderInfo RPC call to remote node failed: %v", err) + } + info = i + } else { + info.Reserve.ClaimedInCurrentRound = big.NewInt(0) + } + } + + for sender := range sw.claimedReserve { + if log.Removed { + c, err := sw.lpEth.ClaimedReserve(sender, sw.lpEth.Account().Address) + if err != nil { + return fmt.Errorf("ClaimedReserve RPC call to remote node failed: %v", err) + } + sw.claimedReserve[sender] = c + } else { + sw.claimedReserve[sender] = big.NewInt(0) + } + } + + return nil +} diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index eaaf3c6..16814f1 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -112,7 +112,10 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { require := require.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) // if not existant on map; make RPC call and init the map @@ -143,7 +146,10 @@ func TestSenderWatcher_WatchAndStop(t *testing.T) { assert := assert.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) go sw.Watch() @@ -154,9 +160,13 @@ func TestSenderWatcher_WatchAndStop(t *testing.T) { } func TestSenderWatcher_HandleLog(t *testing.T) { + assert := assert.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - rw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(t, err) assert := assert.New(t) @@ -165,7 +175,7 @@ func TestSenderWatcher_HandleLog(t *testing.T) { log := newStubBaseLog() log.Topics = []ethcommon.Hash{ethcommon.BytesToHash([]byte("foo"))} - err = rw.handleLog(log) + err = sw.handleLog(log) assert.Nil(err) } @@ -182,7 +192,10 @@ func TestFundDepositEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -248,7 +261,10 @@ func TestFundReserveEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -315,7 +331,10 @@ func TestWithdrawalEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -371,7 +390,6 @@ func TestWithdrawalEvent(t *testing.T) { func TestWinningTicketTransferEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) deposit, _ := new(big.Int).SetString("10000000000000000000", 10) reserve, _ := new(big.Int).SetString("5000000000000000000", 10) startDeposit, _ := new(big.Int).SetString("10000000000000000000", 10) @@ -390,7 +408,10 @@ func TestWinningTicketTransferEvent(t *testing.T) { TranscoderAddress: stubClaimant, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) header := defaultMiniHeader() @@ -420,7 +441,7 @@ func TestWinningTicketTransferEvent(t *testing.T) { info, ok = sw.senders[stubSender] assert.True(ok) assert.Equal(info.Deposit, startDeposit) - assert.Equal(new(big.Int).Add(info.Reserve.FundsRemaining, info.Reserve.ClaimedInCurrentRound), startReserve) + assert.Equal(info.Reserve.FundsRemaining, startReserve) // If map entry exists parse event log // ticket amount is 200 gwei @@ -480,7 +501,10 @@ func TestUnlockEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -539,7 +563,10 @@ func TestUnlockCancelledEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -589,3 +616,97 @@ func TestUnlockCancelledEvent(t *testing.T) { _, ok = sw.senders[s] assert.False(ok) } + +// Test RoundsWatcher Subscription log added and log removed +func TestNewRoundEvent_LogAdded(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Reserve: &pm.ReserveInfo{ + ClaimedInCurrentRound: big.NewInt(1000), + }, + }, + ClaimedAmount: big.NewInt(1000), + } + + watcher := &stubBlockWatcher{} + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + require.Nil(err) + + // set initial values + info, err := sw.GetSenderInfo(stubSender) + require.Nil(err) + claimed, err := sw.ClaimedReserve(stubSender, stubClaimant) + require.Nil(err) + + newRoundEvent := newStubNewRoundLog() + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + rw.sink <- newRoundEvent + + time.Sleep(2 * time.Millisecond) + + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Reserve.ClaimedInCurrentRound.Int64()) + claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(err) + assert.Zero(claimed.Int64()) +} + +func TestNewRoundEvent_LogRemoved(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Reserve: &pm.ReserveInfo{ + ClaimedInCurrentRound: big.NewInt(1000), + }, + }, + ClaimedAmount: big.NewInt(1000), + } + + watcher := &stubBlockWatcher{} + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) + + newRoundEvent := newStubNewRoundLog() + + // set initial values + info, err := sw.GetSenderInfo(stubSender) + require.Nil(err) + claimed, err := sw.ClaimedReserve(stubSender, stubClaimant) + require.Nil(err) + + // change stub RPC call values + expectedClaimedInCurrentRound := big.NewInt(500) + expectedClaimedAmount := big.NewInt(2000) + lpEth.SenderInfo.Reserve.ClaimedInCurrentRound = expectedClaimedInCurrentRound + lpEth.ClaimedAmount = expectedClaimedAmount + + newRoundEvent.Removed = true + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + rw.sink <- newRoundEvent + time.Sleep(2 * time.Millisecond) + + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Equal(info.Reserve.ClaimedInCurrentRound, expectedClaimedInCurrentRound) + claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(err) + assert.Equal(claimed, expectedClaimedAmount) +} From f9107efbb000e13a24f2c1b4f5d2ef0ffd54480b Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Sat, 5 Oct 2019 04:08:45 +0200 Subject: [PATCH 07/38] eth/watchers: refactor RoundsWatcher Subscription mechanism Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 1799b33eafcb50fa48171dd5509a50d05fb8fab4 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 3 ++- eth/watchers/senderwatcher_test.go | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index ea134ff..b6fd17b 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -88,7 +88,8 @@ func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claiman // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { roundEvents := make(chan types.Log, 10) - sw.rw.Subscribe(roundEvents) + roundSub := sw.rw.Subscribe(roundEvents) + defer roundSub.Unsubscribe() events := make(chan []*blockwatch.Event, 10) sub := sw.watcher.Subscribe(events) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 16814f1..0001971 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -118,6 +118,9 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) + // if not existant on map; make RPC call and init the map _, ok := sw.claimedReserve[stubSender] require.False(ok) @@ -143,6 +146,7 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { } func TestSenderWatcher_WatchAndStop(t *testing.T) { + require := require.New(t) assert := assert.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} @@ -169,7 +173,11 @@ func TestSenderWatcher_HandleLog(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(t, err) - assert := assert.New(t) + rw, err := NewRoundsWatcher(stubRoundsManagerAddr, watcher, lpEth) + require.Nil(err) + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) // Test unknown event log := newStubBaseLog() @@ -181,6 +189,7 @@ func TestSenderWatcher_HandleLog(t *testing.T) { func TestFundDepositEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) startDeposit := big.NewInt(10) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ @@ -249,6 +258,7 @@ func TestFundDepositEvent(t *testing.T) { func TestFundReserveEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) lpEth := ð.StubClient{ @@ -319,6 +329,7 @@ func TestFundReserveEvent(t *testing.T) { func TestWithdrawalEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) lpEth := ð.StubClient{ @@ -414,6 +425,9 @@ func TestWinningTicketTransferEvent(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) + header := defaultMiniHeader() newWinningTicketEvent := newStubWinningTicketLog() header.Logs = append(header.Logs, newWinningTicketEvent) @@ -494,6 +508,7 @@ func TestWinningTicketTransferEvent(t *testing.T) { func TestUnlockEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) startWithdrawRound := big.NewInt(5) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ @@ -557,6 +572,7 @@ func TestUnlockEvent(t *testing.T) { func TestUnlockCancelledEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ WithdrawRound: big.NewInt(10), @@ -634,8 +650,14 @@ func TestNewRoundEvent_LogAdded(t *testing.T) { rw := &stubRoundsWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + rw, err := NewRoundsWatcher(stubRoundsManagerAddr, watcherForRounds, lpEth) require.Nil(err) + go rw.Watch() + defer rw.Stop() + time.Sleep(2 * time.Millisecond) + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) // set initial values info, err := sw.GetSenderInfo(stubSender) From 87fd3862ea8d7632afbf46868031ab50914c3a6c Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Sat, 5 Oct 2019 04:20:59 +0200 Subject: [PATCH 08/38] eth: remove LivepeerVerifier references Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: ebdaed880f96e07b72288090e54f1141e8b404ab Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher_test.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 0001971..7d5ee1c 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -118,9 +118,6 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) - assert.Nil(err) - // if not existant on map; make RPC call and init the map _, ok := sw.claimedReserve[stubSender] require.False(ok) @@ -146,7 +143,6 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { } func TestSenderWatcher_WatchAndStop(t *testing.T) { - require := require.New(t) assert := assert.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} @@ -170,12 +166,6 @@ func TestSenderWatcher_HandleLog(t *testing.T) { rw := &stubRoundsWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) - require.Nil(t, err) - - rw, err := NewRoundsWatcher(stubRoundsManagerAddr, watcher, lpEth) - require.Nil(err) - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) @@ -189,7 +179,6 @@ func TestSenderWatcher_HandleLog(t *testing.T) { func TestFundDepositEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) startDeposit := big.NewInt(10) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ @@ -258,7 +247,6 @@ func TestFundDepositEvent(t *testing.T) { func TestFundReserveEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) lpEth := ð.StubClient{ @@ -329,7 +317,6 @@ func TestFundReserveEvent(t *testing.T) { func TestWithdrawalEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) lpEth := ð.StubClient{ @@ -422,9 +409,6 @@ func TestWinningTicketTransferEvent(t *testing.T) { rw := &stubRoundsWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) - require.Nil(err) - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) @@ -508,7 +492,6 @@ func TestWinningTicketTransferEvent(t *testing.T) { func TestUnlockEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) startWithdrawRound := big.NewInt(5) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ @@ -572,7 +555,6 @@ func TestUnlockEvent(t *testing.T) { func TestUnlockCancelledEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ WithdrawRound: big.NewInt(10), @@ -650,12 +632,6 @@ func TestNewRoundEvent_LogAdded(t *testing.T) { rw := &stubRoundsWatcher{} - rw, err := NewRoundsWatcher(stubRoundsManagerAddr, watcherForRounds, lpEth) - require.Nil(err) - go rw.Watch() - defer rw.Stop() - time.Sleep(2 * time.Millisecond) - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) From ab8352982eb2f4cb5bd591c5ecb61f8cce19149d Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Tue, 26 Nov 2019 18:14:38 +0100 Subject: [PATCH 09/38] eth, cmd: cache O stake on new round event Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 70d62a01a49014ce2650f27b7c70b58af5b6f31b Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index b6fd17b..727d79f 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -104,7 +104,9 @@ func (sw *SenderWatcher) Watch() { case events := <-events: sw.handleBlockEvents(events) case roundEvent := <-roundEvents: - sw.handleRoundEvent(roundEvent) + if err := sw.handleRoundEvent(roundEvent); err != nil { + glog.Errorf("error handling new round event: %v", err) + } } } } From 3641b5d416f479679177493b6a4876e64092d421 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 6 Feb 2020 03:31:58 +0100 Subject: [PATCH 10/38] eth/watchers: extend roundsWatcher to be a more generic timeWatcher that watchers for both latest rounds and latest blocks Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: b4605f705bcad5e355bb0c738d784e4503089900 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 8 ++--- eth/watchers/senderwatcher_test.go | 48 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 727d79f..f84703b 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -21,13 +21,13 @@ type SenderWatcher struct { mu sync.RWMutex quit chan struct{} watcher BlockWatcher - rw EventWatcher + tw timeWatcher lpEth eth.LivepeerEthClient dec *EventDecoder } // NewSenderWatcher initiates a new SenderWatcher -func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient, rw EventWatcher) (*SenderWatcher, error) { +func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient, tw timeWatcher) (*SenderWatcher, error) { dec, err := NewEventDecoder(ticketBrokerAddr, contracts.TicketBrokerABI) if err != nil { return nil, err @@ -36,7 +36,7 @@ func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, return &SenderWatcher{ quit: make(chan struct{}), watcher: watcher, - rw: rw, + tw: tw, lpEth: lpEth, senders: make(map[ethcommon.Address]*pm.SenderInfo), claimedReserve: make(map[ethcommon.Address]*big.Int), @@ -88,7 +88,7 @@ func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claiman // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { roundEvents := make(chan types.Log, 10) - roundSub := sw.rw.Subscribe(roundEvents) + roundSub := sw.tw.SubscribeRounds(roundEvents) defer roundSub.Unsubscribe() events := make(chan []*blockwatch.Event, 10) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 7d5ee1c..3e761d6 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -113,9 +113,9 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) require.Nil(err) // if not existant on map; make RPC call and init the map @@ -147,9 +147,9 @@ func TestSenderWatcher_WatchAndStop(t *testing.T) { lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) go sw.Watch() @@ -164,9 +164,9 @@ func TestSenderWatcher_HandleLog(t *testing.T) { lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) // Test unknown event @@ -191,9 +191,9 @@ func TestFundDepositEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -260,9 +260,9 @@ func TestFundReserveEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -330,9 +330,9 @@ func TestWithdrawalEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -407,9 +407,9 @@ func TestWinningTicketTransferEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -500,9 +500,9 @@ func TestUnlockEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -562,9 +562,9 @@ func TestUnlockCancelledEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -630,9 +630,9 @@ func TestNewRoundEvent_LogAdded(t *testing.T) { watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) // set initial values @@ -647,7 +647,7 @@ func TestNewRoundEvent_LogAdded(t *testing.T) { defer sw.Stop() time.Sleep(2 * time.Millisecond) - rw.sink <- newRoundEvent + tw.sink <- newRoundEvent time.Sleep(2 * time.Millisecond) @@ -673,9 +673,9 @@ func TestNewRoundEvent_LogRemoved(t *testing.T) { watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) newRoundEvent := newStubNewRoundLog() @@ -698,7 +698,7 @@ func TestNewRoundEvent_LogRemoved(t *testing.T) { defer sw.Stop() time.Sleep(2 * time.Millisecond) - rw.sink <- newRoundEvent + tw.sink <- newRoundEvent time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) From 842f987d2c00e8e6006204ab30ce209c239f5a13 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 4 Jun 2020 23:45:21 +0200 Subject: [PATCH 11/38] eth,pm: SubscribeReserveChange subscription for LocalSenderMonitor Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 46f074f0cfe6eb794f73bff15476bfb3f7094aa4 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 15 +++++++ eth/watchers/senderwatcher_test.go | 72 ++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index f84703b..c78e75e 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -7,6 +7,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" "github.com/golang/glog" "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/blockwatch" @@ -24,6 +25,10 @@ type SenderWatcher struct { tw timeWatcher lpEth eth.LivepeerEthClient dec *EventDecoder + + // subscriptions + reserveChangeFeed event.Feed + reserveChangeScope event.SubscriptionScope } // NewSenderWatcher initiates a new SenderWatcher @@ -114,6 +119,7 @@ func (sw *SenderWatcher) Watch() { // Stop watching for events func (sw *SenderWatcher) Stop() { close(sw.quit) + sw.reserveChangeScope.Close() } // Clear removes a key-value pair from the map @@ -128,6 +134,11 @@ func (sw *SenderWatcher) Clear(addr ethcommon.Address) { } } +// SubscribeReserveChange notifies subscribers when the sender info for a particular sender changes +func (sw *SenderWatcher) SubscribeReserveChange(sink chan<- ethcommon.Address) event.Subscription { + return sw.reserveChangeScope.Track(sw.reserveChangeFeed.Subscribe(sink)) +} + func (sw *SenderWatcher) handleBlockEvents(events []*blockwatch.Event) { for _, event := range events { for _, log := range event.BlockHeader.Logs { @@ -170,6 +181,7 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { sender = reserveFunded.ReserveHolder if info, ok := sw.senders[sender]; ok && !log.Removed { info.Reserve.FundsRemaining.Add(info.Reserve.FundsRemaining, reserveFunded.Amount) + sw.reserveChangeFeed.Send(sender) } case "Withdrawal": var withdrawal contracts.TicketBrokerWithdrawal @@ -244,6 +256,9 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { return fmt.Errorf("GetSenderInfo RPC call to remote node failed: %v", err) } sw.senders[sender] = info + if eventName == "ReserveFunded" { + sw.reserveChangeFeed.Send(sender) + } } return nil diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 3e761d6..33eb2bf 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -708,3 +708,75 @@ func TestNewRoundEvent_LogRemoved(t *testing.T) { assert.Nil(err) assert.Equal(claimed, expectedClaimedAmount) } + +func TestSubscribeReserveChange(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + watcher := &stubBlockWatcher{} + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(100), + }, + }, + } + + sm, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, &stubTimeWatcher{}) + require.Nil(err) + + header := defaultMiniHeader() + + // Test reserve funded + fundedEvent := newStubReserveFundedLog() + header.Logs = append(header.Logs, fundedEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + sink := make(chan ethcommon.Address, 10) + sub := sm.SubscribeReserveChange(sink) + defer sub.Unsubscribe() + + go sm.Watch() + defer sm.Stop() + time.Sleep(time.Millisecond) + + // set initial values + _, err = sm.GetSenderInfo(stubSender) + require.Nil(err) + + sm.handleBlockEvents([]*blockwatch.Event{blockEvent}) + sender := <-sink + assert.Equal(sender, stubSender) + + // Test log removed + blockEvent.Type = blockwatch.Removed + sm.handleBlockEvents([]*blockwatch.Event{blockEvent}) + sender = <-sink + assert.Equal(sender, stubSender) + + // Test log removed for event that is not ReserveFunded (no value received on the sink channel) + header = defaultMiniHeader() + header.Logs = append(header.Logs, newStubBaseLog()) + blockEvent = &blockwatch.Event{ + Type: blockwatch.Removed, + BlockHeader: header, + } + sm.handleBlockEvents([]*blockwatch.Event{blockEvent}) + + waitC := make(chan struct{}) + defer close(waitC) + go func() { + time.Sleep(1 * time.Second) + waitC <- struct{}{} + }() + + select { + case _, ok := <-waitC: + assert.True(ok) + case <-sink: + t.Fail() + } +} From 4989f03a942a7e562c591e4d97118b0a526371d9 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Wed, 17 Jun 2020 02:31:13 +0200 Subject: [PATCH 12/38] monitor, eth: log deposit and reserve for the broadcaster only when monitor is enabled Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 06245d9dbb2878bee37680aef263a892f6ec8f62 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index c78e75e..51298dc 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -12,6 +12,7 @@ import ( "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/blockwatch" "github.com/livepeer/go-livepeer/eth/contracts" + "github.com/livepeer/go-livepeer/monitor" "github.com/livepeer/go-livepeer/pm" ) @@ -261,6 +262,11 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { } } + if sender == sw.lpEth.Account().Address && monitor.Enabled { + monitor.Deposit(sender.Hex(), sw.senders[sender].Deposit) + monitor.Reserve(sender.Hex(), sw.senders[sender].Reserve.FundsRemaining) + } + return nil } From 766bdafb8bcc7e1887b1ddf96a574d72f06cfd23 Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 30 Jun 2020 17:36:02 -0400 Subject: [PATCH 13/38] eth/watchers: Log deposit/reserve when caching sender info Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: aa6f7d578ad93c0fa36226f105c41452b6119e2f Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 51298dc..5f6c173 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -71,6 +71,11 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn sw.mu.Lock() defer sw.mu.Unlock() sw.senders[addr] = info + + if addr == sw.lpEth.Account().Address && monitor.Enabled { + monitor.Deposit(addr.Hex(), info.Deposit) + monitor.Reserve(addr.Hex(), info.Reserve.FundsRemaining) + } } // ClaimedReserve returns the amount claimed from a sender's reserve by the node operator From b2f64dbd4b3e0bd748d70798f3dc3a360c3eded4 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 25 Jun 2020 23:05:22 +0200 Subject: [PATCH 14/38] cmd, core: allow orchestrator+redeemer mode without starting transcode server. This results in a node that can run all on-chain services using the on-chain registered keys Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: c162e069f2559108641807bbcfacf3fab884e154 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 33eb2bf..5b89844 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -207,11 +207,11 @@ func TestFundDepositEvent(t *testing.T) { go sw.Watch() defer sw.Stop() - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) // If map entry is nil don't handle event watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) _, ok := sw.senders[stubSender] assert.False(ok) @@ -219,7 +219,7 @@ func TestFundDepositEvent(t *testing.T) { blockEvent.Type = blockwatch.Removed sw.senders[stubSender] = &pm.SenderInfo{} watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) info, ok := sw.senders[stubSender] assert.True(ok) assert.Zero(info.Deposit.Cmp(startDeposit)) @@ -228,7 +228,7 @@ func TestFundDepositEvent(t *testing.T) { blockEvent.Type = blockwatch.Added expectedDeposit := new(big.Int).Add(startDeposit, new(big.Int).SetBytes(newDepositEvent.Data)) watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) assert.Zero(info.Deposit.Cmp(expectedDeposit)) @@ -240,7 +240,7 @@ func TestFundDepositEvent(t *testing.T) { copy(senderTopic[:], sender[:]) newDepositEvent.Topics[1] = senderTopic watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) _, ok = sw.senders[s] assert.False(ok) } From 6237995620916183d18e90bd466cadf8e9df6d36 Mon Sep 17 00:00:00 2001 From: Rafal Leszko Date: Thu, 20 Jan 2022 15:55:03 +0100 Subject: [PATCH 15/38] eth: Handle watcher feeds asynchronously Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 87c6e404c7a8bd5552e66e346d3659d696e17f4f Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 5f6c173..0f4ceab 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -113,11 +113,15 @@ func (sw *SenderWatcher) Watch() { case err := <-sub.Err(): glog.Error(err) case events := <-events: - sw.handleBlockEvents(events) + go func() { + sw.handleBlockEvents(events) + }() case roundEvent := <-roundEvents: - if err := sw.handleRoundEvent(roundEvent); err != nil { - glog.Errorf("error handling new round event: %v", err) - } + go func() { + if err := sw.handleRoundEvent(roundEvent); err != nil { + glog.Errorf("error handling new round event: %v", err) + } + }() } } } From de878d054ee1f6dfc9f1d6e0e91de391573d4b1c Mon Sep 17 00:00:00 2001 From: Rafal Leszko Date: Fri, 21 Jan 2022 09:11:42 +0100 Subject: [PATCH 16/38] eth: Refactor sink naming in the watcher feeds Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: f30819e43a6b0d9272301a402220d7df64d70063 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 0f4ceab..1b2d291 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -98,8 +98,8 @@ func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claiman // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { - roundEvents := make(chan types.Log, 10) - roundSub := sw.tw.SubscribeRounds(roundEvents) + roundSink := make(chan types.Log, 10) + roundSub := sw.tw.SubscribeRounds(roundSink) defer roundSub.Unsubscribe() events := make(chan []*blockwatch.Event, 10) @@ -116,9 +116,9 @@ func (sw *SenderWatcher) Watch() { go func() { sw.handleBlockEvents(events) }() - case roundEvent := <-roundEvents: + case round := <-roundSink: go func() { - if err := sw.handleRoundEvent(roundEvent); err != nil { + if err := sw.handleRoundEvent(round); err != nil { glog.Errorf("error handling new round event: %v", err) } }() From 6bf9b5d065aaf3dbcf6a6f6136540c4793c9240d Mon Sep 17 00:00:00 2001 From: Rafal Leszko Date: Thu, 27 Jan 2022 15:48:47 +0100 Subject: [PATCH 17/38] eth: Refactor go func() {} into go Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 754c1ed203539275332363eb2cd2940a7fa6c72a Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 1b2d291..00bff03 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -113,9 +113,7 @@ func (sw *SenderWatcher) Watch() { case err := <-sub.Err(): glog.Error(err) case events := <-events: - go func() { - sw.handleBlockEvents(events) - }() + go sw.handleBlockEvents(events) case round := <-roundSink: go func() { if err := sw.handleRoundEvent(round); err != nil { From b10d92b524a2ce363a0929ab1b3ce70ff5e62bd7 Mon Sep 17 00:00:00 2001 From: hjpotter92 Date: Thu, 13 Oct 2022 10:55:10 +0530 Subject: [PATCH 18/38] test.yaml: Add misspell checker in lint/test jobs. Fix typos across project. Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 12358f5082b18a3e200dd99534ea92f91baea258 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 00bff03..3786107 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -217,7 +217,7 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { // Draw from reserve diff := new(big.Int).Sub(amount, info.Deposit) info.Deposit = big.NewInt(0) - // Substract the difference from the remaining reserve + // Subtract the difference from the remaining reserve info.Reserve.FundsRemaining.Sub(info.Reserve.FundsRemaining, diff) // Add the difference to the amount claimed from the reserve in the current round info.Reserve.ClaimedInCurrentRound.Add(info.Reserve.ClaimedInCurrentRound, diff) From 8f0bcf78bd2aa915caeedbe89d8e5740a0c75fb4 Mon Sep 17 00:00:00 2001 From: houpo-bob Date: Fri, 25 Jul 2025 17:59:39 +0800 Subject: [PATCH 19/38] chore: fix some typos in comment (#3687) Signed-off-by: houpo-bob Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 9755a0dd280821a87e73d40871fc7f2775f150f5 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 5b89844..88be020 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -118,7 +118,7 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) require.Nil(err) - // if not existant on map; make RPC call and init the map + // if not existent on map; make RPC call and init the map _, ok := sw.claimedReserve[stubSender] require.False(ok) lpEth.ClaimedAmount = big.NewInt(5000) @@ -126,7 +126,7 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { assert.Nil(err) assert.Zero(lpEth.ClaimedAmount.Cmp(claimed)) - // if existant on map return the map value + // if existent on map return the map value sw.claimedReserve[stubSender] = big.NewInt(10000) claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) assert.Nil(err) From 7aca71618f9700b544d0497aa18d8a0616ea584c Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Fri, 16 Aug 2019 08:39:08 +0200 Subject: [PATCH 20/38] eth: senderwatcher with cache Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 341b39a0c53c05203d4e7b01d887a12ace1ebd89 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 218 +++++++++++ eth/watchers/senderwatcher_test.go | 576 +++++++++++++++++++++++++++++ 2 files changed, 794 insertions(+) create mode 100644 eth/watchers/senderwatcher.go create mode 100644 eth/watchers/senderwatcher_test.go diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go new file mode 100644 index 0000000..931889f --- /dev/null +++ b/eth/watchers/senderwatcher.go @@ -0,0 +1,218 @@ +package watchers + +import ( + "fmt" + "math/big" + "sync" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/golang/glog" + "github.com/livepeer/go-livepeer/eth" + "github.com/livepeer/go-livepeer/eth/blockwatch" + "github.com/livepeer/go-livepeer/eth/contracts" + "github.com/livepeer/go-livepeer/pm" +) + +// SenderWatcher maintains a concurrency-safe map with SenderInfo +type SenderWatcher struct { + senders map[ethcommon.Address]*pm.SenderInfo + mu sync.RWMutex + quit chan struct{} + watcher BlockWatcher + lpEth eth.LivepeerEthClient + dec *EventDecoder +} + +// NewSenderWatcher initiates a new SenderWatcher +func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient) (*SenderWatcher, error) { + dec, err := NewEventDecoder(ticketBrokerAddr, contracts.TicketBrokerABI) + if err != nil { + return nil, err + } + return &SenderWatcher{ + quit: make(chan struct{}), + watcher: watcher, + lpEth: lpEth, + senders: make(map[ethcommon.Address]*pm.SenderInfo), + dec: dec, + }, nil +} + +// GetSenderInfo returns the senderInfo for a sender +// if values for a sender are not cached an RPC call to a remote ethereum node will be made to initialize the cache +func (sw *SenderWatcher) GetSenderInfo(addr ethcommon.Address) (*pm.SenderInfo, error) { + sw.mu.RLock() + cache := sw.senders[addr] + sw.mu.RUnlock() + if cache == nil { + info, err := sw.lpEth.GetSenderInfo(addr) + if err != nil { + return nil, fmt.Errorf("GetSenderInfo RPC call to remote node failed: %v", err) + } + sw.setSenderInfo(addr, info) + return info, nil + } + return cache, nil +} + +func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderInfo) { + sw.mu.Lock() + defer sw.mu.Unlock() + sw.senders[addr] = info +} + +// Watch starts the event watching loop +func (sw *SenderWatcher) Watch() { + events := make(chan []*blockwatch.Event, 10) + sub := sw.watcher.Subscribe(events) + defer sub.Unsubscribe() + for { + select { + case <-sw.quit: + return + case err := <-sub.Err(): + glog.Error(err) + case events := <-events: + sw.handleBlockEvents(events) + } + } +} + +// Stop watching for events +func (sw *SenderWatcher) Stop() { + close(sw.quit) +} + +// Clear removes a key-value pair from the map +func (sw *SenderWatcher) Clear(addr ethcommon.Address) { + sw.mu.Lock() + defer sw.mu.Unlock() + if _, ok := sw.senders[addr]; ok { + delete(sw.senders, addr) + } +} + +func (sw *SenderWatcher) handleBlockEvents(events []*blockwatch.Event) { + for _, event := range events { + for _, log := range event.BlockHeader.Logs { + if event.Type == blockwatch.Removed { + log.Removed = true + } + if err := sw.handleLog(log); err != nil { + glog.Error(err) + } + } + } +} + +func (sw *SenderWatcher) handleLog(log types.Log) error { + eventName, err := sw.dec.FindEventName(log) + if err != nil { + // Noop if we cannot find the event name + return nil + } + + sw.mu.Lock() + defer sw.mu.Unlock() + + var sender ethcommon.Address + switch eventName { + case "DepositFunded": + var depositFunded contracts.TicketBrokerDepositFunded + if err := sw.dec.Decode("DepositFunded", log, &depositFunded); err != nil { + return fmt.Errorf("failed to decode DepositFunded event: %v", err) + } + sender = depositFunded.Sender + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.Deposit.Add(info.Deposit, depositFunded.Amount) + } + case "ReserveFunded": + var reserveFunded contracts.TicketBrokerReserveFunded + if err := sw.dec.Decode("ReserveFunded", log, &reserveFunded); err != nil { + return fmt.Errorf("failed to decode ReserveFunded event: %v", err) + } + sender = reserveFunded.ReserveHolder + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.Reserve.Add(info.Reserve, reserveFunded.Amount) + } + case "Withdrawal": + var withdrawal contracts.TicketBrokerWithdrawal + if err := sw.dec.Decode("Withdrawal", log, &withdrawal); err != nil { + return fmt.Errorf("failed to decode Withdrawal event: %v", err) + } + sender = withdrawal.Sender + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.Deposit = big.NewInt(0) + info.Reserve = big.NewInt(0) + } + case "WinningTicketTransfer": + var winningTicketTransfer contracts.TicketBrokerWinningTicketTransfer + if err := sw.dec.Decode("WinningTicketTransfer", log, &winningTicketTransfer); err != nil { + return fmt.Errorf("failed to decode WinningTicketTransfer event: %v", err) + } + amount := winningTicketTransfer.Amount + sender = winningTicketTransfer.Sender + + if info, ok := sw.senders[sender]; ok && !log.Removed { + // See if amount > deposit + if info.Deposit.Cmp(amount) < 0 { + // Draw from reserve + // ReserveFrozen will be handled in it's own event log handler + diff := new(big.Int).Sub(amount, info.Deposit) + info.Deposit = big.NewInt(0) + info.Reserve.Sub(info.Reserve, diff) + } else { + // Draw from deposit + info.Deposit.Sub(info.Deposit, amount) + } + } + case "ReserveFrozen": + // Set reserveStatus and thawround + var reserveFrozen contracts.TicketBrokerReserveFrozen + if err := sw.dec.Decode("ReserveFrozen", log, &reserveFrozen); err != nil { + return fmt.Errorf("failed to decode ReserveFrozen event: %v", err) + } + sender = reserveFrozen.ReserveHolder + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.ReserveState = pm.Frozen + // TODO: fetch freezePeriod instead of hardcoding or use a const + // TODO: how to handle unthaw + // frozen checks will have to be against ReserveFrozen and ThawRound < CurrentRound + // But how do we handle state updates when the reserve thaws? + info.ThawRound.Add(reserveFrozen.FreezeRound, big.NewInt(2)) + } + case "Unlock": + // Set withdraw block + var unlock contracts.TicketBrokerUnlock + if err := sw.dec.Decode("Unlock", log, &unlock); err != nil { + return fmt.Errorf("failed to decode Unlock event: %v", err) + } + sender = unlock.Sender + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.WithdrawBlock = unlock.EndBlock + } + case "UnlockCancelled": + // Unset withdrawblock + var unlockCancelled contracts.TicketBrokerUnlockCancelled + if err := sw.dec.Decode("UnlockCancelled", log, &unlockCancelled); err != nil { + return fmt.Errorf("failed to decode UnlockCancelled event: %v", err) + } + sender = unlockCancelled.Sender + if info, ok := sw.senders[sender]; ok && !log.Removed { + info.WithdrawBlock = big.NewInt(0) + } + default: + return nil + } + + if _, ok := sw.senders[sender]; ok && log.Removed { + info, err := sw.lpEth.GetSenderInfo(sender) + if err != nil { + return fmt.Errorf("GetSenderInfo RPC call to remote node failed: %v", err) + } + sw.senders[sender] = info + } + + return nil +} diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go new file mode 100644 index 0000000..65a19d2 --- /dev/null +++ b/eth/watchers/senderwatcher_test.go @@ -0,0 +1,576 @@ +package watchers + +import ( + "math/big" + "testing" + "time" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/livepeer/go-livepeer/eth" + "github.com/livepeer/go-livepeer/eth/blockwatch" + "github.com/livepeer/go-livepeer/pm" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetAndSetSenderInfo(t *testing.T) { + assert := assert.New(t) + sw := &SenderWatcher{ + senders: make(map[ethcommon.Address]*pm.SenderInfo), + lpEth: ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + }, + }, + } + + info := &pm.SenderInfo{ + Deposit: big.NewInt(1000000000000000000), + Reserve: big.NewInt(1000000000000000000), + ReserveState: pm.NotFrozen, + } + sender := pm.RandAddress() + + sw.setSenderInfo(sender, info) + entry, err := sw.GetSenderInfo(sender) + assert.Nil(err) + assert.Equal(sw.senders[sender], info) + assert.Equal(info, entry) + + // If map entry is nil and we call get, make RPC call and set the returned data + sw.Clear(sender) + info, err = sw.GetSenderInfo(sender) + assert.Nil(err) + assert.Zero(info.Deposit.Cmp(big.NewInt(10))) + assert.Zero(info.Reserve.Cmp(big.NewInt(5))) + info = sw.senders[sender] + assert.Zero(info.Deposit.Cmp(big.NewInt(10))) + assert.Zero(info.Reserve.Cmp(big.NewInt(5))) +} + +func TestSenderWatcher_Clear(t *testing.T) { + assert := assert.New(t) + stubClientSenderInfo := &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + } + sw := &SenderWatcher{ + senders: make(map[ethcommon.Address]*pm.SenderInfo), + lpEth: ð.StubClient{ + SenderInfo: stubClientSenderInfo, + }, + } + info := &pm.SenderInfo{ + Deposit: big.NewInt(1000000000000000000), + Reserve: big.NewInt(1000000000000000000), + ReserveState: pm.NotFrozen, + } + sender := pm.RandAddress() + + sw.setSenderInfo(sender, info) + + getInfo, err := sw.GetSenderInfo(sender) + assert.Nil(err) + assert.Equal(info, getInfo) + + sw.Clear(sender) + + getInfo = sw.senders[sender] + assert.Nil(getInfo) + // on calling GetSenderInfo should be back to RPC stub values + getInfo, err = sw.GetSenderInfo(sender) + assert.Nil(err) + assert.Equal(getInfo, stubClientSenderInfo) +} + +func TestSenderWatcher_WatchAndStop(t *testing.T) { + assert := assert.New(t) + lpEth := ð.StubClient{} + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + go sw.Watch() + time.Sleep(2 * time.Millisecond) + sw.Stop() + time.Sleep(2 * time.Millisecond) + assert.True(watcher.sub.unsubscribed) +} + +func TestSenderWatcher_HandleLog(t *testing.T) { + lpEth := ð.StubClient{} + watcher := &stubBlockWatcher{} + rw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + require.Nil(t, err) + + assert := assert.New(t) + + // Test unknown event + log := newStubBaseLog() + log.Topics = []ethcommon.Hash{ethcommon.BytesToHash([]byte("foo"))} + + err = rw.handleLog(log) + assert.Nil(err) +} + +func TestFundDepositEvent(t *testing.T) { + assert := assert.New(t) + startDeposit := big.NewInt(10) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newDepositEvent := newStubDepositFundedLog() + header.Logs = append(header.Logs, newDepositEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Zero(info.Deposit.Cmp(startDeposit)) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + expectedDeposit := new(big.Int).Add(startDeposit, new(big.Int).SetBytes(newDepositEvent.Data)) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Deposit.Cmp(expectedDeposit)) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newDepositEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestFundReserveEvent(t *testing.T) { + assert := assert.New(t) + startDeposit := big.NewInt(10) + startReserve := big.NewInt(5) + startThawR := big.NewInt(1) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + ThawRound: big.NewInt(1), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newReserveEvent := newStubReserveFundedLog() + header.Logs = append(header.Logs, newReserveEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Equal(info.Deposit, startDeposit) + assert.Equal(info.Reserve, startReserve) + assert.Equal(info.ThawRound, startThawR) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + expectedReserve := new(big.Int).Add(startReserve, new(big.Int).SetBytes(newReserveEvent.Data)) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Reserve.Cmp(expectedReserve)) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newReserveEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestWithdrawalEvent(t *testing.T) { + assert := assert.New(t) + startDeposit := big.NewInt(10) + startReserve := big.NewInt(5) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Deposit: big.NewInt(10), + Reserve: big.NewInt(5), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newWithdrawalEvent := newStubWithdrawalLog() + header.Logs = append(header.Logs, newWithdrawalEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Zero(info.Deposit.Cmp(startDeposit)) + assert.Zero(info.Reserve.Cmp(startReserve)) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + time.Sleep(2 * time.Millisecond) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Deposit.Cmp(big.NewInt(0))) + assert.Zero(info.Reserve.Cmp(big.NewInt(0))) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newWithdrawalEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestWinningTicketTransferEvent(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + deposit, _ := new(big.Int).SetString("10000000000000000000", 10) + reserve, _ := new(big.Int).SetString("5000000000000000000", 10) + startDeposit, _ := new(big.Int).SetString("10000000000000000000", 10) + startReserve, _ := new(big.Int).SetString("5000000000000000000", 10) + senderInfo := &pm.SenderInfo{ + Deposit: deposit, + Reserve: reserve, + } + lpEth := ð.StubClient{ + SenderInfo: senderInfo, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + require.Nil(err) + + header := defaultMiniHeader() + newWinningTicketEvent := newStubWinningTicketLog() + header.Logs = append(header.Logs, newWinningTicketEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok = sw.senders[stubSender] + assert.True(ok) + assert.Equal(info.Deposit, startDeposit) + assert.Equal(info.Reserve, startReserve) + + // If map entry exists parse event log + // ticket amount is 200 gwei + // init the map entry + blockEvent.Type = blockwatch.Added + faceValue := big.NewInt(200000000000) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(startDeposit.Sub(startDeposit, faceValue).Cmp(info.Deposit)) + assert.Zero(startReserve.Cmp(info.Reserve)) + + // Test facevalue > deposit + senderInfo.Deposit = big.NewInt(100000000000) + info, err = sw.GetSenderInfo(stubSender) + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Deposit.Int64()) + diff := new(big.Int).Sub(faceValue, big.NewInt(100000000000)) + expectedReserve := new(big.Int).Sub(startReserve, diff) + assert.Zero(expectedReserve.Cmp(info.Reserve)) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newWinningTicketEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestReserveFrozenEvent(t *testing.T) { + assert := assert.New(t) + startThawR := big.NewInt(10) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + ThawRound: big.NewInt(10), + ReserveState: pm.Frozen, + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newReserveFrozenEvent := newStubReserveFrozenLog() + header.Logs = append(header.Logs, newReserveFrozenEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Equal(info.ReserveState, pm.Frozen) + assert.Zero(startThawR.Cmp(info.ThawRound)) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Equal(info.ReserveState, pm.Frozen) + expectedThawRound := new(big.Int).Add(new(big.Int).SetBytes(newReserveFrozenEvent.Data[:32]), big.NewInt(2)) + assert.Zero(expectedThawRound.Cmp(info.ThawRound)) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newReserveFrozenEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestUnlockEvent(t *testing.T) { + assert := assert.New(t) + startWithdrawBlock := big.NewInt(5) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + WithdrawBlock: big.NewInt(5), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newUnlockEvent := newStubUnlockLog() + header.Logs = append(header.Logs, newUnlockEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Zero(info.WithdrawBlock.Cmp(startWithdrawBlock)) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.WithdrawBlock.Cmp(big.NewInt(150))) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newUnlockEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} + +func TestUnlockCancelledEvent(t *testing.T) { + assert := assert.New(t) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + WithdrawBlock: big.NewInt(10), + }, + } + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + assert.Nil(err) + + header := defaultMiniHeader() + newUnlockCancelledEvent := newStubUnlockCancelledLog() + header.Logs = append(header.Logs, newUnlockCancelledEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + // If map entry is nil don't handle event + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok := sw.senders[stubSender] + assert.False(ok) + + // if block removed, make RPC call + blockEvent.Type = blockwatch.Removed + sw.senders[stubSender] = &pm.SenderInfo{} + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, ok := sw.senders[stubSender] + assert.True(ok) + assert.Zero(info.WithdrawBlock.Cmp(big.NewInt(10))) + + // If map entry exists parse event log + blockEvent.Type = blockwatch.Added + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.WithdrawBlock.Int64()) + + // If we don't care about the address, don't handle the event + s := pm.RandAddress() + sender := ethcommon.LeftPadBytes(s.Bytes(), 32) + var senderTopic ethcommon.Hash + copy(senderTopic[:], sender[:]) + newUnlockCancelledEvent.Topics[1] = senderTopic + watcher.sink <- []*blockwatch.Event{blockEvent} + time.Sleep(2 * time.Millisecond) + _, ok = sw.senders[s] + assert.False(ok) +} From 14c1e9aaef1f8573bc336657c2083a576c775be6 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 29 Aug 2019 21:51:47 +0200 Subject: [PATCH 21/38] pm, eth: calculate reserveAlloc from SenderWatcher cache Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 6a283a135e40e6738b06b4152ee56c44b75ab32d Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 40 +++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 931889f..9ca5dc2 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -16,12 +16,13 @@ import ( // SenderWatcher maintains a concurrency-safe map with SenderInfo type SenderWatcher struct { - senders map[ethcommon.Address]*pm.SenderInfo - mu sync.RWMutex - quit chan struct{} - watcher BlockWatcher - lpEth eth.LivepeerEthClient - dec *EventDecoder + senders map[ethcommon.Address]*pm.SenderInfo + claimedReserve map[ethcommon.Address]*big.Int // map representing how much a recipient has drawn from a sender's reserve + mu sync.RWMutex + quit chan struct{} + watcher BlockWatcher + lpEth eth.LivepeerEthClient + dec *EventDecoder } // NewSenderWatcher initiates a new SenderWatcher @@ -62,6 +63,13 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn sw.senders[addr] = info } +// ClaimedReserve returns the amount claimed from a sender's reserve +func (sw *SenderWatcher) ClaimedReserve(sender ethcommon.Address) *big.Int { + sw.mu.RLock() + defer sw.mu.RUnlock() + return sw.claimedReserve[sender] +} + // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { events := make(chan []*blockwatch.Event, 10) @@ -135,6 +143,19 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { sender = reserveFunded.ReserveHolder if info, ok := sw.senders[sender]; ok && !log.Removed { info.Reserve.Add(info.Reserve, reserveFunded.Amount) + // if a thawed reserve is funded we flush claimedReserve for a sender and set it to NotFrozen + // making an RPC call here is suboptimal but doesn't happen frequently + var currentRound *big.Int + if info.ThawRound.Int64() != 0 { + currentRound, err = sw.lpEth.CurrentRound() + if err != nil { + return err + } + if info.ThawRound.Cmp(currentRound) < 0 { + info.ReserveState = pm.NotFrozen + sw.claimedReserve[sender] = big.NewInt(0) + } + } } case "Withdrawal": var withdrawal contracts.TicketBrokerWithdrawal @@ -161,7 +182,12 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { // ReserveFrozen will be handled in it's own event log handler diff := new(big.Int).Sub(amount, info.Deposit) info.Deposit = big.NewInt(0) - info.Reserve.Sub(info.Reserve, diff) + // sw.senders[sender].Reserve.Sub(sw.senders[sender].Reserve, amount) + if claimed, ok := sw.claimedReserve[sender]; ok { + claimed.Add(claimed, diff) + } else { + claimed = diff + } } else { // Draw from deposit info.Deposit.Sub(info.Deposit, amount) From c7434c7c66168a9144044154dbadbb57a598e8c5 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Tue, 3 Sep 2019 14:01:18 +0200 Subject: [PATCH 22/38] eth/watchers: track claimedReserve on SenderWatcher Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 57e28870688c191b0d669d3a1776f848348ef470 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 32 ++++++++++++++----- eth/watchers/senderwatcher_test.go | 49 ++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 9ca5dc2..095c06a 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -32,11 +32,12 @@ func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, return nil, err } return &SenderWatcher{ - quit: make(chan struct{}), - watcher: watcher, - lpEth: lpEth, - senders: make(map[ethcommon.Address]*pm.SenderInfo), - dec: dec, + quit: make(chan struct{}), + watcher: watcher, + lpEth: lpEth, + senders: make(map[ethcommon.Address]*pm.SenderInfo), + claimedReserve: make(map[ethcommon.Address]*big.Int), + dec: dec, }, nil } @@ -64,10 +65,21 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn } // ClaimedReserve returns the amount claimed from a sender's reserve -func (sw *SenderWatcher) ClaimedReserve(sender ethcommon.Address) *big.Int { +func (sw *SenderWatcher) ClaimedReserve(sender ethcommon.Address, claimant ethcommon.Address) (*big.Int, error) { sw.mu.RLock() - defer sw.mu.RUnlock() - return sw.claimedReserve[sender] + claimed := sw.claimedReserve[sender] + sw.mu.RUnlock() + if claimed != nil { + return claimed, nil + } + claimed, err := sw.lpEth.ClaimedReserve(claimant, sender) + if err != nil { + return nil, fmt.Errorf("ClaimedReserve RPC call to remote node failed: %v", err) + } + sw.mu.Lock() + sw.claimedReserve[sender] = claimed + sw.mu.Unlock() + return claimed, nil } // Watch starts the event watching loop @@ -99,6 +111,9 @@ func (sw *SenderWatcher) Clear(addr ethcommon.Address) { if _, ok := sw.senders[addr]; ok { delete(sw.senders, addr) } + if _, ok := sw.claimedReserve[addr]; ok { + delete(sw.claimedReserve, addr) + } } func (sw *SenderWatcher) handleBlockEvents(events []*blockwatch.Event) { @@ -166,6 +181,7 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { if info, ok := sw.senders[sender]; ok && !log.Removed { info.Deposit = big.NewInt(0) info.Reserve = big.NewInt(0) + sw.claimedReserve[sender] = big.NewInt(0) } case "WinningTicketTransfer": var winningTicketTransfer contracts.TicketBrokerWinningTicketTransfer diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 65a19d2..4e9cb59 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -1,6 +1,7 @@ package watchers import ( + "errors" "math/big" "testing" "time" @@ -57,7 +58,8 @@ func TestSenderWatcher_Clear(t *testing.T) { Reserve: big.NewInt(5), } sw := &SenderWatcher{ - senders: make(map[ethcommon.Address]*pm.SenderInfo), + senders: make(map[ethcommon.Address]*pm.SenderInfo), + claimedReserve: make(map[ethcommon.Address]*big.Int), lpEth: ð.StubClient{ SenderInfo: stubClientSenderInfo, }, @@ -70,10 +72,15 @@ func TestSenderWatcher_Clear(t *testing.T) { sender := pm.RandAddress() sw.setSenderInfo(sender, info) + claimed := big.NewInt(100) + sw.claimedReserve[sender] = claimed getInfo, err := sw.GetSenderInfo(sender) assert.Nil(err) assert.Equal(info, getInfo) + getClaimed, err := sw.ClaimedReserve(sender, ethcommon.Address{}) + assert.Nil(err) + assert.Zero(claimed.Cmp(getClaimed)) sw.Clear(sender) @@ -83,6 +90,42 @@ func TestSenderWatcher_Clear(t *testing.T) { getInfo, err = sw.GetSenderInfo(sender) assert.Nil(err) assert.Equal(getInfo, stubClientSenderInfo) + getClaimed, err = sw.ClaimedReserve(sender, ethcommon.Address{}) + assert.Nil(err) + assert.Nil(getClaimed) + +} + +func TestSenderWatcher_ClaimedReserve(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + lpEth := ð.StubClient{} + watcher := &stubBlockWatcher{} + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + require.Nil(err) + + // if not existant on map; make RPC call and init the map + _, ok := sw.claimedReserve[stubSender] + require.False(ok) + lpEth.ClaimedAmount = big.NewInt(5000) + claimed, err := sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(err) + assert.Zero(lpEth.ClaimedAmount.Cmp(claimed)) + + // if existant on map return the map value + sw.claimedReserve[stubSender] = big.NewInt(10000) + claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(err) + assert.Equal(big.NewInt(10000), claimed) + + // if RPC error return error + lpEth.ClaimedAmount = nil + sw.claimedReserve[stubSender] = nil + expectedErr := errors.New("ClaimedReserve RPC error") + lpEth.ClaimedReserveError = expectedErr + claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(claimed) + assert.Contains(err.Error(), expectedErr.Error()) } func TestSenderWatcher_WatchAndStop(t *testing.T) { @@ -374,11 +417,11 @@ func TestWinningTicketTransferEvent(t *testing.T) { watcher.sink <- []*blockwatch.Event{blockEvent} time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) + claimed, err := sw.ClaimedReserve(stubSender, stubClaimant) assert.Nil(err) assert.Zero(info.Deposit.Int64()) diff := new(big.Int).Sub(faceValue, big.NewInt(100000000000)) - expectedReserve := new(big.Int).Sub(startReserve, diff) - assert.Zero(expectedReserve.Cmp(info.Reserve)) + assert.Equal(claimed, diff) // If we don't care about the address, don't handle the event s := pm.RandAddress() From 547876e5ed7b00cf8586d45744dabfd276028477 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Tue, 3 Sep 2019 14:03:11 +0200 Subject: [PATCH 23/38] pm: updates to Broker and SenderManager interface Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 5b74602591c67645a422b5414d0fb052158248e4 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 095c06a..317575c 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -65,19 +65,19 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn } // ClaimedReserve returns the amount claimed from a sender's reserve -func (sw *SenderWatcher) ClaimedReserve(sender ethcommon.Address, claimant ethcommon.Address) (*big.Int, error) { +func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claimant ethcommon.Address) (*big.Int, error) { sw.mu.RLock() - claimed := sw.claimedReserve[sender] + claimed := sw.claimedReserve[reserveHolder] sw.mu.RUnlock() if claimed != nil { return claimed, nil } - claimed, err := sw.lpEth.ClaimedReserve(claimant, sender) + claimed, err := sw.lpEth.ClaimedReserve(reserveHolder, claimant) if err != nil { return nil, fmt.Errorf("ClaimedReserve RPC call to remote node failed: %v", err) } sw.mu.Lock() - sw.claimedReserve[sender] = claimed + sw.claimedReserve[reserveHolder] = claimed sw.mu.Unlock() return claimed, nil } @@ -198,11 +198,10 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { // ReserveFrozen will be handled in it's own event log handler diff := new(big.Int).Sub(amount, info.Deposit) info.Deposit = big.NewInt(0) - // sw.senders[sender].Reserve.Sub(sw.senders[sender].Reserve, amount) if claimed, ok := sw.claimedReserve[sender]; ok { claimed.Add(claimed, diff) } else { - claimed = diff + sw.claimedReserve[sender] = diff } } else { // Draw from deposit From 05679b7f6d3d8063e8e793dda501ead14dc005c4 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 3 Oct 2019 17:58:11 +0200 Subject: [PATCH 24/38] cmd, eth, pm, server: redefine pm.SenderInfo, remove frozen reserve logic, withdrawRound instead of withdrawBlock Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 93e7256c8ac37c35f8b48414a969003d90b8e76f Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 59 ++++------- eth/watchers/senderwatcher_test.go | 156 ++++++++++++----------------- 2 files changed, 83 insertions(+), 132 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 317575c..e6df673 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -41,7 +41,7 @@ func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, }, nil } -// GetSenderInfo returns the senderInfo for a sender +// GetSenderInfo returns information about a sender's deposit and reserve // if values for a sender are not cached an RPC call to a remote ethereum node will be made to initialize the cache func (sw *SenderWatcher) GetSenderInfo(addr ethcommon.Address) (*pm.SenderInfo, error) { sw.mu.RLock() @@ -64,7 +64,7 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn sw.senders[addr] = info } -// ClaimedReserve returns the amount claimed from a sender's reserve +// ClaimedReserve returns the amount claimed from a sender's reserve by the node operator func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claimant ethcommon.Address) (*big.Int, error) { sw.mu.RLock() claimed := sw.claimedReserve[reserveHolder] @@ -157,20 +157,7 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { } sender = reserveFunded.ReserveHolder if info, ok := sw.senders[sender]; ok && !log.Removed { - info.Reserve.Add(info.Reserve, reserveFunded.Amount) - // if a thawed reserve is funded we flush claimedReserve for a sender and set it to NotFrozen - // making an RPC call here is suboptimal but doesn't happen frequently - var currentRound *big.Int - if info.ThawRound.Int64() != 0 { - currentRound, err = sw.lpEth.CurrentRound() - if err != nil { - return err - } - if info.ThawRound.Cmp(currentRound) < 0 { - info.ReserveState = pm.NotFrozen - sw.claimedReserve[sender] = big.NewInt(0) - } - } + info.Reserve.FundsRemaining.Add(info.Reserve.FundsRemaining, reserveFunded.Amount) } case "Withdrawal": var withdrawal contracts.TicketBrokerWithdrawal @@ -180,7 +167,8 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { sender = withdrawal.Sender if info, ok := sw.senders[sender]; ok && !log.Removed { info.Deposit = big.NewInt(0) - info.Reserve = big.NewInt(0) + info.Reserve.FundsRemaining = big.NewInt(0) + info.Reserve.ClaimedInCurrentRound = big.NewInt(0) sw.claimedReserve[sender] = big.NewInt(0) } case "WinningTicketTransfer": @@ -195,34 +183,25 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { // See if amount > deposit if info.Deposit.Cmp(amount) < 0 { // Draw from reserve - // ReserveFrozen will be handled in it's own event log handler diff := new(big.Int).Sub(amount, info.Deposit) info.Deposit = big.NewInt(0) - if claimed, ok := sw.claimedReserve[sender]; ok { - claimed.Add(claimed, diff) - } else { - sw.claimedReserve[sender] = diff + // Substract the difference from the remaining reserve + info.Reserve.FundsRemaining.Sub(info.Reserve.FundsRemaining, diff) + // Add the difference to the amount claimed from the reserve in the current round + info.Reserve.ClaimedInCurrentRound.Add(info.Reserve.ClaimedInCurrentRound, diff) + // if ticket recipient is the node operator, add to amount the recipient has claimed from a reserve + if sw.lpEth.Account().Address == winningTicketTransfer.Recipient { + if claimed, ok := sw.claimedReserve[sender]; ok { + claimed.Add(claimed, diff) + } else { + sw.claimedReserve[sender] = diff + } } } else { // Draw from deposit info.Deposit.Sub(info.Deposit, amount) } } - case "ReserveFrozen": - // Set reserveStatus and thawround - var reserveFrozen contracts.TicketBrokerReserveFrozen - if err := sw.dec.Decode("ReserveFrozen", log, &reserveFrozen); err != nil { - return fmt.Errorf("failed to decode ReserveFrozen event: %v", err) - } - sender = reserveFrozen.ReserveHolder - if info, ok := sw.senders[sender]; ok && !log.Removed { - info.ReserveState = pm.Frozen - // TODO: fetch freezePeriod instead of hardcoding or use a const - // TODO: how to handle unthaw - // frozen checks will have to be against ReserveFrozen and ThawRound < CurrentRound - // But how do we handle state updates when the reserve thaws? - info.ThawRound.Add(reserveFrozen.FreezeRound, big.NewInt(2)) - } case "Unlock": // Set withdraw block var unlock contracts.TicketBrokerUnlock @@ -231,17 +210,17 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { } sender = unlock.Sender if info, ok := sw.senders[sender]; ok && !log.Removed { - info.WithdrawBlock = unlock.EndBlock + info.WithdrawRound = unlock.EndRound } case "UnlockCancelled": - // Unset withdrawblock + // Unset withdrawRound var unlockCancelled contracts.TicketBrokerUnlockCancelled if err := sw.dec.Decode("UnlockCancelled", log, &unlockCancelled); err != nil { return fmt.Errorf("failed to decode UnlockCancelled event: %v", err) } sender = unlockCancelled.Sender if info, ok := sw.senders[sender]; ok && !log.Removed { - info.WithdrawBlock = big.NewInt(0) + info.WithdrawRound = big.NewInt(0) } default: return nil diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 4e9cb59..eaaf3c6 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/blockwatch" @@ -22,15 +23,20 @@ func TestGetAndSetSenderInfo(t *testing.T) { lpEth: ð.StubClient{ SenderInfo: &pm.SenderInfo{ Deposit: big.NewInt(10), - Reserve: big.NewInt(5), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, }, }, } info := &pm.SenderInfo{ - Deposit: big.NewInt(1000000000000000000), - Reserve: big.NewInt(1000000000000000000), - ReserveState: pm.NotFrozen, + Deposit: big.NewInt(1000000000000000000), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(1000000000000000000), + ClaimedInCurrentRound: big.NewInt(0), + }, } sender := pm.RandAddress() @@ -45,17 +51,20 @@ func TestGetAndSetSenderInfo(t *testing.T) { info, err = sw.GetSenderInfo(sender) assert.Nil(err) assert.Zero(info.Deposit.Cmp(big.NewInt(10))) - assert.Zero(info.Reserve.Cmp(big.NewInt(5))) + assert.Zero(info.Reserve.FundsRemaining.Cmp(big.NewInt(5))) info = sw.senders[sender] assert.Zero(info.Deposit.Cmp(big.NewInt(10))) - assert.Zero(info.Reserve.Cmp(big.NewInt(5))) + assert.Zero(info.Reserve.FundsRemaining.Cmp(big.NewInt(5))) } func TestSenderWatcher_Clear(t *testing.T) { assert := assert.New(t) stubClientSenderInfo := &pm.SenderInfo{ Deposit: big.NewInt(10), - Reserve: big.NewInt(5), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, } sw := &SenderWatcher{ senders: make(map[ethcommon.Address]*pm.SenderInfo), @@ -65,9 +74,11 @@ func TestSenderWatcher_Clear(t *testing.T) { }, } info := &pm.SenderInfo{ - Deposit: big.NewInt(1000000000000000000), - Reserve: big.NewInt(1000000000000000000), - ReserveState: pm.NotFrozen, + Deposit: big.NewInt(1000000000000000000), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(1000000000000000000), + ClaimedInCurrentRound: big.NewInt(0), + }, } sender := pm.RandAddress() @@ -164,7 +175,10 @@ func TestFundDepositEvent(t *testing.T) { lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ Deposit: big.NewInt(10), - Reserve: big.NewInt(5), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, }, } watcher := &stubBlockWatcher{} @@ -224,12 +238,13 @@ func TestFundReserveEvent(t *testing.T) { assert := assert.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) - startThawR := big.NewInt(1) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ - Deposit: big.NewInt(10), - Reserve: big.NewInt(5), - ThawRound: big.NewInt(1), + Deposit: big.NewInt(10), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, }, } watcher := &stubBlockWatcher{} @@ -263,8 +278,7 @@ func TestFundReserveEvent(t *testing.T) { info, ok := sw.senders[stubSender] assert.True(ok) assert.Equal(info.Deposit, startDeposit) - assert.Equal(info.Reserve, startReserve) - assert.Equal(info.ThawRound, startThawR) + assert.Equal(info.Reserve.FundsRemaining, startReserve) // If map entry exists parse event log blockEvent.Type = blockwatch.Added @@ -273,7 +287,7 @@ func TestFundReserveEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) - assert.Zero(info.Reserve.Cmp(expectedReserve)) + assert.Zero(info.Reserve.FundsRemaining.Cmp(expectedReserve)) // If we don't care about the address, don't handle the event s := pm.RandAddress() @@ -294,7 +308,10 @@ func TestWithdrawalEvent(t *testing.T) { lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ Deposit: big.NewInt(10), - Reserve: big.NewInt(5), + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(5), + ClaimedInCurrentRound: big.NewInt(0), + }, }, } watcher := &stubBlockWatcher{} @@ -328,7 +345,7 @@ func TestWithdrawalEvent(t *testing.T) { info, ok := sw.senders[stubSender] assert.True(ok) assert.Zero(info.Deposit.Cmp(startDeposit)) - assert.Zero(info.Reserve.Cmp(startReserve)) + assert.Zero(info.Reserve.FundsRemaining.Cmp(startReserve)) // If map entry exists parse event log blockEvent.Type = blockwatch.Added @@ -338,7 +355,7 @@ func TestWithdrawalEvent(t *testing.T) { info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) assert.Zero(info.Deposit.Cmp(big.NewInt(0))) - assert.Zero(info.Reserve.Cmp(big.NewInt(0))) + assert.Zero(info.Reserve.FundsRemaining.Cmp(big.NewInt(0))) // If we don't care about the address, don't handle the event s := pm.RandAddress() @@ -359,12 +376,18 @@ func TestWinningTicketTransferEvent(t *testing.T) { reserve, _ := new(big.Int).SetString("5000000000000000000", 10) startDeposit, _ := new(big.Int).SetString("10000000000000000000", 10) startReserve, _ := new(big.Int).SetString("5000000000000000000", 10) + faceValue := big.NewInt(200000000000) + senderInfo := &pm.SenderInfo{ Deposit: deposit, - Reserve: reserve, + Reserve: &pm.ReserveInfo{ + FundsRemaining: reserve, + ClaimedInCurrentRound: big.NewInt(0), + }, } lpEth := ð.StubClient{ - SenderInfo: senderInfo, + SenderInfo: senderInfo, + TranscoderAddress: stubClaimant, } watcher := &stubBlockWatcher{} sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) @@ -397,19 +420,18 @@ func TestWinningTicketTransferEvent(t *testing.T) { info, ok = sw.senders[stubSender] assert.True(ok) assert.Equal(info.Deposit, startDeposit) - assert.Equal(info.Reserve, startReserve) + assert.Equal(new(big.Int).Add(info.Reserve.FundsRemaining, info.Reserve.ClaimedInCurrentRound), startReserve) // If map entry exists parse event log // ticket amount is 200 gwei // init the map entry blockEvent.Type = blockwatch.Added - faceValue := big.NewInt(200000000000) watcher.sink <- []*blockwatch.Event{blockEvent} time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) assert.Zero(startDeposit.Sub(startDeposit, faceValue).Cmp(info.Deposit)) - assert.Zero(startReserve.Cmp(info.Reserve)) + assert.Zero(startReserve.Cmp(info.Reserve.FundsRemaining)) // Test facevalue > deposit senderInfo.Deposit = big.NewInt(100000000000) @@ -422,6 +444,7 @@ func TestWinningTicketTransferEvent(t *testing.T) { assert.Zero(info.Deposit.Int64()) diff := new(big.Int).Sub(faceValue, big.NewInt(100000000000)) assert.Equal(claimed, diff) + assert.Equal(diff, info.Reserve.ClaimedInCurrentRound) // If we don't care about the address, don't handle the event s := pm.RandAddress() @@ -433,78 +456,27 @@ func TestWinningTicketTransferEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) _, ok = sw.senders[s] assert.False(ok) -} - -func TestReserveFrozenEvent(t *testing.T) { - assert := assert.New(t) - startThawR := big.NewInt(10) - lpEth := ð.StubClient{ - SenderInfo: &pm.SenderInfo{ - ThawRound: big.NewInt(10), - ReserveState: pm.Frozen, - }, - } - watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) - assert.Nil(err) - - header := defaultMiniHeader() - newReserveFrozenEvent := newStubReserveFrozenLog() - header.Logs = append(header.Logs, newReserveFrozenEvent) - - blockEvent := &blockwatch.Event{ - Type: blockwatch.Added, - BlockHeader: header, - } - - go sw.Watch() - defer sw.Stop() - time.Sleep(2 * time.Millisecond) - - // If map entry is nil don't handle event - watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) - _, ok := sw.senders[stubSender] - assert.False(ok) - // if block removed, make RPC call - blockEvent.Type = blockwatch.Removed - sw.senders[stubSender] = &pm.SenderInfo{} + // If ticket recipient is not stubClaimant, don't alter ClaimedReserve for stubClaimant + beforeClaimed, err := sw.ClaimedReserve(stubSender, stubClaimant) + randRecipient := pm.RandAddress() + var randRecipientTopic [32]byte + copy(randRecipientTopic[:], common.LeftPadBytes(randRecipient.Bytes(), 32)[:]) + blockEvent.BlockHeader.Logs[1].Topics[2] = randRecipientTopic watcher.sink <- []*blockwatch.Event{blockEvent} time.Sleep(2 * time.Millisecond) - info, ok := sw.senders[stubSender] - assert.True(ok) - assert.Equal(info.ReserveState, pm.Frozen) - assert.Zero(startThawR.Cmp(info.ThawRound)) - - // If map entry exists parse event log - blockEvent.Type = blockwatch.Added - watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) - info, err = sw.GetSenderInfo(stubSender) + afterClaimed, err := sw.ClaimedReserve(stubSender, stubClaimant) assert.Nil(err) - assert.Equal(info.ReserveState, pm.Frozen) - expectedThawRound := new(big.Int).Add(new(big.Int).SetBytes(newReserveFrozenEvent.Data[:32]), big.NewInt(2)) - assert.Zero(expectedThawRound.Cmp(info.ThawRound)) + assert.Equal(beforeClaimed, afterClaimed) - // If we don't care about the address, don't handle the event - s := pm.RandAddress() - sender := ethcommon.LeftPadBytes(s.Bytes(), 32) - var senderTopic ethcommon.Hash - copy(senderTopic[:], sender[:]) - newReserveFrozenEvent.Topics[1] = senderTopic - watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) - _, ok = sw.senders[s] - assert.False(ok) } func TestUnlockEvent(t *testing.T) { assert := assert.New(t) - startWithdrawBlock := big.NewInt(5) + startWithdrawRound := big.NewInt(5) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ - WithdrawBlock: big.NewInt(5), + WithdrawRound: big.NewInt(5), }, } watcher := &stubBlockWatcher{} @@ -537,7 +509,7 @@ func TestUnlockEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, ok := sw.senders[stubSender] assert.True(ok) - assert.Zero(info.WithdrawBlock.Cmp(startWithdrawBlock)) + assert.Zero(info.WithdrawRound.Cmp(startWithdrawRound)) // If map entry exists parse event log blockEvent.Type = blockwatch.Added @@ -545,7 +517,7 @@ func TestUnlockEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) - assert.Zero(info.WithdrawBlock.Cmp(big.NewInt(150))) + assert.Zero(info.WithdrawRound.Cmp(big.NewInt(150))) // If we don't care about the address, don't handle the event s := pm.RandAddress() @@ -563,7 +535,7 @@ func TestUnlockCancelledEvent(t *testing.T) { assert := assert.New(t) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ - WithdrawBlock: big.NewInt(10), + WithdrawRound: big.NewInt(10), }, } watcher := &stubBlockWatcher{} @@ -596,7 +568,7 @@ func TestUnlockCancelledEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, ok := sw.senders[stubSender] assert.True(ok) - assert.Zero(info.WithdrawBlock.Cmp(big.NewInt(10))) + assert.Zero(info.WithdrawRound.Cmp(big.NewInt(10))) // If map entry exists parse event log blockEvent.Type = blockwatch.Added @@ -604,7 +576,7 @@ func TestUnlockCancelledEvent(t *testing.T) { time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) - assert.Zero(info.WithdrawBlock.Int64()) + assert.Zero(info.WithdrawRound.Int64()) // If we don't care about the address, don't handle the event s := pm.RandAddress() From 4df310c40ea210d662372a75bce51c5db63f3669 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Fri, 4 Oct 2019 06:01:01 +0200 Subject: [PATCH 25/38] cmd, eth: RoundsWatcher subscription for SenderWatcher Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 30942f652de14e5ac1eea7a7ea7e0ff7c6cc7f7d Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 42 ++++++++- eth/watchers/senderwatcher_test.go | 145 ++++++++++++++++++++++++++--- 2 files changed, 174 insertions(+), 13 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index e6df673..ea134ff 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -21,19 +21,22 @@ type SenderWatcher struct { mu sync.RWMutex quit chan struct{} watcher BlockWatcher + rw EventWatcher lpEth eth.LivepeerEthClient dec *EventDecoder } // NewSenderWatcher initiates a new SenderWatcher -func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient) (*SenderWatcher, error) { +func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient, rw EventWatcher) (*SenderWatcher, error) { dec, err := NewEventDecoder(ticketBrokerAddr, contracts.TicketBrokerABI) if err != nil { return nil, err } + return &SenderWatcher{ quit: make(chan struct{}), watcher: watcher, + rw: rw, lpEth: lpEth, senders: make(map[ethcommon.Address]*pm.SenderInfo), claimedReserve: make(map[ethcommon.Address]*big.Int), @@ -84,9 +87,13 @@ func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claiman // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { + roundEvents := make(chan types.Log, 10) + sw.rw.Subscribe(roundEvents) + events := make(chan []*blockwatch.Event, 10) sub := sw.watcher.Subscribe(events) defer sub.Unsubscribe() + for { select { case <-sw.quit: @@ -95,6 +102,8 @@ func (sw *SenderWatcher) Watch() { glog.Error(err) case events := <-events: sw.handleBlockEvents(events) + case roundEvent := <-roundEvents: + sw.handleRoundEvent(roundEvent) } } } @@ -236,3 +245,34 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { return nil } + +func (sw *SenderWatcher) handleRoundEvent(log types.Log) error { + sw.mu.Lock() + defer sw.mu.Unlock() + + for sender, info := range sw.senders { + if log.Removed { + i, err := sw.lpEth.GetSenderInfo(sender) + if err != nil { + return fmt.Errorf("GetSenderInfo RPC call to remote node failed: %v", err) + } + info = i + } else { + info.Reserve.ClaimedInCurrentRound = big.NewInt(0) + } + } + + for sender := range sw.claimedReserve { + if log.Removed { + c, err := sw.lpEth.ClaimedReserve(sender, sw.lpEth.Account().Address) + if err != nil { + return fmt.Errorf("ClaimedReserve RPC call to remote node failed: %v", err) + } + sw.claimedReserve[sender] = c + } else { + sw.claimedReserve[sender] = big.NewInt(0) + } + } + + return nil +} diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index eaaf3c6..16814f1 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -112,7 +112,10 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { require := require.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) // if not existant on map; make RPC call and init the map @@ -143,7 +146,10 @@ func TestSenderWatcher_WatchAndStop(t *testing.T) { assert := assert.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) go sw.Watch() @@ -154,9 +160,13 @@ func TestSenderWatcher_WatchAndStop(t *testing.T) { } func TestSenderWatcher_HandleLog(t *testing.T) { + assert := assert.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - rw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(t, err) assert := assert.New(t) @@ -165,7 +175,7 @@ func TestSenderWatcher_HandleLog(t *testing.T) { log := newStubBaseLog() log.Topics = []ethcommon.Hash{ethcommon.BytesToHash([]byte("foo"))} - err = rw.handleLog(log) + err = sw.handleLog(log) assert.Nil(err) } @@ -182,7 +192,10 @@ func TestFundDepositEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -248,7 +261,10 @@ func TestFundReserveEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -315,7 +331,10 @@ func TestWithdrawalEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -371,7 +390,6 @@ func TestWithdrawalEvent(t *testing.T) { func TestWinningTicketTransferEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) deposit, _ := new(big.Int).SetString("10000000000000000000", 10) reserve, _ := new(big.Int).SetString("5000000000000000000", 10) startDeposit, _ := new(big.Int).SetString("10000000000000000000", 10) @@ -390,7 +408,10 @@ func TestWinningTicketTransferEvent(t *testing.T) { TranscoderAddress: stubClaimant, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) header := defaultMiniHeader() @@ -420,7 +441,7 @@ func TestWinningTicketTransferEvent(t *testing.T) { info, ok = sw.senders[stubSender] assert.True(ok) assert.Equal(info.Deposit, startDeposit) - assert.Equal(new(big.Int).Add(info.Reserve.FundsRemaining, info.Reserve.ClaimedInCurrentRound), startReserve) + assert.Equal(info.Reserve.FundsRemaining, startReserve) // If map entry exists parse event log // ticket amount is 200 gwei @@ -480,7 +501,10 @@ func TestUnlockEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -539,7 +563,10 @@ func TestUnlockCancelledEvent(t *testing.T) { }, } watcher := &stubBlockWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth) + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) header := defaultMiniHeader() @@ -589,3 +616,97 @@ func TestUnlockCancelledEvent(t *testing.T) { _, ok = sw.senders[s] assert.False(ok) } + +// Test RoundsWatcher Subscription log added and log removed +func TestNewRoundEvent_LogAdded(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Reserve: &pm.ReserveInfo{ + ClaimedInCurrentRound: big.NewInt(1000), + }, + }, + ClaimedAmount: big.NewInt(1000), + } + + watcher := &stubBlockWatcher{} + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + require.Nil(err) + + // set initial values + info, err := sw.GetSenderInfo(stubSender) + require.Nil(err) + claimed, err := sw.ClaimedReserve(stubSender, stubClaimant) + require.Nil(err) + + newRoundEvent := newStubNewRoundLog() + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + rw.sink <- newRoundEvent + + time.Sleep(2 * time.Millisecond) + + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Zero(info.Reserve.ClaimedInCurrentRound.Int64()) + claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(err) + assert.Zero(claimed.Int64()) +} + +func TestNewRoundEvent_LogRemoved(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Reserve: &pm.ReserveInfo{ + ClaimedInCurrentRound: big.NewInt(1000), + }, + }, + ClaimedAmount: big.NewInt(1000), + } + + watcher := &stubBlockWatcher{} + + rw := &stubRoundsWatcher{} + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) + + newRoundEvent := newStubNewRoundLog() + + // set initial values + info, err := sw.GetSenderInfo(stubSender) + require.Nil(err) + claimed, err := sw.ClaimedReserve(stubSender, stubClaimant) + require.Nil(err) + + // change stub RPC call values + expectedClaimedInCurrentRound := big.NewInt(500) + expectedClaimedAmount := big.NewInt(2000) + lpEth.SenderInfo.Reserve.ClaimedInCurrentRound = expectedClaimedInCurrentRound + lpEth.ClaimedAmount = expectedClaimedAmount + + newRoundEvent.Removed = true + + go sw.Watch() + defer sw.Stop() + time.Sleep(2 * time.Millisecond) + + rw.sink <- newRoundEvent + time.Sleep(2 * time.Millisecond) + + info, err = sw.GetSenderInfo(stubSender) + assert.Nil(err) + assert.Equal(info.Reserve.ClaimedInCurrentRound, expectedClaimedInCurrentRound) + claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) + assert.Nil(err) + assert.Equal(claimed, expectedClaimedAmount) +} From a9ba8e1fa20dcc88de19201396a1da68e067a52c Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Sat, 5 Oct 2019 04:08:45 +0200 Subject: [PATCH 26/38] eth/watchers: refactor RoundsWatcher Subscription mechanism Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 1799b33eafcb50fa48171dd5509a50d05fb8fab4 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 3 ++- eth/watchers/senderwatcher_test.go | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index ea134ff..b6fd17b 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -88,7 +88,8 @@ func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claiman // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { roundEvents := make(chan types.Log, 10) - sw.rw.Subscribe(roundEvents) + roundSub := sw.rw.Subscribe(roundEvents) + defer roundSub.Unsubscribe() events := make(chan []*blockwatch.Event, 10) sub := sw.watcher.Subscribe(events) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 16814f1..0001971 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -118,6 +118,9 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) + // if not existant on map; make RPC call and init the map _, ok := sw.claimedReserve[stubSender] require.False(ok) @@ -143,6 +146,7 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { } func TestSenderWatcher_WatchAndStop(t *testing.T) { + require := require.New(t) assert := assert.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} @@ -169,7 +173,11 @@ func TestSenderWatcher_HandleLog(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(t, err) - assert := assert.New(t) + rw, err := NewRoundsWatcher(stubRoundsManagerAddr, watcher, lpEth) + require.Nil(err) + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) // Test unknown event log := newStubBaseLog() @@ -181,6 +189,7 @@ func TestSenderWatcher_HandleLog(t *testing.T) { func TestFundDepositEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) startDeposit := big.NewInt(10) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ @@ -249,6 +258,7 @@ func TestFundDepositEvent(t *testing.T) { func TestFundReserveEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) lpEth := ð.StubClient{ @@ -319,6 +329,7 @@ func TestFundReserveEvent(t *testing.T) { func TestWithdrawalEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) lpEth := ð.StubClient{ @@ -414,6 +425,9 @@ func TestWinningTicketTransferEvent(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) + header := defaultMiniHeader() newWinningTicketEvent := newStubWinningTicketLog() header.Logs = append(header.Logs, newWinningTicketEvent) @@ -494,6 +508,7 @@ func TestWinningTicketTransferEvent(t *testing.T) { func TestUnlockEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) startWithdrawRound := big.NewInt(5) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ @@ -557,6 +572,7 @@ func TestUnlockEvent(t *testing.T) { func TestUnlockCancelledEvent(t *testing.T) { assert := assert.New(t) + require := require.New(t) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ WithdrawRound: big.NewInt(10), @@ -634,8 +650,14 @@ func TestNewRoundEvent_LogAdded(t *testing.T) { rw := &stubRoundsWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + rw, err := NewRoundsWatcher(stubRoundsManagerAddr, watcherForRounds, lpEth) require.Nil(err) + go rw.Watch() + defer rw.Stop() + time.Sleep(2 * time.Millisecond) + + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + assert.Nil(err) // set initial values info, err := sw.GetSenderInfo(stubSender) From 561b05b82c584df9204af6937092ab6c8ebce6c1 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Sat, 5 Oct 2019 04:20:59 +0200 Subject: [PATCH 27/38] eth: remove LivepeerVerifier references Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: ebdaed880f96e07b72288090e54f1141e8b404ab Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher_test.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 0001971..7d5ee1c 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -118,9 +118,6 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) require.Nil(err) - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) - assert.Nil(err) - // if not existant on map; make RPC call and init the map _, ok := sw.claimedReserve[stubSender] require.False(ok) @@ -146,7 +143,6 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { } func TestSenderWatcher_WatchAndStop(t *testing.T) { - require := require.New(t) assert := assert.New(t) lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} @@ -170,12 +166,6 @@ func TestSenderWatcher_HandleLog(t *testing.T) { rw := &stubRoundsWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) - require.Nil(t, err) - - rw, err := NewRoundsWatcher(stubRoundsManagerAddr, watcher, lpEth) - require.Nil(err) - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) @@ -189,7 +179,6 @@ func TestSenderWatcher_HandleLog(t *testing.T) { func TestFundDepositEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) startDeposit := big.NewInt(10) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ @@ -258,7 +247,6 @@ func TestFundDepositEvent(t *testing.T) { func TestFundReserveEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) lpEth := ð.StubClient{ @@ -329,7 +317,6 @@ func TestFundReserveEvent(t *testing.T) { func TestWithdrawalEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) startDeposit := big.NewInt(10) startReserve := big.NewInt(5) lpEth := ð.StubClient{ @@ -422,9 +409,6 @@ func TestWinningTicketTransferEvent(t *testing.T) { rw := &stubRoundsWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) - require.Nil(err) - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) @@ -508,7 +492,6 @@ func TestWinningTicketTransferEvent(t *testing.T) { func TestUnlockEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) startWithdrawRound := big.NewInt(5) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ @@ -572,7 +555,6 @@ func TestUnlockEvent(t *testing.T) { func TestUnlockCancelledEvent(t *testing.T) { assert := assert.New(t) - require := require.New(t) lpEth := ð.StubClient{ SenderInfo: &pm.SenderInfo{ WithdrawRound: big.NewInt(10), @@ -650,12 +632,6 @@ func TestNewRoundEvent_LogAdded(t *testing.T) { rw := &stubRoundsWatcher{} - rw, err := NewRoundsWatcher(stubRoundsManagerAddr, watcherForRounds, lpEth) - require.Nil(err) - go rw.Watch() - defer rw.Stop() - time.Sleep(2 * time.Millisecond) - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) assert.Nil(err) From 8f731a8612c469795cbfe69a01a03e9f11a448a9 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Tue, 26 Nov 2019 18:14:38 +0100 Subject: [PATCH 28/38] eth, cmd: cache O stake on new round event Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 70d62a01a49014ce2650f27b7c70b58af5b6f31b Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index b6fd17b..727d79f 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -104,7 +104,9 @@ func (sw *SenderWatcher) Watch() { case events := <-events: sw.handleBlockEvents(events) case roundEvent := <-roundEvents: - sw.handleRoundEvent(roundEvent) + if err := sw.handleRoundEvent(roundEvent); err != nil { + glog.Errorf("error handling new round event: %v", err) + } } } } From b5d39e2a27d62a65c78013321105e2bfa3ea1d9a Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 6 Feb 2020 03:31:58 +0100 Subject: [PATCH 29/38] eth/watchers: extend roundsWatcher to be a more generic timeWatcher that watchers for both latest rounds and latest blocks Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: b4605f705bcad5e355bb0c738d784e4503089900 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 8 ++--- eth/watchers/senderwatcher_test.go | 48 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 727d79f..f84703b 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -21,13 +21,13 @@ type SenderWatcher struct { mu sync.RWMutex quit chan struct{} watcher BlockWatcher - rw EventWatcher + tw timeWatcher lpEth eth.LivepeerEthClient dec *EventDecoder } // NewSenderWatcher initiates a new SenderWatcher -func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient, rw EventWatcher) (*SenderWatcher, error) { +func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, lpEth eth.LivepeerEthClient, tw timeWatcher) (*SenderWatcher, error) { dec, err := NewEventDecoder(ticketBrokerAddr, contracts.TicketBrokerABI) if err != nil { return nil, err @@ -36,7 +36,7 @@ func NewSenderWatcher(ticketBrokerAddr ethcommon.Address, watcher BlockWatcher, return &SenderWatcher{ quit: make(chan struct{}), watcher: watcher, - rw: rw, + tw: tw, lpEth: lpEth, senders: make(map[ethcommon.Address]*pm.SenderInfo), claimedReserve: make(map[ethcommon.Address]*big.Int), @@ -88,7 +88,7 @@ func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claiman // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { roundEvents := make(chan types.Log, 10) - roundSub := sw.rw.Subscribe(roundEvents) + roundSub := sw.tw.SubscribeRounds(roundEvents) defer roundSub.Unsubscribe() events := make(chan []*blockwatch.Event, 10) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 7d5ee1c..3e761d6 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -113,9 +113,9 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) require.Nil(err) // if not existant on map; make RPC call and init the map @@ -147,9 +147,9 @@ func TestSenderWatcher_WatchAndStop(t *testing.T) { lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) go sw.Watch() @@ -164,9 +164,9 @@ func TestSenderWatcher_HandleLog(t *testing.T) { lpEth := ð.StubClient{} watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) // Test unknown event @@ -191,9 +191,9 @@ func TestFundDepositEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -260,9 +260,9 @@ func TestFundReserveEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -330,9 +330,9 @@ func TestWithdrawalEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -407,9 +407,9 @@ func TestWinningTicketTransferEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -500,9 +500,9 @@ func TestUnlockEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -562,9 +562,9 @@ func TestUnlockCancelledEvent(t *testing.T) { } watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) header := defaultMiniHeader() @@ -630,9 +630,9 @@ func TestNewRoundEvent_LogAdded(t *testing.T) { watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) // set initial values @@ -647,7 +647,7 @@ func TestNewRoundEvent_LogAdded(t *testing.T) { defer sw.Stop() time.Sleep(2 * time.Millisecond) - rw.sink <- newRoundEvent + tw.sink <- newRoundEvent time.Sleep(2 * time.Millisecond) @@ -673,9 +673,9 @@ func TestNewRoundEvent_LogRemoved(t *testing.T) { watcher := &stubBlockWatcher{} - rw := &stubRoundsWatcher{} + tw := &stubTimeWatcher{} - sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, rw) + sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) assert.Nil(err) newRoundEvent := newStubNewRoundLog() @@ -698,7 +698,7 @@ func TestNewRoundEvent_LogRemoved(t *testing.T) { defer sw.Stop() time.Sleep(2 * time.Millisecond) - rw.sink <- newRoundEvent + tw.sink <- newRoundEvent time.Sleep(2 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) From a0bf1fbb437c80c0286bd9c96edbd801b7044785 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 4 Jun 2020 23:45:21 +0200 Subject: [PATCH 30/38] eth,pm: SubscribeReserveChange subscription for LocalSenderMonitor Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 46f074f0cfe6eb794f73bff15476bfb3f7094aa4 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 15 +++++++ eth/watchers/senderwatcher_test.go | 72 ++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index f84703b..c78e75e 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -7,6 +7,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" "github.com/golang/glog" "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/blockwatch" @@ -24,6 +25,10 @@ type SenderWatcher struct { tw timeWatcher lpEth eth.LivepeerEthClient dec *EventDecoder + + // subscriptions + reserveChangeFeed event.Feed + reserveChangeScope event.SubscriptionScope } // NewSenderWatcher initiates a new SenderWatcher @@ -114,6 +119,7 @@ func (sw *SenderWatcher) Watch() { // Stop watching for events func (sw *SenderWatcher) Stop() { close(sw.quit) + sw.reserveChangeScope.Close() } // Clear removes a key-value pair from the map @@ -128,6 +134,11 @@ func (sw *SenderWatcher) Clear(addr ethcommon.Address) { } } +// SubscribeReserveChange notifies subscribers when the sender info for a particular sender changes +func (sw *SenderWatcher) SubscribeReserveChange(sink chan<- ethcommon.Address) event.Subscription { + return sw.reserveChangeScope.Track(sw.reserveChangeFeed.Subscribe(sink)) +} + func (sw *SenderWatcher) handleBlockEvents(events []*blockwatch.Event) { for _, event := range events { for _, log := range event.BlockHeader.Logs { @@ -170,6 +181,7 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { sender = reserveFunded.ReserveHolder if info, ok := sw.senders[sender]; ok && !log.Removed { info.Reserve.FundsRemaining.Add(info.Reserve.FundsRemaining, reserveFunded.Amount) + sw.reserveChangeFeed.Send(sender) } case "Withdrawal": var withdrawal contracts.TicketBrokerWithdrawal @@ -244,6 +256,9 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { return fmt.Errorf("GetSenderInfo RPC call to remote node failed: %v", err) } sw.senders[sender] = info + if eventName == "ReserveFunded" { + sw.reserveChangeFeed.Send(sender) + } } return nil diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 3e761d6..33eb2bf 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -708,3 +708,75 @@ func TestNewRoundEvent_LogRemoved(t *testing.T) { assert.Nil(err) assert.Equal(claimed, expectedClaimedAmount) } + +func TestSubscribeReserveChange(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + watcher := &stubBlockWatcher{} + lpEth := ð.StubClient{ + SenderInfo: &pm.SenderInfo{ + Reserve: &pm.ReserveInfo{ + FundsRemaining: big.NewInt(100), + }, + }, + } + + sm, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, &stubTimeWatcher{}) + require.Nil(err) + + header := defaultMiniHeader() + + // Test reserve funded + fundedEvent := newStubReserveFundedLog() + header.Logs = append(header.Logs, fundedEvent) + + blockEvent := &blockwatch.Event{ + Type: blockwatch.Added, + BlockHeader: header, + } + + sink := make(chan ethcommon.Address, 10) + sub := sm.SubscribeReserveChange(sink) + defer sub.Unsubscribe() + + go sm.Watch() + defer sm.Stop() + time.Sleep(time.Millisecond) + + // set initial values + _, err = sm.GetSenderInfo(stubSender) + require.Nil(err) + + sm.handleBlockEvents([]*blockwatch.Event{blockEvent}) + sender := <-sink + assert.Equal(sender, stubSender) + + // Test log removed + blockEvent.Type = blockwatch.Removed + sm.handleBlockEvents([]*blockwatch.Event{blockEvent}) + sender = <-sink + assert.Equal(sender, stubSender) + + // Test log removed for event that is not ReserveFunded (no value received on the sink channel) + header = defaultMiniHeader() + header.Logs = append(header.Logs, newStubBaseLog()) + blockEvent = &blockwatch.Event{ + Type: blockwatch.Removed, + BlockHeader: header, + } + sm.handleBlockEvents([]*blockwatch.Event{blockEvent}) + + waitC := make(chan struct{}) + defer close(waitC) + go func() { + time.Sleep(1 * time.Second) + waitC <- struct{}{} + }() + + select { + case _, ok := <-waitC: + assert.True(ok) + case <-sink: + t.Fail() + } +} From 7ae54c636c78fb1dce56e5c2c764d5c6d192bbb3 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Wed, 17 Jun 2020 02:31:13 +0200 Subject: [PATCH 31/38] monitor, eth: log deposit and reserve for the broadcaster only when monitor is enabled Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 06245d9dbb2878bee37680aef263a892f6ec8f62 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index c78e75e..51298dc 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -12,6 +12,7 @@ import ( "github.com/livepeer/go-livepeer/eth" "github.com/livepeer/go-livepeer/eth/blockwatch" "github.com/livepeer/go-livepeer/eth/contracts" + "github.com/livepeer/go-livepeer/monitor" "github.com/livepeer/go-livepeer/pm" ) @@ -261,6 +262,11 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { } } + if sender == sw.lpEth.Account().Address && monitor.Enabled { + monitor.Deposit(sender.Hex(), sw.senders[sender].Deposit) + monitor.Reserve(sender.Hex(), sw.senders[sender].Reserve.FundsRemaining) + } + return nil } From 9f9269883c2102dd7e41cf3199a1b7f567c0cf0f Mon Sep 17 00:00:00 2001 From: Yondon Fu Date: Tue, 30 Jun 2020 17:36:02 -0400 Subject: [PATCH 32/38] eth/watchers: Log deposit/reserve when caching sender info Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: aa6f7d578ad93c0fa36226f105c41452b6119e2f Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 51298dc..5f6c173 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -71,6 +71,11 @@ func (sw *SenderWatcher) setSenderInfo(addr ethcommon.Address, info *pm.SenderIn sw.mu.Lock() defer sw.mu.Unlock() sw.senders[addr] = info + + if addr == sw.lpEth.Account().Address && monitor.Enabled { + monitor.Deposit(addr.Hex(), info.Deposit) + monitor.Reserve(addr.Hex(), info.Reserve.FundsRemaining) + } } // ClaimedReserve returns the amount claimed from a sender's reserve by the node operator From ff22002fda851d87153b133196151e7675c533c4 Mon Sep 17 00:00:00 2001 From: Nico Vergauwen Date: Thu, 25 Jun 2020 23:05:22 +0200 Subject: [PATCH 33/38] cmd, core: allow orchestrator+redeemer mode without starting transcode server. This results in a node that can run all on-chain services using the on-chain registered keys Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: c162e069f2559108641807bbcfacf3fab884e154 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 33eb2bf..5b89844 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -207,11 +207,11 @@ func TestFundDepositEvent(t *testing.T) { go sw.Watch() defer sw.Stop() - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) // If map entry is nil don't handle event watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) _, ok := sw.senders[stubSender] assert.False(ok) @@ -219,7 +219,7 @@ func TestFundDepositEvent(t *testing.T) { blockEvent.Type = blockwatch.Removed sw.senders[stubSender] = &pm.SenderInfo{} watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) info, ok := sw.senders[stubSender] assert.True(ok) assert.Zero(info.Deposit.Cmp(startDeposit)) @@ -228,7 +228,7 @@ func TestFundDepositEvent(t *testing.T) { blockEvent.Type = blockwatch.Added expectedDeposit := new(big.Int).Add(startDeposit, new(big.Int).SetBytes(newDepositEvent.Data)) watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) info, err = sw.GetSenderInfo(stubSender) assert.Nil(err) assert.Zero(info.Deposit.Cmp(expectedDeposit)) @@ -240,7 +240,7 @@ func TestFundDepositEvent(t *testing.T) { copy(senderTopic[:], sender[:]) newDepositEvent.Topics[1] = senderTopic watcher.sink <- []*blockwatch.Event{blockEvent} - time.Sleep(2 * time.Millisecond) + time.Sleep(20 * time.Millisecond) _, ok = sw.senders[s] assert.False(ok) } From 0f8b29b02ed924e59cd4f2d2c0d74c85f67c0bde Mon Sep 17 00:00:00 2001 From: Rafal Leszko Date: Thu, 20 Jan 2022 15:55:03 +0100 Subject: [PATCH 34/38] eth: Handle watcher feeds asynchronously Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 87c6e404c7a8bd5552e66e346d3659d696e17f4f Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 5f6c173..0f4ceab 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -113,11 +113,15 @@ func (sw *SenderWatcher) Watch() { case err := <-sub.Err(): glog.Error(err) case events := <-events: - sw.handleBlockEvents(events) + go func() { + sw.handleBlockEvents(events) + }() case roundEvent := <-roundEvents: - if err := sw.handleRoundEvent(roundEvent); err != nil { - glog.Errorf("error handling new round event: %v", err) - } + go func() { + if err := sw.handleRoundEvent(roundEvent); err != nil { + glog.Errorf("error handling new round event: %v", err) + } + }() } } } From 0c88979d4b61dc985e530b5f08422537b3c64421 Mon Sep 17 00:00:00 2001 From: Rafal Leszko Date: Fri, 21 Jan 2022 09:11:42 +0100 Subject: [PATCH 35/38] eth: Refactor sink naming in the watcher feeds Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: f30819e43a6b0d9272301a402220d7df64d70063 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 0f4ceab..1b2d291 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -98,8 +98,8 @@ func (sw *SenderWatcher) ClaimedReserve(reserveHolder ethcommon.Address, claiman // Watch starts the event watching loop func (sw *SenderWatcher) Watch() { - roundEvents := make(chan types.Log, 10) - roundSub := sw.tw.SubscribeRounds(roundEvents) + roundSink := make(chan types.Log, 10) + roundSub := sw.tw.SubscribeRounds(roundSink) defer roundSub.Unsubscribe() events := make(chan []*blockwatch.Event, 10) @@ -116,9 +116,9 @@ func (sw *SenderWatcher) Watch() { go func() { sw.handleBlockEvents(events) }() - case roundEvent := <-roundEvents: + case round := <-roundSink: go func() { - if err := sw.handleRoundEvent(roundEvent); err != nil { + if err := sw.handleRoundEvent(round); err != nil { glog.Errorf("error handling new round event: %v", err) } }() From 63819b7c39cd3abde390488c799d8271a6f3bd32 Mon Sep 17 00:00:00 2001 From: Rafal Leszko Date: Thu, 27 Jan 2022 15:48:47 +0100 Subject: [PATCH 36/38] eth: Refactor go func() {} into go Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 754c1ed203539275332363eb2cd2940a7fa6c72a Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 1b2d291..00bff03 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -113,9 +113,7 @@ func (sw *SenderWatcher) Watch() { case err := <-sub.Err(): glog.Error(err) case events := <-events: - go func() { - sw.handleBlockEvents(events) - }() + go sw.handleBlockEvents(events) case round := <-roundSink: go func() { if err := sw.handleRoundEvent(round); err != nil { From 804b60e41159a5e16984c7309ff0f1afc7defd82 Mon Sep 17 00:00:00 2001 From: hjpotter92 Date: Thu, 13 Oct 2022 10:55:10 +0530 Subject: [PATCH 37/38] test.yaml: Add misspell checker in lint/test jobs. Fix typos across project. Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 12358f5082b18a3e200dd99534ea92f91baea258 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/watchers/senderwatcher.go b/eth/watchers/senderwatcher.go index 00bff03..3786107 100644 --- a/eth/watchers/senderwatcher.go +++ b/eth/watchers/senderwatcher.go @@ -217,7 +217,7 @@ func (sw *SenderWatcher) handleLog(log types.Log) error { // Draw from reserve diff := new(big.Int).Sub(amount, info.Deposit) info.Deposit = big.NewInt(0) - // Substract the difference from the remaining reserve + // Subtract the difference from the remaining reserve info.Reserve.FundsRemaining.Sub(info.Reserve.FundsRemaining, diff) // Add the difference to the amount claimed from the reserve in the current round info.Reserve.ClaimedInCurrentRound.Add(info.Reserve.ClaimedInCurrentRound, diff) From 30bd2b71eebfb5130e440839c5b6e4697424097f Mon Sep 17 00:00:00 2001 From: houpo-bob Date: Fri, 25 Jul 2025 17:59:39 +0800 Subject: [PATCH 38/38] chore: fix some typos in comment (#3687) Signed-off-by: houpo-bob Source-Repository: https://github.com/livepeer/go-livepeer Original-Commit: 9755a0dd280821a87e73d40871fc7f2775f150f5 Source-Scope: eth/watchers/senderwatcher.go; eth/watchers/senderwatcher_test.go --- eth/watchers/senderwatcher_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/watchers/senderwatcher_test.go b/eth/watchers/senderwatcher_test.go index 5b89844..88be020 100644 --- a/eth/watchers/senderwatcher_test.go +++ b/eth/watchers/senderwatcher_test.go @@ -118,7 +118,7 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { sw, err := NewSenderWatcher(stubTicketBrokerAddr, watcher, lpEth, tw) require.Nil(err) - // if not existant on map; make RPC call and init the map + // if not existent on map; make RPC call and init the map _, ok := sw.claimedReserve[stubSender] require.False(ok) lpEth.ClaimedAmount = big.NewInt(5000) @@ -126,7 +126,7 @@ func TestSenderWatcher_ClaimedReserve(t *testing.T) { assert.Nil(err) assert.Zero(lpEth.ClaimedAmount.Cmp(claimed)) - // if existant on map return the map value + // if existent on map return the map value sw.claimedReserve[stubSender] = big.NewInt(10000) claimed, err = sw.ClaimedReserve(stubSender, stubClaimant) assert.Nil(err)