2017-12-28 18:14:33 +01:00
|
|
|
package ytsync
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2018-03-09 17:47:38 +01:00
|
|
|
"github.com/lbryio/lbry.go/errors"
|
2017-12-28 18:14:33 +01:00
|
|
|
"github.com/lbryio/lbry.go/jsonrpc"
|
|
|
|
"github.com/lbryio/lbry.go/lbrycrd"
|
|
|
|
|
|
|
|
"github.com/shopspring/decimal"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (s *Sync) walletSetup() error {
|
2018-06-06 23:47:28 +02:00
|
|
|
//prevent unnecessary concurrent execution
|
2018-08-14 16:48:55 +02:00
|
|
|
s.walletMux.Lock()
|
|
|
|
defer s.walletMux.Unlock()
|
2018-01-10 23:07:16 +01:00
|
|
|
err := s.ensureChannelOwnership()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-12-28 18:14:33 +01:00
|
|
|
balanceResp, err := s.daemon.WalletBalance()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if balanceResp == nil {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("no response")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
balance := decimal.Decimal(*balanceResp)
|
|
|
|
log.Debugf("Starting balance is %s", balance.String())
|
|
|
|
|
2018-08-14 16:48:55 +02:00
|
|
|
var numOnSource int
|
2018-02-13 18:47:05 +01:00
|
|
|
if s.LbryChannelName == "@UCBerkeley" {
|
|
|
|
numOnSource = 10104
|
|
|
|
} else {
|
2018-08-14 16:48:55 +02:00
|
|
|
n, err := s.CountVideos()
|
2018-02-13 18:47:05 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-14 16:48:55 +02:00
|
|
|
numOnSource = int(n)
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
log.Debugf("Source channel has %d videos", numOnSource)
|
2018-06-15 23:03:28 +02:00
|
|
|
if numOnSource == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2017-12-28 18:14:33 +01:00
|
|
|
|
2018-08-23 00:28:31 +02:00
|
|
|
s.syncedVideosMux.RLock()
|
2018-08-14 16:48:55 +02:00
|
|
|
numPublished := len(s.syncedVideos) //should we only count published videos? Credits are allocated even for failed ones...
|
2018-08-23 00:28:31 +02:00
|
|
|
s.syncedVideosMux.RUnlock()
|
2018-09-20 01:05:47 +02:00
|
|
|
log.Debugf("We already allocated credits for %d videos", numPublished)
|
2017-12-28 18:14:33 +01:00
|
|
|
|
2018-09-26 06:08:18 +02:00
|
|
|
if numOnSource-numPublished > s.Manager.videosLimit {
|
|
|
|
numOnSource = s.Manager.videosLimit
|
2018-07-24 02:01:35 +02:00
|
|
|
}
|
2018-07-31 01:19:12 +02:00
|
|
|
|
2018-05-26 02:43:16 +02:00
|
|
|
minBalance := (float64(numOnSource)-float64(numPublished))*(publishAmount+0.1) + channelClaimAmount
|
2018-08-08 11:55:27 +02:00
|
|
|
if numPublished > numOnSource && balance.LessThan(decimal.NewFromFloat(1)) {
|
2018-07-24 02:01:35 +02:00
|
|
|
SendErrorToSlack("something is going on as we published more videos than those available on source: %d/%d", numPublished, numOnSource)
|
2018-05-07 22:26:46 +02:00
|
|
|
minBalance = 1 //since we ended up in this function it means some juice is still needed
|
|
|
|
}
|
2017-12-28 18:14:33 +01:00
|
|
|
amountToAdd, _ := decimal.NewFromFloat(minBalance).Sub(balance).Float64()
|
2018-03-12 21:58:37 +01:00
|
|
|
|
|
|
|
if s.Refill > 0 {
|
|
|
|
if amountToAdd < 0 {
|
|
|
|
amountToAdd = float64(s.Refill)
|
|
|
|
} else {
|
|
|
|
amountToAdd += float64(s.Refill)
|
|
|
|
}
|
|
|
|
}
|
2017-12-28 18:14:33 +01:00
|
|
|
|
|
|
|
if amountToAdd > 0 {
|
2018-01-09 16:49:21 +01:00
|
|
|
if amountToAdd < 1 {
|
2018-03-12 21:58:37 +01:00
|
|
|
amountToAdd = 1 // no reason to bother adding less than 1 credit
|
2018-01-09 16:49:21 +01:00
|
|
|
}
|
2018-01-10 23:07:16 +01:00
|
|
|
s.addCredits(amountToAdd)
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
claimAddress, err := s.daemon.WalletUnusedAddress()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if claimAddress == nil {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("could not get unused address")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
s.claimAddress = string(*claimAddress)
|
|
|
|
if s.claimAddress == "" {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("found blank claim address")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
err = s.ensureEnoughUTXOs()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Sync) ensureEnoughUTXOs() error {
|
|
|
|
utxolist, err := s.daemon.UTXOList()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if utxolist == nil {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("no response")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
2018-05-05 18:18:44 +02:00
|
|
|
target := 40
|
2018-08-10 02:59:52 +02:00
|
|
|
slack := int(float32(0.1) * float32(target))
|
2017-12-28 18:14:33 +01:00
|
|
|
count := 0
|
|
|
|
|
|
|
|
for _, utxo := range *utxolist {
|
|
|
|
if !utxo.IsClaim && !utxo.IsSupport && !utxo.IsUpdate && utxo.Amount.Cmp(decimal.New(0, 0)) == 1 {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-08 23:59:59 +02:00
|
|
|
if count < target-slack {
|
2017-12-28 18:14:33 +01:00
|
|
|
newAddresses := target - count
|
|
|
|
|
|
|
|
balance, err := s.daemon.WalletBalance()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if balance == nil {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("no response")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Println("balance is " + decimal.Decimal(*balance).String())
|
|
|
|
|
|
|
|
amountPerAddress := decimal.Decimal(*balance).Div(decimal.NewFromFloat(float64(target)))
|
|
|
|
log.Infof("Putting %s credits into each of %d new addresses", amountPerAddress.String(), newAddresses)
|
|
|
|
prefillTx, err := s.daemon.WalletPrefillAddresses(newAddresses, amountPerAddress, true)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if prefillTx == nil {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("no response")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
2018-08-03 23:19:36 +02:00
|
|
|
err = s.waitForNewBlock()
|
2017-12-28 18:14:33 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-03 19:50:17 +02:00
|
|
|
} else if !allUTXOsConfirmed(utxolist) {
|
|
|
|
log.Println("Waiting for previous txns to confirm")
|
2018-08-03 23:19:36 +02:00
|
|
|
err := s.waitForNewBlock()
|
2018-08-03 19:50:17 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-08-03 23:19:36 +02:00
|
|
|
func (s *Sync) waitForNewBlock() error {
|
|
|
|
status, err := s.daemon.Status()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-10 02:19:37 +02:00
|
|
|
for status.Wallet.Blocks == 0 || status.Wallet.BlocksBehind != 0 {
|
2018-08-03 23:19:36 +02:00
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
status, err = s.daemon.Status()
|
2017-12-28 18:14:33 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-03 23:19:36 +02:00
|
|
|
}
|
2018-08-10 02:19:37 +02:00
|
|
|
currentBlock := status.Wallet.Blocks
|
|
|
|
for i := 0; status.Wallet.Blocks <= currentBlock; i++ {
|
2018-08-03 23:19:36 +02:00
|
|
|
if i%3 == 0 {
|
|
|
|
log.Printf("Waiting for new block (%d)...", currentBlock+1)
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
2018-08-03 23:19:36 +02:00
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
status, err = s.daemon.Status()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-05-05 16:15:02 +02:00
|
|
|
}
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
2018-08-03 23:19:36 +02:00
|
|
|
return nil
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Sync) ensureChannelOwnership() error {
|
|
|
|
if s.LbryChannelName == "" {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("no channel name set")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
2018-03-15 19:39:43 +01:00
|
|
|
channels, err := s.daemon.ChannelList()
|
2017-12-28 18:14:33 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if channels == nil {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("no channel response")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
isChannelMine := false
|
|
|
|
for _, channel := range *channels {
|
|
|
|
if channel.Name == s.LbryChannelName {
|
2018-08-03 23:19:36 +02:00
|
|
|
s.lbryChannelID = channel.ClaimID
|
2017-12-28 18:14:33 +01:00
|
|
|
isChannelMine = true
|
|
|
|
} else {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("this wallet has multiple channels. maybe something went wrong during setup?")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if isChannelMine {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
resolveResp, err := s.daemon.Resolve(s.LbryChannelName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
channel := (*resolveResp)[s.LbryChannelName]
|
|
|
|
channelBidAmount := channelClaimAmount
|
|
|
|
|
|
|
|
channelNotFound := channel.Error != nil && strings.Contains(*(channel.Error), "cannot be resolved")
|
|
|
|
if !channelNotFound {
|
|
|
|
if !s.TakeOverExistingChannel {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("Channel exists and we don't own it. Pick another channel.")
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
log.Println("Channel exists and we don't own it. Outbidding existing claim.")
|
|
|
|
channelBidAmount, _ = channel.Certificate.Amount.Add(decimal.NewFromFloat(channelClaimAmount)).Float64()
|
|
|
|
}
|
|
|
|
|
2018-01-10 23:07:16 +01:00
|
|
|
balanceResp, err := s.daemon.WalletBalance()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if balanceResp == nil {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("no response")
|
2018-01-10 23:07:16 +01:00
|
|
|
}
|
|
|
|
balance := decimal.Decimal(*balanceResp)
|
|
|
|
|
|
|
|
if balance.LessThan(decimal.NewFromFloat(channelBidAmount)) {
|
|
|
|
s.addCredits(channelBidAmount + 0.1)
|
|
|
|
}
|
|
|
|
|
2018-08-03 23:19:36 +02:00
|
|
|
c, err := s.daemon.ChannelNew(s.LbryChannelName, channelBidAmount)
|
2017-12-28 18:14:33 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-03 23:19:36 +02:00
|
|
|
s.lbryChannelID = c.ClaimID
|
2017-12-28 18:14:33 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func allUTXOsConfirmed(utxolist *jsonrpc.UTXOListResponse) bool {
|
|
|
|
if utxolist == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(*utxolist) < 1 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, utxo := range *utxolist {
|
|
|
|
if utxo.Height == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
2018-01-10 23:07:16 +01:00
|
|
|
|
|
|
|
func (s *Sync) addCredits(amountToAdd float64) error {
|
|
|
|
log.Printf("Adding %f credits", amountToAdd)
|
2018-08-08 23:59:59 +02:00
|
|
|
var lbrycrdd *lbrycrd.Client
|
|
|
|
var err error
|
|
|
|
if s.LbrycrdString == "" {
|
|
|
|
lbrycrdd, err = lbrycrd.NewWithDefaultURL()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lbrycrdd, err = lbrycrd.New(s.LbrycrdString)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-01-10 23:07:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
addressResp, err := s.daemon.WalletUnusedAddress()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
} else if addressResp == nil {
|
2018-03-09 17:47:38 +01:00
|
|
|
return errors.Err("no response")
|
2018-01-10 23:07:16 +01:00
|
|
|
}
|
|
|
|
address := string(*addressResp)
|
|
|
|
|
|
|
|
_, err = lbrycrdd.SimpleSend(address, amountToAdd)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
wait := 15 * time.Second
|
|
|
|
log.Println("Waiting " + wait.String() + " for lbryum to let us know we have the new transaction")
|
|
|
|
time.Sleep(wait)
|
|
|
|
|
2018-06-15 23:03:28 +02:00
|
|
|
return nil
|
2018-01-10 23:07:16 +01:00
|
|
|
}
|