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

Moved funds sweep SPV proofs #3793

Merged
merged 9 commits into from
Mar 15, 2024
51 changes: 51 additions & 0 deletions pkg/chain/ethereum/tbtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,57 @@ func (tc *TbtcChain) SubmitMovingFundsProofWithReimbursement(
return err
}

func (tc *TbtcChain) SubmitMovedFundsSweepProofWithReimbursement(
transaction *bitcoin.Transaction,
proof *bitcoin.SpvProof,
mainUTXO bitcoin.UnspentTransactionOutput,
) error {
bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{
Version: transaction.SerializeVersion(),
InputVector: transaction.SerializeInputs(),
OutputVector: transaction.SerializeOutputs(),
Locktime: transaction.SerializeLocktime(),
}
movedFundsSweepProof := tbtcabi.BitcoinTxProof2{
MerkleProof: proof.MerkleProof,
TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)),
BitcoinHeaders: proof.BitcoinHeaders,
CoinbasePreimage: proof.CoinbasePreimage,
CoinbaseProof: proof.CoinbaseProof,
}
utxo := tbtcabi.BitcoinTxUTXO2{
TxHash: mainUTXO.Outpoint.TransactionHash,
TxOutputIndex: mainUTXO.Outpoint.OutputIndex,
TxOutputValue: uint64(mainUTXO.Value),
}

gasEstimate, err := tc.maintainerProxy.SubmitMovedFundsSweepProofGasEstimate(
bitcoinTxInfo,
movedFundsSweepProof,
utxo,
)
if err != nil {
return err
}

// The original estimate for this contract call is too low and the call
// fails on reimbursing the submitter. Example:
// 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114
// Here we add a 20% margin to overcome the gas problems.
gasEstimateWithMargin := float64(gasEstimate) * float64(1.2)

_, err = tc.maintainerProxy.SubmitMovedFundsSweepProof(
bitcoinTxInfo,
movedFundsSweepProof,
utxo,
ethutil.TransactionOptions{
GasLimit: uint64(gasEstimateWithMargin),
},
)

return err
}

func (tc *TbtcChain) ValidateMovedFundsSweepProposal(
walletPublicKeyHash [20]byte,
proposal *tbtc.MovedFundsSweepProposal,
Expand Down
15 changes: 15 additions & 0 deletions pkg/maintainer/spv/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ type Chain interface {
redeemerOutputScript bitcoin.Script,
) (*tbtc.RedemptionRequest, bool, error)

// GetMovedFundsSweepRequest gets the on-chain moved funds sweep request for
// the given moving funds transaction hash and output index.
GetMovedFundsSweepRequest(
movingFundsTxHash bitcoin.Hash,
movingFundsTxOutpointIndex uint32,
) (*tbtc.MovedFundsSweepRequest, error)

// SubmitRedemptionProofWithReimbursement submits the redemption proof
// via MaintainerProxy. The caller is reimbursed.
SubmitRedemptionProofWithReimbursement(
Expand All @@ -75,6 +82,14 @@ type Chain interface {
walletPublicKeyHash [20]byte,
) error

// SubmitMovedFundsSweepProofWithReimbursement submits the moved funds sweep
// proof via MaintainerProxy. The caller is reimbursed.
SubmitMovedFundsSweepProofWithReimbursement(
transaction *bitcoin.Transaction,
proof *bitcoin.SpvProof,
mainUTXO bitcoin.UnspentTransactionOutput,
) error

// PastDepositRevealedEvents fetches past deposit reveal events according
// to the provided filter or unfiltered if the filter is nil. Returned
// events are sorted by the block number in the ascending order, i.e. the
Expand Down
87 changes: 87 additions & 0 deletions pkg/maintainer/spv/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,24 @@ type submittedMovingFundsProof struct {
walletPublicKeyHash [20]byte
}

type submittedMovedFundsSweepProof struct {
transaction *bitcoin.Transaction
proof *bitcoin.SpvProof
mainUTXO bitcoin.UnspentTransactionOutput
}

type localChain struct {
mutex sync.Mutex

blockCounter chain.BlockCounter
wallets map[[20]byte]*tbtc.WalletChainData
depositRequests map[[32]byte]*tbtc.DepositChainRequest
pendingRedemptionRequests map[[32]byte]*tbtc.RedemptionRequest
movedFundsSweepRequests map[[32]byte]*tbtc.MovedFundsSweepRequest
submittedRedemptionProofs []*submittedRedemptionProof
submittedDepositSweepProofs []*submittedDepositSweepProof
submittedMovingFundsProofs []*submittedMovingFundsProof
submittedMovedFundsSweepProofs []*submittedMovedFundsSweepProof
pastRedemptionRequestedEvents map[[32]byte][]*tbtc.RedemptionRequestedEvent
pastDepositRevealedEvents map[[32]byte][]*tbtc.DepositRevealedEvent
pastMovingFundsCommitmentSubmittedEvents map[[32]byte][]*tbtc.MovingFundsCommitmentSubmittedEvent
Expand All @@ -62,6 +70,7 @@ func newLocalChain() *localChain {
wallets: make(map[[20]byte]*tbtc.WalletChainData),
depositRequests: make(map[[32]byte]*tbtc.DepositChainRequest),
pendingRedemptionRequests: make(map[[32]byte]*tbtc.RedemptionRequest),
movedFundsSweepRequests: make(map[[32]byte]*tbtc.MovedFundsSweepRequest),
submittedRedemptionProofs: make([]*submittedRedemptionProof, 0),
submittedDepositSweepProofs: make([]*submittedDepositSweepProof, 0),
submittedMovingFundsProofs: make([]*submittedMovingFundsProof, 0),
Expand Down Expand Up @@ -304,6 +313,33 @@ func (lc *localChain) getSubmittedMovingFundsProofs() []*submittedMovingFundsPro
return lc.submittedMovingFundsProofs
}

func (lc *localChain) SubmitMovedFundsSweepProofWithReimbursement(
transaction *bitcoin.Transaction,
proof *bitcoin.SpvProof,
mainUTXO bitcoin.UnspentTransactionOutput,
) error {
lc.mutex.Lock()
defer lc.mutex.Unlock()

lc.submittedMovedFundsSweepProofs = append(
lc.submittedMovedFundsSweepProofs,
&submittedMovedFundsSweepProof{
transaction: transaction,
proof: proof,
mainUTXO: mainUTXO,
},
)

return nil
}

func (lc *localChain) getSubmittedMovedFundsSweepProofs() []*submittedMovedFundsSweepProof {
lc.mutex.Lock()
defer lc.mutex.Unlock()

return lc.submittedMovedFundsSweepProofs
}

func (lc *localChain) Ready() (bool, error) {
panic("unsupported")
}
Expand Down Expand Up @@ -596,6 +632,57 @@ func buildPastMovingFundsCommitmentSubmittedEventsKey(
return sha256.Sum256(buffer.Bytes()), nil
}

func buildMovedFundsSweepRequestKey(
movingFundsTxHash bitcoin.Hash,
movingFundsTxOutpointIndex uint32,
) [32]byte {
var buffer bytes.Buffer

buffer.Write(movingFundsTxHash[:])

outputIndex := make([]byte, 4)
binary.BigEndian.PutUint32(outputIndex, movingFundsTxOutpointIndex)
buffer.Write(outputIndex)

return sha256.Sum256(buffer.Bytes())
}

func (lc *localChain) setMovedFundsSweepRequest(
movingFundsTxHash bitcoin.Hash,
movingFundsTxOutpointIndex uint32,
request *tbtc.MovedFundsSweepRequest,
) {
lc.mutex.Lock()
defer lc.mutex.Unlock()

requestKey := buildMovedFundsSweepRequestKey(
movingFundsTxHash,
movingFundsTxOutpointIndex,
)

lc.movedFundsSweepRequests[requestKey] = request
}

func (lc *localChain) GetMovedFundsSweepRequest(
movingFundsTxHash bitcoin.Hash,
movingFundsTxOutpointIndex uint32,
) (*tbtc.MovedFundsSweepRequest, error) {
lc.mutex.Lock()
defer lc.mutex.Unlock()

requestKey := buildMovedFundsSweepRequestKey(
movingFundsTxHash,
movingFundsTxOutpointIndex,
)

request, ok := lc.movedFundsSweepRequests[requestKey]
if !ok {
return nil, fmt.Errorf("request not found")
}

return request, nil
}

type mockBlockCounter struct {
mutex sync.Mutex
currentBlock uint64
Expand Down
Loading
Loading