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

proof: add magic bytes to individual proofs and proof files #488

Merged
merged 4 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions cmd/tapcli/proofs.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func verifyProof(ctx *cli.Context) error {
defer cleanUp()

resp, err := client.VerifyProof(ctxc, &taprpc.ProofFile{
RawProof: rawFile,
RawProofFile: rawFile,
})
if err != nil {
return fmt.Errorf("unable to verify proof file: %w", err)
Expand Down Expand Up @@ -273,7 +273,7 @@ func exportProof(ctx *cli.Context) error {
// JSON format.
if ctx.String(proofPathName) != "" {
filePath := lncfg.CleanAndExpandPath(ctx.String(proofPathName))
return writeToFile(filePath, resp.RawProof)
return writeToFile(filePath, resp.RawProofFile)
}

printRespJSON(resp)
Expand Down
4 changes: 2 additions & 2 deletions itest/addrs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,10 @@ func sendProof(t *harnessTest, src, dst *tapdHarness, scriptKey []byte,
}, defaultWaitTimeout)
require.NoError(t.t, waitErr)

t.Logf("Importing proof %x", proofResp.RawProof)
t.Logf("Importing proof %x", proofResp.RawProofFile)

importResp, err := dst.ImportProof(ctxb, &tapdevrpc.ImportProofRequest{
ProofFile: proofResp.RawProof,
ProofFile: proofResp.RawProofFile,
GenesisPoint: genInfo.GenesisPoint,
})
require.NoError(t.t, err)
Expand Down
12 changes: 6 additions & 6 deletions itest/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func WaitForProofUpdate(t *testing.T, client taprpc.TaprootAssetsClient,

f := &proof.File{}
require.NoError(
t, f.Decode(bytes.NewReader(exportResp.RawProof)),
t, f.Decode(bytes.NewReader(exportResp.RawProofFile)),
)
lastProof, err := f.LastProof()
require.NoError(t, err)
Expand Down Expand Up @@ -255,7 +255,7 @@ func AssertAssetProofs(t *testing.T, tapClient taprpc.TaprootAssetsClient,
require.NoError(t, err)

file, snapshot := verifyProofBlob(
t, tapClient, chainClient, a, exportResp.RawProof,
t, tapClient, chainClient, a, exportResp.RawProofFile,
)

assetJSON, err := formatProtoJSON(a)
Expand All @@ -268,7 +268,7 @@ func AssertAssetProofs(t *testing.T, tapClient taprpc.TaprootAssetsClient,
t, CommitmentKey(t, a), snapshot.Asset.AssetCommitmentKey(),
)

return exportResp.RawProof
return exportResp.RawProofFile
}

// assertAssetProofsInvalid makes sure the proofs for the given asset can be
Expand All @@ -289,11 +289,11 @@ func assertAssetProofsInvalid(t *testing.T, tapd *tapdHarness,
require.NoError(t, err)

f := &proof.File{}
require.NoError(t, f.Decode(bytes.NewReader(exportResp.RawProof)))
require.NoError(t, f.Decode(bytes.NewReader(exportResp.RawProofFile)))

// Also make sure that the RPC can verify the proof as well.
verifyResp, err := tapd.VerifyProof(ctxt, &taprpc.ProofFile{
RawProof: exportResp.RawProof,
RawProofFile: exportResp.RawProofFile,
})
require.NoError(t, err)
require.False(t, verifyResp.Valid)
Expand All @@ -314,7 +314,7 @@ func verifyProofBlob(t *testing.T, tapClient taprpc.TaprootAssetsClient,

// Also make sure that the RPC can verify the proof as well.
verifyResp, err := tapClient.VerifyProof(ctxt, &taprpc.ProofFile{
RawProof: blob,
RawProofFile: blob,
})
require.NoError(t, err)
require.True(t, verifyResp.Valid)
Expand Down
4 changes: 2 additions & 2 deletions itest/send_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -861,11 +861,11 @@ func addProofTestVectorFromFile(t *testing.T, testName string,
require.NoError(t, waitErr)

if binaryFileName != "" {
test.WriteTestFileHex(t, binaryFileName, proofResp.RawProof)
test.WriteTestFileHex(t, binaryFileName, proofResp.RawProofFile)
}

var f proof.File
err := f.Decode(bytes.NewReader(proofResp.RawProof))
err := f.Decode(bytes.NewReader(proofResp.RawProofFile))
require.NoError(t, err)

if f.NumProofs() <= fileIndex {
Expand Down
25 changes: 24 additions & 1 deletion proof/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,15 @@ func NewFile(v Version, proofs ...Proof) (*File, error) {

// Encode encodes the proof file into `w` including its checksum.
func (f *File) Encode(w io.Writer) error {
err := binary.Write(w, binary.BigEndian, uint32(f.Version))
num, err := w.Write(FilePrefixMagicBytes[:])
if err != nil {
return err
}
if num != PrefixMagicBytesLength {
return errors.New("failed to write prefix magic bytes")
}

err = binary.Write(w, binary.BigEndian, uint32(f.Version))
if err != nil {
return err
}
Expand Down Expand Up @@ -137,6 +145,21 @@ func (f *File) Encode(w io.Writer) error {

// Decode decodes a proof file from `r`.
func (f *File) Decode(r io.Reader) error {
var prefixMagicBytes [PrefixMagicBytesLength]byte
num, err := r.Read(prefixMagicBytes[:])
if err != nil {
return err
}
if num != PrefixMagicBytesLength {
return errors.New("failed to read prefix magic bytes")
}

if prefixMagicBytes != FilePrefixMagicBytes {
return fmt.Errorf("invalid prefix magic bytes, expected %s, "+
"got %s", string(FilePrefixMagicBytes[:]),
string(prefixMagicBytes[:]))
}

var version uint32
if err := binary.Read(r, binary.BigEndian, &version); err != nil {
return err
Expand Down
67 changes: 67 additions & 0 deletions proof/proof.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package proof

import (
"bytes"
"errors"
"fmt"
"io"

"github.com/btcsuite/btcd/wire"
Expand Down Expand Up @@ -58,6 +60,48 @@ var (
RegtestOwnershipProofName = "ownership-proof.hex"
)

const (
// PrefixMagicBytesLength is the length of the magic bytes that are
// prefixed to individual proofs or proof files.
PrefixMagicBytesLength = 4
)

var (
// PrefixMagicBytes are the magic bytes that are prefixed to an
// individual transition or mint proof when encoding it. This is the
// ASCII encoding of the string "TAPP" (Taproot Assets Protocol Proof)
// in hex.
PrefixMagicBytes = [PrefixMagicBytesLength]byte{0x54, 0x41, 0x50, 0x50}

// FilePrefixMagicBytes are the magic bytes that are prefixed to a proof
// file when encoding it. This is the ASCII encoding of the string
// "TAPF" (Taproot Assets Protocol File) in hex.
FilePrefixMagicBytes = [PrefixMagicBytesLength]byte{
0x54, 0x41, 0x50, 0x46,
}
)

// IsSingleProof returns true if the given blob is an encoded individual
// mint/transition proof.
func IsSingleProof(blob Blob) bool {
if len(blob) < PrefixMagicBytesLength {
return false
}

return bytes.Equal(blob[:PrefixMagicBytesLength], PrefixMagicBytes[:])
}

// IsProofFile returns true if the given blob is an encoded proof file.
func IsProofFile(blob Blob) bool {
if len(blob) < PrefixMagicBytesLength {
return false
}

return bytes.Equal(
blob[:PrefixMagicBytesLength], FilePrefixMagicBytes[:],
)
}

// UpdateCallback is a callback that is called when proofs are updated because
// of a re-org.
type UpdateCallback func([]*Proof) error
Expand Down Expand Up @@ -212,6 +256,14 @@ func (p *Proof) DecodeRecords() []tlv.Record {

// Encode encodes a Proof into `w`.
func (p *Proof) Encode(w io.Writer) error {
num, err := w.Write(PrefixMagicBytes[:])
if err != nil {
return err
}
if num != PrefixMagicBytesLength {
return errors.New("failed to write prefix magic bytes")
}

stream, err := tlv.NewStream(p.EncodeRecords()...)
if err != nil {
return err
Expand All @@ -221,6 +273,21 @@ func (p *Proof) Encode(w io.Writer) error {

// Decode decodes a Proof from `r`.
func (p *Proof) Decode(r io.Reader) error {
var prefixMagicBytes [PrefixMagicBytesLength]byte
num, err := r.Read(prefixMagicBytes[:])
if err != nil {
return err
}
if num != PrefixMagicBytesLength {
return errors.New("failed to read prefix magic bytes")
}

if prefixMagicBytes != PrefixMagicBytes {
return fmt.Errorf("invalid prefix magic bytes, expected %s, "+
"got %s", string(PrefixMagicBytes[:]),
string(prefixMagicBytes[:]))
}

stream, err := tlv.NewStream(p.DecodeRecords()...)
if err != nil {
return err
Expand Down
32 changes: 29 additions & 3 deletions proof/proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,38 @@ func TestProofEncoding(t *testing.T) {
require.NoError(t, err)
proof.AdditionalInputs = []File{*file, *file}

var buf bytes.Buffer
require.NoError(t, proof.Encode(&buf))
var proofBuf bytes.Buffer
require.NoError(t, proof.Encode(&proofBuf))
proofBytes := proofBuf.Bytes()

var decodedProof Proof
require.NoError(t, decodedProof.Decode(&buf))
require.NoError(t, decodedProof.Decode(bytes.NewReader(proofBytes)))

assertEqualProof(t, &proof, &decodedProof)

// Make sure the proof and proof file prefixes are checked correctly.
var fileBuf bytes.Buffer
require.NoError(t, file.Encode(&fileBuf))
fileBytes := fileBuf.Bytes()

p := &Proof{}
err = p.Decode(bytes.NewReader(fileBytes))
require.ErrorContains(
t, err, "invalid prefix magic bytes, expected TAPP",
)
guggero marked this conversation as resolved.
Show resolved Hide resolved

f := &File{}
err = f.Decode(bytes.NewReader(proofBytes))
require.ErrorContains(
t, err, "invalid prefix magic bytes, expected TAPF",
)

require.True(t, IsSingleProof(proofBytes))
require.True(t, IsProofFile(fileBytes))
require.False(t, IsProofFile(proofBytes))
require.False(t, IsSingleProof(fileBytes))
require.False(t, IsProofFile(nil))
require.False(t, IsSingleProof(nil))
}

func genRandomGenesisWithProof(t testing.TB, assetType asset.Type,
Expand Down
2 changes: 1 addition & 1 deletion proof/testdata/ownership-proof.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
00240eebbbf4b53b1759d7cac14c6414b85815bf4df5f91f9ab7db3af230d787c1e000000001015000004020881098b292c1342c3880f14b416a641c21c2f312ce0127bb121f27d5a15496433da16a1b841ed6743acdae8eab606ab9a0820aecb5e57da538573d1e879f4178577cf064ffff7f200200000002fd018c0200000000010200f7627d2a9f7186d9e3e18009a81dc39d7d5c527b4ecb4049d02f3d4d07e5940000000000ffffffff0eebbbf4b53b1759d7cac14c6414b85815bf4df5f91f9ab7db3af230d787c1e001000000000000000003e8030000000000002251201ddfbf3832ad0061453e6533e2b98bb6b344ef9addb3222cb56a9f37fadee2bbe8030000000000002251205a7c661fd2b45837e8b1096cac64dff973b8afa3deb3c269c95af12a5ee348884aa9f505000000002251200c011e5c0cf6fe653634b835845f835b95a07818336e45d0fa51bf8eacba085f0247304402206a96d02679d947906572e8083569aa2c2ce4a582f4c0f75aa9c313bee9daf7d8022052af856a8f8b8e69d689ac3c9b3ef348ea38728942750b913771abfe3f08e07c01210246e7a2190cfa4cfd80ef894cc0e3752e682a8cb334710c01fc11f5ead08a910c0140ec31f6ed956aee5b5bf5d6a0040c5c0de9727537db72e6bf8b5a37a531225ccf63591131043b1c4e6cc9d58238df2c402fa404118d52f65123d103916c1970300000000003220163b1e755ba02747c67db8fe1fa693dcf9e66f9333f58bffb4f06ce8501c207fb0004fd02b400010001590075dc43685274cde4fe26577ae1bc945cedd6d2421ea22bde862ea9d5c7278c000000000f66697273742d697465737462757878dedfcaf730cec72f6dbea97c64d4a4f3489edc3c2ff8413ad169e9717a3b058d00000000000201000303fd025806fd022301fd021f0065000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002fd01b44a000151a8d993f84dbcbd37fa9e637701df24adcd8ecb9f3673d52c4e2ee2ead56e5c0000000000000258ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffd016600010001590075dc43685274cde4fe26577ae1bc945cedd6d2421ea22bde862ea9d5c7278c000000000f66697273742d697465737462757878dedfcaf730cec72f6dbea97c64d4a4f3489edc3c2ff8413ad169e9717a3b058d00000000000201000303fd025806ad01ab00650eebbbf4b53b1759d7cac14c6414b85815bf4df5f91f9ab7db3af230d787c1e000000001af46688108d6b3a8a7133502b742290df126c9f0649e26754225ea45935fdfd402c3239dc920d64be74c8ed1dfccaa26550e5c8cee2d6d12881ab0d3183c60d6ad01420140afa7bdc77debb61cbf36aa9cb601534d52aa49a1079cf64a1d44586e651bfa0fa02deea716e6e4f8fe8eae052b5950a54b1fc0b2eaf522c0b467a86b4de2546f07281b4bd820fed5f617faf7f481a8d076dd8be8e7fe5ca5e3163282c9105818d54900000000000004b0080200000921026fbf5bf92953f9bcf93603befd44dc83fcfd813a5a4adb40be42427378e8b5b508020000092102777d47bfc5e62bdd5199995fb6c485069239b92b083d9d6723917b75176d70ac059f000400000001012102bb464af24d0d8a24611c821e08f3fd57054ccac7b5875905bc6e045060f79503027400490001000120af46688108d6b3a8a7133502b742290df126c9f0649e26754225ea45935fdfd402220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff06f802c700040000000001210315e5b3b06d61b3ad951f51d1daf4a3b8ec224224ea4025e72dda731d590497c9029c00710001000120af46688108d6b3a8a7133502b742290df126c9f0649e26754225ea45935fdfd4024a0001c4207b6354ab04e407d260e4e01f8a0626639071b556a0961da00fbfe7c7964f0000000000000258ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2e000400000002012102c7df030275b2ea0de67f7158d8cc1bd28fe98881d1fdac0db674ad62d04b02ce0303020101079f00040000000001210315e5b3b06d61b3ad951f51d1daf4a3b8ec224224ea4025e72dda731d590497c9027400490001000120af46688108d6b3a8a7133502b742290df126c9f0649e26754225ea45935fdfd402220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0a420140f840a182fba1a6eae0c1bcd7d9e4bc257c711480e2d7f93dd421134f23c63ebccf327a6930ce0bcbd17bd388659db0d59e272d9877fcc1fef68e9a901bf9efa10b04000001bc
5441505000248f6f371662879ba4db9cce742e4e79703f772a51dfbc65a00bf5dd6964e3ff190000000101500000402017b41c426f6e0ea2318a0272c0e5166355a97e0b45e014ae265b055cf15e4a09cac22e8351343ddf97acbf38f547709654cc4cdb49bdfb77c3b195e44e1345cd697ef864ffff7f200000000002fd018c02000000000102000b9c5425fd4186521743f78203b73a0cc85fffeeb48026f11bb6eed16a1f290000000000ffffffff8f6f371662879ba4db9cce742e4e79703f772a51dfbc65a00bf5dd6964e3ff1901000000000000000003e8030000000000002251206be9cc2c4d68ec4e9827bff12067bca0770a92fd10e346c9587eb674e534b2d4e8030000000000002251209556a078749c7a768ccbb97124296639ffa4785b59c82c4c135121e0217a96a44aa9f50500000000225120a152904d7685c3e24c348784140064f68ea252af71560c9c400574e696f375f502473044022069b2a86cf0a1e9f3f23bd38cab06c99a3ed326b4a2bdf932c744e8a662fe9cf302206911494f403f7e0aff12d53a3ec8dff3b8792bbcb6769499ae84e0423e39486c012102e641075343b4dae91de76437617f51a4c251395600c763595a8128f296dcb02901404986e85a6e29e02504b22b313bff0de240164a5b2d1309d2ffe1b841742c72042c40b70609c0206a6660d97dd812911eb054f2548d847989937b7e06459753460000000003220162a6107a3639a2d6d21ea74f905bbdd2cd760247197c19250125cce69ad617150004fd02b400010001590476d8a24114bbe8346622da3422784ed07c069deb8f1bc388664b31156379bc000000000f66697273742d697465737462757878dedfcaf730cec72f6dbea97c64d4a4f3489edc3c2ff8413ad169e9717a3b058d00000000000201000303fd025806fd022301fd021f0065000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002fd01b44a0001cac8adc6fbff43b96418da0830e41cc24e687198fae443bbe64d13df6630dc830000000000000258ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffd016600010001590476d8a24114bbe8346622da3422784ed07c069deb8f1bc388664b31156379bc000000000f66697273742d697465737462757878dedfcaf730cec72f6dbea97c64d4a4f3489edc3c2ff8413ad169e9717a3b058d00000000000201000303fd025806ad01ab00658f6f371662879ba4db9cce742e4e79703f772a51dfbc65a00bf5dd6964e3ff19000000010fb088e12cab13f6f2f0a9cb1a48132477f9bf81625c8d513071ce841ef39b960232cad83de89c46e89e5522cf298fa8082ff0724bc4b9d9f8f577984e21ebb2bb014201408d602ec4a118925c23597faa896bccb675025b95f6827e29d6aa4a109eeea5ff64abdef145caa91bcba5f89d5dc79186620b1ce71db2780417fc1cefe584d7d607287a1d9a8372ee98c20893e1ab4d8028f1d3661c5fee83dbcfc528bfa738e5b07100000000000004b008020000092102efd8980ab3cacc9183c45bbbdded673416dba20a21486343d2dbbc5c37a0659b080200000921021cd140129b6140352dbf4469c8f2fbe4b3567479925f0619f59e8dfad0e0ea02059f000400000001012103a5d14787afdd48b27852302d8e6044adc80b2b09a84e405bd15df0a58372cb090274004900010001200fb088e12cab13f6f2f0a9cb1a48132477f9bf81625c8d513071ce841ef39b9602220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff06f802c7000400000000012103a2989befc0beca8bd322df75b77adbffcd472231e4f0f6709ba355c2590bafdb029c007100010001200fb088e12cab13f6f2f0a9cb1a48132477f9bf81625c8d513071ce841ef39b96024a000113e93b60b459b01018e7d1c419a0c085dc2a8022272f096228640e957005277a0000000000000258ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2e000400000002012102fe7cd697bcc60a44dfd209ed970e6cd14d6094506a47a2a6a5b0cfa6f63dd0d60303020101079f000400000000012103a2989befc0beca8bd322df75b77adbffcd472231e4f0f6709ba355c2590bafdb0274004900010001200fb088e12cab13f6f2f0a9cb1a48132477f9bf81625c8d513071ce841ef39b9602220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff012700010001220000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0a420140e960b17b2a7553e40958ee541c99c31b1143fb6ff70c363d04a761111ec634e4526d969945031d185f32df5f20bc2a2e6b4617e58db851fe559bf91f8e9ea4b70b04000001bc
Loading