diff --git a/autopilot/contractor/contractor.go b/autopilot/contractor/contractor.go index b452a21c4..af16e4998 100644 --- a/autopilot/contractor/contractor.go +++ b/autopilot/contractor/contractor.go @@ -198,7 +198,7 @@ func (c *Contractor) formContract(ctx *mCtx, hs HostScanner, host api.Host, minI } // shouldn't go below the minimum immediately so we add some buffer - minCollateral := MinCollateral.Mul64(2) + minCollateral := MinCollateral.Mul64(2).Add(contractPrice) if hostCollateral.Cmp(minCollateral) < 0 { hostCollateral = minCollateral } @@ -255,14 +255,17 @@ func (c *Contractor) refreshContract(ctx *mCtx, contract contract, host api.Host } var expectedNewStorage uint64 + var contractPrice types.Currency if host.IsV2() { + contractPrice = host.V2Settings.Prices.ContractPrice expectedNewStorage = renterFundsToExpectedStorageV2(renterFunds, contract.EndHeight()-cs.BlockHeight, host.V2Settings.Prices) } else { + contractPrice = host.PriceTable.ContractPrice expectedNewStorage = renterFundsToExpectedStorage(renterFunds, contract.EndHeight()-cs.BlockHeight, pt) } // a refresh should always result in a contract that has enough collateral - minNewCollateral := MinCollateral.Mul64(2) + minNewCollateral := MinCollateral.Mul64(2).Add(contractPrice) // renew the contract renewal, err := c.bus.RenewContract(ctx, contract.ID, contract.EndHeight(), renterFunds, minNewCollateral, expectedNewStorage) @@ -1041,7 +1044,9 @@ func performHostChecks(ctx *mCtx, bus Bus, logger *zap.SugaredLogger) error { return fmt.Errorf("failed to fetch consensus state: %w", err) } for _, h := range scoredHosts { - h.host.PriceTable.HostBlockHeight = cs.BlockHeight // ignore HostBlockHeight + // ignore HostBlockHeight + h.host.PriceTable.HostBlockHeight = cs.BlockHeight + h.host.V2Settings.Prices.TipHeight = cs.BlockHeight hc := checkHost(ctx.GougingChecker(cs), h, minScore, ctx.Period()) if err := bus.UpdateHostCheck(ctx, h.host.PublicKey, *hc); err != nil { return fmt.Errorf("failed to update host check for host %v: %w", h.host.PublicKey, err) diff --git a/autopilot/contractor/evaluate.go b/autopilot/contractor/evaluate.go index 0dca3c3a8..b5f4995f7 100644 --- a/autopilot/contractor/evaluate.go +++ b/autopilot/contractor/evaluate.go @@ -35,7 +35,9 @@ func EvaluateConfig(cfg api.AutopilotConfig, cs api.ConsensusState, rs api.Redun resp.Hosts = uint64(len(hosts)) for i, host := range hosts { - hosts[i].PriceTable.HostBlockHeight = cs.BlockHeight // ignore block height + // ignore block height + hosts[i].PriceTable.HostBlockHeight = cs.BlockHeight + hosts[i].V2Settings.Prices.TipHeight = cs.BlockHeight hc := checkHost(gc, scoreHost(host, cfg, gs, rs.Redundancy()), minValidScore, cfg.Contracts.Period) if hc.UsabilityBreakdown.IsUsable() { resp.Usable++ diff --git a/internal/accounts/accounts.go b/internal/accounts/accounts.go index 72d35ee9d..8e1731ebb 100644 --- a/internal/accounts/accounts.go +++ b/internal/accounts/accounts.go @@ -341,7 +341,9 @@ func (a *Manager) refillAccounts() { // fetch all usable hosts hosts, err := a.hs.UsableHosts(a.shutdownCtx) - if err != nil { + if utils.IsErr(err, context.Canceled) { + return + } else if err != nil { a.logger.Errorw(fmt.Sprintf("failed to fetch usable hosts for refill: %v", err)) return } diff --git a/internal/bus/chainsubscriber.go b/internal/bus/chainsubscriber.go index 0224ab0f3..325a486c4 100644 --- a/internal/bus/chainsubscriber.go +++ b/internal/bus/chainsubscriber.go @@ -415,38 +415,42 @@ func (s *chainSubscriber) broadcastExpiredFileContractResolutions(tx sql.ChainUp s.logger.Errorf("failed to get expired file contract elements: %v", err) return } + for _, fce := range expiredFCEs { - txn := types.V2Transaction{ - MinerFee: s.cm.RecommendedFee().Mul64(ContractResolutionTxnWeight), - FileContractResolutions: []types.V2FileContractResolution{ - { - Parent: fce, - Resolution: &types.V2FileContractExpiration{}, + go func(fce types.V2FileContractElement) { + txn := types.V2Transaction{ + MinerFee: s.cm.RecommendedFee().Mul64(ContractResolutionTxnWeight), + FileContractResolutions: []types.V2FileContractResolution{ + { + Parent: fce, + Resolution: &types.V2FileContractExpiration{}, + }, }, - }, - } - // fund and sign txn - basis, toSign, err := s.wallet.FundV2Transaction(&txn, txn.MinerFee, true) - if err != nil { - s.logger.Errorf("failed to fund contract expiration txn: %v", err) - continue - } - s.wallet.SignV2Inputs(&txn, toSign) - - // verify txn and broadcast it - _, err = s.cm.AddV2PoolTransactions(basis, []types.V2Transaction{txn}) - if err != nil && - (strings.Contains(err.Error(), "has already been resolved") || - strings.Contains(err.Error(), "not present in the accumulator")) { - s.wallet.ReleaseInputs(nil, []types.V2Transaction{txn}) - s.logger.With(zap.Error(err)).Debug("failed to broadcast contract expiration txn") - continue - } else if err != nil { - s.logger.With(zap.Error(err)).Error("failed to broadcast contract expiration txn") - s.wallet.ReleaseInputs(nil, []types.V2Transaction{txn}) - continue - } - s.s.BroadcastV2TransactionSet(basis, []types.V2Transaction{txn}) + } + + // fund and sign txn + basis, toSign, err := s.wallet.FundV2Transaction(&txn, txn.MinerFee, true) + if err != nil { + s.logger.Errorf("failed to fund contract expiration txn: %v", err) + return + } + s.wallet.SignV2Inputs(&txn, toSign) + + // verify txn and broadcast it + _, err = s.cm.AddV2PoolTransactions(basis, []types.V2Transaction{txn}) + if err != nil && + (strings.Contains(err.Error(), "has already been resolved") || + strings.Contains(err.Error(), "not present in the accumulator")) { + s.wallet.ReleaseInputs(nil, []types.V2Transaction{txn}) + s.logger.With(zap.Error(err)).Debug("failed to broadcast contract expiration txn") + return + } else if err != nil { + s.logger.With(zap.Error(err)).Error("failed to broadcast contract expiration txn") + s.wallet.ReleaseInputs(nil, []types.V2Transaction{txn}) + return + } + s.s.BroadcastV2TransactionSet(basis, []types.V2Transaction{txn}) + }(fce) } } diff --git a/internal/test/e2e/build_v2.go b/internal/test/e2e/build_v2.go index ac79a448f..6bf89372b 100644 --- a/internal/test/e2e/build_v2.go +++ b/internal/test/e2e/build_v2.go @@ -5,5 +5,5 @@ package e2e // configuration for post-v2-hardfork tests const ( HardforkV2AllowHeight = 2 - HardforkV2RequireHeight = 30000 // TODO: change this to 3 once all the code is updated to use v2 above the allow height + HardforkV2RequireHeight = 3 ) diff --git a/internal/test/e2e/cluster.go b/internal/test/e2e/cluster.go index f5d34a454..17dde4647 100644 --- a/internal/test/e2e/cluster.go +++ b/internal/test/e2e/cluster.go @@ -335,13 +335,15 @@ func newTestCluster(t *testing.T, opts testClusterOptions) *TestCluster { }))) cm := opts.cm - network, genesis := testNetwork() if cm == nil { // create chain manager + network, genesis := testNetwork() store, state, err := chain.NewDBStore(chain.NewMemDB(), network, genesis) tt.OK(err) cm = chain.NewManager(store, state) } + network := cm.TipState().Network + genesis := types.Block{Timestamp: network.HardforkOak.GenesisTimestamp} // Create bus. busDir := filepath.Join(dir, "bus") diff --git a/internal/test/e2e/cluster_test.go b/internal/test/e2e/cluster_test.go index f6c989dd8..b7cdac701 100644 --- a/internal/test/e2e/cluster_test.go +++ b/internal/test/e2e/cluster_test.go @@ -2932,7 +2932,7 @@ func TestV1ToV2Transition(t *testing.T) { apCfg := test.AutopilotConfig apCfg.Contracts.Amount = 2 apCfg.Contracts.Period = 1000 // make sure we handle trying to form contracts with a proof height after the v2 require height - apCfg.Contracts.RenewWindow = 50 + apCfg.Contracts.RenewWindow = 1 // create a test cluster nHosts := 3 @@ -2949,7 +2949,7 @@ func TestV1ToV2Transition(t *testing.T) { cluster.AddHosts(nHosts) // make sure we are still before the v2 allow height - if cm.Tip().Height >= network.HardforkV2.AllowHeight { + if cluster.IsPassedV2AllowHeight() { t.Fatal("should be before the v2 allow height") } diff --git a/internal/test/e2e/host.go b/internal/test/e2e/host.go index bc1adb8f1..fc3d1194c 100644 --- a/internal/test/e2e/host.go +++ b/internal/test/e2e/host.go @@ -6,7 +6,6 @@ import ( "net" "os" "path/filepath" - "sync" "time" "go.sia.tech/core/consensus" @@ -37,73 +36,6 @@ const ( blocksPerMonth = blocksPerDay * 30 ) -type ephemeralPeerStore struct { - peers map[string]syncer.PeerInfo - bans map[string]time.Time - mu sync.Mutex -} - -func (eps *ephemeralPeerStore) AddPeer(addr string) error { - eps.mu.Lock() - defer eps.mu.Unlock() - eps.peers[addr] = syncer.PeerInfo{Address: addr} - return nil -} - -func (eps *ephemeralPeerStore) Peers() ([]syncer.PeerInfo, error) { - eps.mu.Lock() - defer eps.mu.Unlock() - var peers []syncer.PeerInfo - for _, peer := range eps.peers { - peers = append(peers, peer) - } - return peers, nil -} - -func (eps *ephemeralPeerStore) PeerInfo(addr string) (syncer.PeerInfo, error) { - eps.mu.Lock() - defer eps.mu.Unlock() - peer, ok := eps.peers[addr] - if !ok { - return syncer.PeerInfo{}, syncer.ErrPeerNotFound - } - return peer, nil -} - -func (eps *ephemeralPeerStore) UpdatePeerInfo(addr string, fn func(*syncer.PeerInfo)) error { - eps.mu.Lock() - defer eps.mu.Unlock() - peer, ok := eps.peers[addr] - if !ok { - return syncer.ErrPeerNotFound - } - fn(&peer) - eps.peers[addr] = peer - return nil -} - -func (eps *ephemeralPeerStore) Ban(addr string, duration time.Duration, reason string) error { - eps.mu.Lock() - defer eps.mu.Unlock() - eps.bans[addr] = time.Now().Add(duration) - return nil -} - -// Banned returns true, nil if the peer is banned. -func (eps *ephemeralPeerStore) Banned(addr string) (bool, error) { - eps.mu.Lock() - defer eps.mu.Unlock() - t, ok := eps.bans[addr] - return ok && time.Now().Before(t), nil -} - -func newEphemeralPeerStore() syncer.PeerStore { - return &ephemeralPeerStore{ - peers: make(map[string]syncer.PeerInfo), - bans: make(map[string]time.Time), - } -} - // A Host is an ephemeral host that can be used for testing. type Host struct { dir string @@ -235,7 +167,7 @@ func NewHost(privKey types.PrivateKey, cm *chain.Manager, dir string, network *c if err != nil { return nil, fmt.Errorf("failed to create syncer listener: %w", err) } - s := syncer.New(l, cm, newEphemeralPeerStore(), gateway.Header{ + s := syncer.New(l, cm, testutil.NewEphemeralPeerStore(), gateway.Header{ GenesisID: genesisBlock.ID(), UniqueID: gateway.GenerateUniqueID(), NetAddress: l.Addr().String(),