diff --git a/pkg/covenant/names.go b/pkg/covenant/names.go index 85476a3..93cfcc9 100644 --- a/pkg/covenant/names.go +++ b/pkg/covenant/names.go @@ -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]) diff --git a/pkg/covenant/rules_extra_test.go b/pkg/covenant/rules_extra_test.go index 97062e1..0970f6e 100644 --- a/pkg/covenant/rules_extra_test.go +++ b/pkg/covenant/rules_extra_test.go @@ -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) + } +} diff --git a/pkg/covenant/verify.go b/pkg/covenant/verify.go index 0dce257..950fe66 100644 --- a/pkg/covenant/verify.go +++ b/pkg/covenant/verify.go @@ -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