Validate coinbase claim witnesses

This commit is contained in:
Virgil 2026-04-04 07:58:52 +00:00
parent 1e0ae2cf07
commit 90bbdb6531
3 changed files with 85 additions and 2 deletions

View file

@ -198,6 +198,11 @@ func HasSaneCovenants(tx primitives.Transaction) bool {
return false
}
var claim primitives.Claim
if err := claim.UnmarshalBinary(input.Witness[0]); err != nil {
return false
}
var hash primitives.Hash
copy(hash[:], cov.Items[0])

View file

@ -484,7 +484,7 @@ func TestVerifyCovenantsCoinbaseLinkedOutputs(t *testing.T) {
{Prevout: primitives.NewOutpoint()},
{
Prevout: primitives.Outpoint{TxHash: primitives.Hash{1}, Index: 0},
Witness: [][]byte{[]byte("proof")},
Witness: [][]byte{encodeTestClaim([]byte("proof"))},
},
},
Outputs: []primitives.Output{
@ -504,7 +504,7 @@ func TestVerifyCovenantsCoinbaseLinkedOutputs(t *testing.T) {
{Prevout: primitives.NewOutpoint()},
{
Prevout: primitives.Outpoint{TxHash: primitives.Hash{2}, Index: 0},
Witness: [][]byte{[]byte("proof")},
Witness: [][]byte{encodeTestClaim([]byte("proof"))},
},
},
Outputs: []primitives.Output{
@ -593,6 +593,16 @@ func appendTestVarBytes(dst, src []byte) []byte {
return append(dst, src...)
}
func encodeTestClaim(blob []byte) []byte {
claim := primitives.Claim{Blob: append([]byte(nil), blob...)}
raw, err := claim.MarshalBinary()
if err != nil {
panic(err)
}
return raw
}
func TestCoinbaseAirdropProofValidation(t *testing.T) {
address := bytes.Repeat([]byte{0x42}, 20)
key := encodeTestAirdropKey(0, address, 1000, false)
@ -634,3 +644,66 @@ func TestCoinbaseAirdropProofValidation(t *testing.T) {
t.Fatalf("VerifyCovenants returned %d for a mismatched coinbase airdrop value, want -1", got)
}
}
func TestCoinbaseClaimWitnessValidation(t *testing.T) {
reservedHash, err := HashString("reserved")
if err != nil {
t.Fatalf("HashString returned error: %v", err)
}
var claimHash primitives.Hash
copy(claimHash[:], reservedHash[:])
blockHeight := make([]byte, 4)
binary.LittleEndian.PutUint32(blockHeight, 100)
tx := primitives.Transaction{
Inputs: []primitives.Input{
{Prevout: primitives.NewOutpoint()},
{
Prevout: primitives.Outpoint{TxHash: primitives.Hash{5}, Index: 0},
Witness: [][]byte{encodeTestClaim([]byte("proof"))},
},
},
Outputs: []primitives.Output{
{Covenant: primitives.Covenant{Type: uint8(TypeNone)}},
{
Value: 0,
Address: primitives.Address{
Version: 0,
Hash: bytes.Repeat([]byte{0x24}, 20),
},
Covenant: primitives.Covenant{
Type: uint8(TypeClaim),
Items: [][]byte{
claimHash[:],
blockHeight,
[]byte("reserved"),
[]byte{0},
make([]byte, 32),
blockHeight,
},
},
},
},
}
if got := HasSaneCovenants(tx); !got {
t.Fatal("HasSaneCovenants should accept a valid coinbase claim witness wrapper")
}
if got := VerifyCovenants(tx, testCoinView{coins: map[primitives.Outpoint]primitives.Output{}}, 100, Network{}); got != 0 {
t.Fatalf("VerifyCovenants returned %d for a valid coinbase claim witness wrapper, want 0", got)
}
badTx := tx
badTx.Inputs[1].Witness = [][]byte{[]byte("proof")}
if got := HasSaneCovenants(badTx); got {
t.Fatal("HasSaneCovenants should reject an invalid coinbase claim witness wrapper")
}
if got := VerifyCovenants(badTx, testCoinView{coins: map[primitives.Outpoint]primitives.Output{}}, 100, Network{}); got != -1 {
t.Fatalf("VerifyCovenants returned %d for an invalid coinbase claim witness wrapper, want -1", got)
}
}

View file

@ -87,6 +87,11 @@ func VerifyCovenants(tx primitives.Transaction, view CoinView, height uint32, ne
conjured += value
case uint8(TypeClaim):
var claim primitives.Claim
if err := claim.UnmarshalBinary(input.Witness[0]); err != nil {
return -1
}
blockHeight, err := cov.GetU32(1)
if err != nil || blockHeight != height {
return -1