-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(BUX-164): verify merkle root * chore(BUX-164): add SPV method * chore(BUX-164): remove unused files * chore(BUX-164): remove logs and fix adding up outputs * chore(BUX-164): change the way of calculating input sum * chore(BUX-164): refactor SPV methods * chore(BUX-164): move CMP into separate file * chore(BUX-164): move BEEF tx SPV to separate file * chore(BUX-170): refactor verify merkle roots method * chore(BUX-170): fix example
- Loading branch information
1 parent
92c6a65
commit 90b9efb
Showing
11 changed files
with
250 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package paymail | ||
|
||
type CompoundMerklePath []map[string]uint64 | ||
|
||
type CMPSlice []CompoundMerklePath | ||
|
||
func (cmp CompoundMerklePath) calculateMerkleRoots() ([]string, error) { | ||
merkleRoots := make([]string, 0) | ||
|
||
for tx, offset := range cmp[len(cmp)-1] { | ||
merkleRoot, err := calculateMerkleRoot(tx, offset, cmp) | ||
if err != nil { | ||
return nil, err | ||
} | ||
merkleRoots = append(merkleRoots, merkleRoot) | ||
} | ||
return merkleRoots, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package paymail | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"github.com/libsv/go-bt/v2" | ||
"github.com/libsv/go-bt/v2/bscript/interpreter" | ||
) | ||
|
||
type MerkleRootVerifier interface { | ||
VerifyMerkleRoots( | ||
ctx context.Context, | ||
merkleRoots []string, | ||
) error | ||
} | ||
|
||
// ExecuteSimplifiedPaymentVerification executes the SPV for decoded BEEF tx | ||
func ExecuteSimplifiedPaymentVerification(dBeef *DecodedBEEF, provider MerkleRootVerifier) error { | ||
err := validateSatoshisSum(dBeef) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = validateLockTime(dBeef) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = validateScripts(dBeef) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = verifyMerkleRoots(dBeef, provider) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func verifyMerkleRoots(dBeef *DecodedBEEF, provider MerkleRootVerifier) error { | ||
merkleRoots, err := dBeef.GetMerkleRoots() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = provider.VerifyMerkleRoots(context.Background(), merkleRoots) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func validateScripts(dBeef *DecodedBEEF) error { | ||
for _, input := range dBeef.ProcessedTxData.Transaction.Inputs { | ||
txId := input.PreviousTxID() | ||
for j, input2 := range dBeef.InputsTxData { | ||
if input2.Transaction.TxID() == string(txId) { | ||
result := verifyScripts(dBeef.ProcessedTxData.Transaction, input2.Transaction, j) | ||
if !result { | ||
return errors.New("invalid script") | ||
} | ||
break | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func validateSatoshisSum(dBeef *DecodedBEEF) error { | ||
if len(dBeef.ProcessedTxData.Transaction.Outputs) == 0 { | ||
return errors.New("invalid output, no outputs") | ||
} | ||
|
||
if len(dBeef.ProcessedTxData.Transaction.Inputs) == 0 { | ||
return errors.New("invalid input, no inputs") | ||
} | ||
|
||
inputSum, outputSum := uint64(0), uint64(0) | ||
for i, input := range dBeef.ProcessedTxData.Transaction.Inputs { | ||
inputParentTx := dBeef.InputsTxData[i] | ||
inputSum += inputParentTx.Transaction.Outputs[input.PreviousTxOutIndex].Satoshis | ||
} | ||
for _, output := range dBeef.ProcessedTxData.Transaction.Outputs { | ||
outputSum += output.Satoshis | ||
} | ||
|
||
if inputSum <= outputSum { | ||
return errors.New("invalid input and output sum, outputs can not be larger than inputs") | ||
} | ||
return nil | ||
} | ||
|
||
func validateLockTime(dBeef *DecodedBEEF) error { | ||
if dBeef.ProcessedTxData.Transaction.LockTime == 0 { | ||
for _, input := range dBeef.ProcessedTxData.Transaction.Inputs { | ||
if input.SequenceNumber != 0xffffffff { | ||
return errors.New("unexpected transaction with nSequence") | ||
} | ||
} | ||
} else { | ||
return errors.New("nexpected transaction with nLockTime") | ||
} | ||
return nil | ||
} | ||
|
||
// Verify locking and unlocking scripts pair | ||
func verifyScripts(tx, prevTx *bt.Tx, inputIdx int) bool { | ||
input := tx.InputIdx(inputIdx) | ||
prevOutput := prevTx.OutputIdx(int(input.PreviousTxOutIndex)) | ||
|
||
if err := interpreter.NewEngine().Execute( | ||
interpreter.WithTx(tx, inputIdx, prevOutput), | ||
interpreter.WithForkID(), | ||
interpreter.WithAfterGenesis(), | ||
); err != nil { | ||
fmt.Println(err) | ||
return false | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters