Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rhp4,chain: simplify renew, move proof updates to chain manager #102

Merged
merged 3 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 59 additions & 20 deletions chain/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ func (m *Manager) V2TransactionSet(basis types.ChainIndex, txn types.V2Transacti
m.revalidatePool()

// update the transaction's basis to match tip
_, txns, err := m.updateV2TransactionSet(basis, []types.V2Transaction{txn})
txns, err := m.UpdateV2TransactionSet([]types.V2Transaction{txn}, basis, m.tipState.Index)
if err != nil {
return types.ChainIndex{}, nil, fmt.Errorf("failed to update transaction set basis: %w", err)
} else if len(txns) == 0 {
Expand Down Expand Up @@ -1086,47 +1086,86 @@ func (m *Manager) AddPoolTransactions(txns []types.Transaction) (known bool, err
return
}

// updateV2TransactionSet updates the basis of a transaction set to the current
// tip. If the basis is already the tip, the transaction set is returned as-is.
// Any transactions that were confirmed are removed from the set. Any ephemeral
// state elements that were created by an update are updated.
// UpdateStateElement updates the basis of a state element from "from" to "to".
// If from and to are equal, the state element is not modified.
func (m *Manager) UpdateStateElement(se *types.StateElement, from, to types.ChainIndex) error {
if from == to {
n8maninger marked this conversation as resolved.
Show resolved Hide resolved
return nil
}

revert, apply, err := m.reorgPath(from, to)
if err != nil {
return fmt.Errorf("couldn't determine reorg path from %v to %v: %w", from, to, err)
} else if len(revert)+len(apply) > 144 {
return fmt.Errorf("reorg path from %v to %v is too long (-%v +%v)", from, to, len(revert), len(apply))
}
for _, index := range revert {
b, _, cs, ok := blockAndParent(m.store, index.ID)
if !ok {
return fmt.Errorf("missing reverted block at index %v", index)
} else if b.V2 == nil {
return fmt.Errorf("reorg path from %v to %v contains a non-v2 block (%v)", from, to, index)
}
// NOTE: since we are post-hardfork, we don't need a v1 supplement
cru := consensus.RevertBlock(cs, b, consensus.V1BlockSupplement{})
cru.UpdateElementProof(se)
}

for _, index := range apply {
b, _, cs, ok := blockAndParent(m.store, index.ID)
if !ok {
return fmt.Errorf("missing applied block at index %v", index)
} else if b.V2 == nil {
return fmt.Errorf("reorg path from %v to %v contains a non-v2 block (%v)", from, to, index)
}
// NOTE: since we are post-hardfork, we don't need a v1 supplement or ancestorTimestamp
_, cau := consensus.ApplyBlock(cs, b, consensus.V1BlockSupplement{}, time.Time{})
cau.UpdateElementProof(se)
}
return nil
}

// UpdateV2TransactionSet updates the basis of a transaction set from "from" to "to".
// If from and to are equal, the transaction set is returned as-is.
// Any transactions that were confirmed are removed from the set.
// Any ephemeral state elements that were created by an update are updated.
//
// If it is undesirable to modify the transaction set, deep-copy it
// before calling this method.
func (m *Manager) updateV2TransactionSet(basis types.ChainIndex, txns []types.V2Transaction) (types.ChainIndex, []types.V2Transaction, error) {
if basis == m.tipState.Index {
return basis, txns, nil
func (m *Manager) UpdateV2TransactionSet(txns []types.V2Transaction, from, to types.ChainIndex) ([]types.V2Transaction, error) {
if from == to {
return txns, nil
}

// bring txns up-to-date
revert, apply, err := m.reorgPath(basis, m.tipState.Index)
revert, apply, err := m.reorgPath(from, to)
if err != nil {
return types.ChainIndex{}, nil, fmt.Errorf("couldn't determine reorg path from %v to %v: %w", basis, m.tipState.Index, err)
return nil, fmt.Errorf("couldn't determine reorg path from %v to %v: %w", from, to, err)
} else if len(revert)+len(apply) > 144 {
return types.ChainIndex{}, nil, fmt.Errorf("reorg path from %v to %v is too long (-%v +%v)", basis, m.tipState.Index, len(revert), len(apply))
return nil, fmt.Errorf("reorg path from %v to %v is too long (-%v +%v)", from, to, len(revert), len(apply))
}
for _, index := range revert {
b, _, cs, ok := blockAndParent(m.store, index.ID)
if !ok {
return types.ChainIndex{}, nil, fmt.Errorf("missing reverted block at index %v", index)
return nil, fmt.Errorf("missing reverted block at index %v", index)
} else if b.V2 == nil {
return types.ChainIndex{}, nil, fmt.Errorf("reorg path from %v to %v contains a non-v2 block (%v)", basis, m.tipState.Index, index)
return nil, fmt.Errorf("reorg path from %v to %v contains a non-v2 block (%v)", from, to, index)
}
// NOTE: since we are post-hardfork, we don't need a v1 supplement
cru := consensus.RevertBlock(cs, b, consensus.V1BlockSupplement{})
for i := range txns {
if !updateTxnProofs(&txns[i], cru.UpdateElementProof, cs.Elements.NumLeaves) {
return types.ChainIndex{}, nil, fmt.Errorf("transaction %v references element that does not exist in our chain", txns[i].ID())
return nil, fmt.Errorf("transaction %v references element that does not exist in our chain", txns[i].ID())
}
}
}

for _, index := range apply {
b, _, cs, ok := blockAndParent(m.store, index.ID)
if !ok {
return types.ChainIndex{}, nil, fmt.Errorf("missing applied block at index %v", index)
return nil, fmt.Errorf("missing applied block at index %v", index)
} else if b.V2 == nil {
return types.ChainIndex{}, nil, fmt.Errorf("reorg path from %v to %v contains a non-v2 block (%v)", basis, m.tipState.Index, index)
return nil, fmt.Errorf("reorg path from %v to %v contains a non-v2 block (%v)", from, to, index)
}
// NOTE: since we are post-hardfork, we don't need a v1 supplement or ancestorTimestamp
cs, cau := consensus.ApplyBlock(cs, b, consensus.V1BlockSupplement{}, time.Time{})
Expand Down Expand Up @@ -1154,7 +1193,6 @@ func (m *Manager) updateV2TransactionSet(basis types.ChainIndex, txns []types.V2
// remove any transactions that were confirmed in this block
continue
}
rem = append(rem, txns[i])

// update the state elements for any confirmed ephemeral elements
for j := range txns[i].SiacoinInputs {
Expand Down Expand Up @@ -1182,11 +1220,12 @@ func (m *Manager) updateV2TransactionSet(basis types.ChainIndex, txns []types.V2

// NOTE: all elements guaranteed to exist from here on, so no
// need to check this return value
updateTxnProofs(&rem[len(rem)-1], cau.UpdateElementProof, cs.Elements.NumLeaves)
updateTxnProofs(&txns[i], cau.UpdateElementProof, cs.Elements.NumLeaves)
rem = append(rem, txns[i])
}
txns = rem
}
return m.tipState.Index, txns, nil
return txns, nil
}

// AddV2PoolTransactions validates a transaction set and adds it to the txpool.
Expand Down Expand Up @@ -1220,7 +1259,7 @@ func (m *Manager) AddV2PoolTransactions(basis types.ChainIndex, txns []types.V2T
}

// update the transaction set to the current tip
_, txns, err := m.updateV2TransactionSet(basis, txns)
txns, err := m.UpdateV2TransactionSet(txns, basis, m.tipState.Index)
if err != nil {
return false, m.markBadTxnSet(setID, fmt.Errorf("failed to update set basis: %w", err))
} else if len(txns) == 0 {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.23.0

require (
go.etcd.io/bbolt v1.3.11
go.sia.tech/core v0.4.8-0.20241002233523-418236837b5a
go.sia.tech/core v0.4.8-0.20241003192046-425f95763c90
go.sia.tech/mux v1.3.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.27.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.sia.tech/core v0.4.8-0.20241002233523-418236837b5a h1:Lz7h42eZCk8iI4/agdWNwlVneqaybxkV1lor+Eavzso=
go.sia.tech/core v0.4.8-0.20241002233523-418236837b5a/go.mod h1:j2Ke8ihV8or7d2VDrFZWcCkwSVHO0DNMQJAGs9Qop2M=
go.sia.tech/core v0.4.8-0.20241003192046-425f95763c90 h1:R/G7XXyzLKelfGBGT6XQpih379v5jJx6T4AsjLwyjJc=
go.sia.tech/core v0.4.8-0.20241003192046-425f95763c90/go.mod h1:j2Ke8ihV8or7d2VDrFZWcCkwSVHO0DNMQJAGs9Qop2M=
go.sia.tech/mux v1.3.0 h1:hgR34IEkqvfBKUJkAzGi31OADeW2y7D6Bmy/Jcbop9c=
go.sia.tech/mux v1.3.0/go.mod h1:I46++RD4beqA3cW9Xm9SwXbezwPqLvHhVs9HLpDtt58=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
Expand Down
64 changes: 25 additions & 39 deletions rhp/v4/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,48 +649,28 @@ func RPCRenewContract(ctx context.Context, t TransportClient, tp TxPool, signer
}

renterCost, hostCost := rhp4.RenewalCost(cs, p, renewal, renewalTxn.MinerFee)

basis := cs.Index // start with a decent basis and overwrite it if a setup transaction is needed
var renewalParents []types.V2Transaction
if !renterCost.IsZero() {
setupTxn := types.V2Transaction{
SiacoinOutputs: []types.SiacoinOutput{
{Address: renewal.NewContract.RenterOutput.Address, Value: renterCost},
},
}
var err error
var toSign []int
basis, toSign, err = signer.FundV2Transaction(&setupTxn, renterCost)
if err != nil {
return RPCRenewContractResult{}, fmt.Errorf("failed to fund transaction: %w", err)
}
signer.SignV2Inputs(&setupTxn, toSign)

basis, renewalParents, err = tp.V2TransactionSet(basis, setupTxn)
if err != nil {
return RPCRenewContractResult{}, fmt.Errorf("failed to get transaction set: %w", err)
}
setupTxn = renewalParents[len(renewalParents)-1]

renewalTxn.SiacoinInputs = append(renewalTxn.SiacoinInputs, types.V2SiacoinInput{
Parent: setupTxn.EphemeralSiacoinOutput(0),
})
signer.SignV2Inputs(&renewalTxn, []int{0})
req := rhp4.RPCRenewContractRequest{
Prices: p,
Renewal: params,
MinerFee: renewalTxn.MinerFee,
Basis: cs.Index,
n8maninger marked this conversation as resolved.
Show resolved Hide resolved
}

renterSiacoinElements := make([]types.SiacoinElement, 0, len(renewalTxn.SiacoinInputs))
for _, i := range renewalTxn.SiacoinInputs {
renterSiacoinElements = append(renterSiacoinElements, i.Parent)
basis, toSign, err := signer.FundV2Transaction(&renewalTxn, renterCost)
if err != nil {
return RPCRenewContractResult{}, fmt.Errorf("failed to fund transaction: %w", err)
}
signer.SignV2Inputs(&renewalTxn, toSign)

req := rhp4.RPCRenewContractRequest{
Prices: p,
Renewal: params,
MinerFee: renewalTxn.MinerFee,
Basis: basis,
RenterInputs: renterSiacoinElements,
RenterParents: renewalParents,
req.Basis, req.RenterParents, err = tp.V2TransactionSet(basis, renewalTxn)
if err != nil {
return RPCRenewContractResult{}, fmt.Errorf("failed to get transaction set: %w", err)
}
for _, si := range renewalTxn.SiacoinInputs {
req.RenterInputs = append(req.RenterInputs, si.Parent)
}
req.RenterParents = req.RenterParents[:len(req.RenterParents)-1] // last transaction is the renewal

sigHash := req.ChallengeSigHash(existing.RevisionNumber)
req.ChallengeSignature = signer.SignHash(sigHash)

Expand All @@ -714,8 +694,14 @@ func RPCRenewContract(ctx context.Context, t TransportClient, tp TxPool, signer
}

// verify the host added enough inputs
if !hostInputSum.Equals(hostCost) {
if n := hostInputSum.Cmp(hostCost); n < 0 {
return RPCRenewContractResult{}, fmt.Errorf("expected host to fund %v, got %v", hostCost, hostInputSum)
} else if n > 0 {
// add change output
renewalTxn.SiacoinOutputs = append(renewalTxn.SiacoinOutputs, types.SiacoinOutput{
Address: existing.HostOutput.Address,
Value: hostInputSum.Sub(hostCost),
})
}

// sign the renter inputs
Expand All @@ -728,7 +714,7 @@ func RPCRenewContract(ctx context.Context, t TransportClient, tp TxPool, signer
renterPolicyResp := rhp4.RPCRenewContractSecondResponse{
RenterRenewalSignature: renewal.RenterSignature,
}
for _, si := range renewalTxn.SiacoinInputs[:len(renterSiacoinElements)] {
for _, si := range renewalTxn.SiacoinInputs[:len(req.RenterInputs)] {
renterPolicyResp.RenterSatisfiedPolicies = append(renterPolicyResp.RenterSatisfiedPolicies, si.SatisfiedPolicy)
}
if err := rhp4.WriteResponse(s, &renterPolicyResp); err != nil {
Expand Down
Loading
Loading