1// SPDX-License-Identifier: ISC
2// Copyright (c) 2014-2020 Bitmark Inc.
3// Use of this source code is governed by an ISC
4// license that can be found in the LICENSE file.
5
6package transactionrecord_test
7
8import (
9	"bytes"
10	"encoding/json"
11	"fmt"
12	"reflect"
13	"testing"
14
15	"golang.org/x/crypto/ed25519"
16
17	"github.com/bitmark-inc/bitmarkd/fault"
18	"github.com/bitmark-inc/bitmarkd/merkle"
19	"github.com/bitmark-inc/bitmarkd/transactionrecord"
20	"github.com/bitmark-inc/bitmarkd/util"
21)
22
23// test the packing/unpacking of Bitmark issue record
24//
25// ensures that pack->unpack returns the same original value
26func TestPackBitmarkIssue(t *testing.T) {
27
28	issuerAccount := makeAccount(issuer.publicKey)
29
30	var assetId transactionrecord.AssetIdentifier
31	_, err := fmt.Sscan("59d06155d25dffdb982729de8dce9d7855ca094d8bab8124b347c40668477056b3c27ccb7d71b54043d207ccd187642bf9c8466f9a8d0dbefb4c41633a7e39ef", &assetId)
32	if nil != err {
33		t.Fatalf("hex to asset id error: %s", err)
34	}
35
36	r := transactionrecord.BitmarkIssue{
37		AssetId: assetId,
38		Owner:   issuerAccount,
39		Nonce:   99,
40	}
41
42	expected := []byte{
43		0x03, 0x40, 0x59, 0xd0, 0x61, 0x55, 0xd2, 0x5d,
44		0xff, 0xdb, 0x98, 0x27, 0x29, 0xde, 0x8d, 0xce,
45		0x9d, 0x78, 0x55, 0xca, 0x09, 0x4d, 0x8b, 0xab,
46		0x81, 0x24, 0xb3, 0x47, 0xc4, 0x06, 0x68, 0x47,
47		0x70, 0x56, 0xb3, 0xc2, 0x7c, 0xcb, 0x7d, 0x71,
48		0xb5, 0x40, 0x43, 0xd2, 0x07, 0xcc, 0xd1, 0x87,
49		0x64, 0x2b, 0xf9, 0xc8, 0x46, 0x6f, 0x9a, 0x8d,
50		0x0d, 0xbe, 0xfb, 0x4c, 0x41, 0x63, 0x3a, 0x7e,
51		0x39, 0xef, 0x21, 0x13, 0x9f, 0xc4, 0x86, 0xa2,
52		0x53, 0x4f, 0x17, 0xe3, 0x67, 0x07, 0xfa, 0x4b,
53		0x95, 0x3e, 0x3b, 0x34, 0x00, 0xe2, 0x72, 0x9f,
54		0x65, 0x61, 0x16, 0xdd, 0x7b, 0x01, 0x8d, 0xf3,
55		0x46, 0x98, 0xbd, 0xc2, 0x63,
56	}
57
58	expectedTxId := merkle.Digest{
59		0x79, 0xa6, 0x7b, 0xe2, 0xb3, 0xd3, 0x13, 0xbd,
60		0x49, 0x03, 0x63, 0xfb, 0x0d, 0x27, 0x90, 0x1c,
61		0x46, 0xed, 0x53, 0xd3, 0xf7, 0xb2, 0x1f, 0x60,
62		0xd4, 0x8b, 0xc4, 0x24, 0x39, 0xb0, 0x60, 0x84,
63	}
64
65	// manually sign the record and attach signature to "expected"
66	signature := ed25519.Sign(issuer.privateKey, expected)
67	r.Signature = signature
68	l := util.ToVarint64(uint64(len(signature)))
69	expected = append(expected, l...)
70	expected = append(expected, signature...)
71
72	// test the packer
73	packed, err := r.Pack(issuerAccount)
74	if nil != err {
75		if nil != packed {
76			t.Errorf("partial packed:\n%s", util.FormatBytes("expected", packed))
77		}
78		t.Errorf("pack error: %s", err)
79	}
80
81	// if either of above fail we will have the message _without_ a signature
82	if !bytes.Equal(packed, expected) {
83		t.Errorf("pack record: %x  expected: %x", packed, expected)
84		t.Errorf("*** GENERATED Packed:\n%s", util.FormatBytes("expected", packed))
85		t.Fatal("fatal error")
86	}
87
88	t.Logf("Packed length: %d bytes", len(packed))
89
90	// check txId
91	txId := packed.MakeLink()
92
93	if txId != expectedTxId {
94		t.Errorf("pack tx id: %#v  expected: %x", txId, expectedTxId)
95		t.Errorf("*** GENERATED tx id:\n%s", util.FormatBytes("expectedTxId", txId[:]))
96		t.Fatal("fatal error")
97	}
98
99	// test the unpacker
100	unpacked, n, err := packed.Unpack(true)
101	if nil != err {
102		t.Fatalf("unpack error: %s", err)
103	}
104	if len(packed) != n {
105		t.Errorf("did not unpack all data: only used: %d of: %d bytes", n, len(packed))
106	}
107
108	bmt, ok := unpacked.(*transactionrecord.BitmarkIssue)
109	if !ok {
110		t.Fatalf("did not unpack to BitmarkIssue")
111	}
112
113	// display a JSON version for information
114	item := struct {
115		TxId         merkle.Digest
116		BitmarkIssue *transactionrecord.BitmarkIssue
117	}{
118		txId,
119		bmt,
120	}
121	b, err := json.MarshalIndent(item, "", "  ")
122	if nil != err {
123		t.Fatalf("json error: %s", err)
124	}
125
126	t.Logf("Bitmark Issue: JSON: %s", b)
127
128	// check that structure is preserved through Pack/Unpack
129	// note reg is a pointer here
130	if !reflect.DeepEqual(r, *bmt) {
131		t.Fatalf("different, original: %v  recovered: %v", r, *bmt)
132	}
133	checkPackedData(t, "issue", packed)
134}
135
136// make 10 separate issues for testing
137//
138// This only prints out 10 valid issue records that can be used for
139// simple testing
140func TestPackTenBitmarkIssues(t *testing.T) {
141
142	issuerAccount := makeAccount(issuer.publicKey)
143
144	var assetId transactionrecord.AssetIdentifier
145	_, err := fmt.Sscan("59d06155d25dffdb982729de8dce9d7855ca094d8bab8124b347c40668477056b3c27ccb7d71b54043d207ccd187642bf9c8466f9a8d0dbefb4c41633a7e39ef", &assetId)
146	if nil != err {
147		t.Fatalf("hex to asset id error: %s", err)
148	}
149
150	rs := make([]*transactionrecord.BitmarkIssue, 10)
151	for i := 0; i < len(rs); i += 1 {
152		r := &transactionrecord.BitmarkIssue{
153			AssetId: assetId,
154			Owner:   issuerAccount,
155			Nonce:   uint64(i) + 1,
156		}
157		rs[i] = r
158
159		partial, err := r.Pack(issuerAccount)
160		if fault.InvalidSignature != err {
161			if nil != partial {
162				t.Errorf("partial packed:\n%s", util.FormatBytes("expected", partial))
163			}
164			t.Fatalf("pack error: %s", err)
165		}
166		signature := ed25519.Sign(issuer.privateKey, partial)
167		r.Signature = signature
168
169		_, err = r.Pack(issuerAccount)
170		if nil != err {
171			t.Fatalf("pack error: %s", err)
172		}
173	}
174	// display a JSON version for information
175	b, err := json.MarshalIndent(rs, "", "  ")
176	if nil != err {
177		t.Fatalf("json error: %s", err)
178	}
179
180	t.Logf("Bitmark Issue: JSON: %s", b)
181}
182
183// test the pack failure on trying to use the zero public key
184func TestPackBitmarkIssueWithZeroAccount(t *testing.T) {
185
186	issuerAccount := makeAccount(theZeroKey.publicKey)
187
188	var assetId transactionrecord.AssetIdentifier
189	_, err := fmt.Sscan("59d06155d25dffdb982729de8dce9d7855ca094d8bab8124b347c40668477056b3c27ccb7d71b54043d207ccd187642bf9c8466f9a8d0dbefb4c41633a7e39ef", &assetId)
190	if nil != err {
191		t.Fatalf("hex to asset id error: %s", err)
192	}
193
194	r := transactionrecord.BitmarkIssue{
195		AssetId:   assetId,
196		Owner:     issuerAccount,
197		Nonce:     99,
198		Signature: []byte{1, 2, 3, 4},
199	}
200
201	// test the packer
202	_, err = r.Pack(issuerAccount)
203	if nil == err {
204		t.Fatalf("pack should have failed")
205	}
206	if fault.InvalidOwnerOrRegistrant != err {
207		t.Fatalf("unexpected pack error: %s", err)
208	}
209}
210