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
7
8import (
9	"encoding/hex"
10	"fmt"
11
12	"golang.org/x/crypto/sha3"
13
14	"github.com/bitmark-inc/bitmarkd/fault"
15)
16
17// limits
18const (
19	AssetIdentifierLength = 64
20)
21
22// AssetIdentifier - the type for an asset identifier
23// stored as little endian byte array
24// represented as little endian hex text for JSON encoding
25// convert a binary assetId to byte slice
26// to get bytes value just use assetId[:]
27type AssetIdentifier [AssetIdentifierLength]byte
28
29// NewAssetIdentifier - create an asset id from a byte slice
30//
31// SHA3-512 Hash
32func NewAssetIdentifier(record []byte) AssetIdentifier {
33	return AssetIdentifier(sha3.Sum512(record))
34}
35
36// String - convert a binary assetId to little endian hex string for use by the fmt package (for %s)
37func (assetId AssetIdentifier) String() string {
38	return hex.EncodeToString(assetId[:])
39}
40
41// GoString - convert a binary assetId to little endian hex string for use by the fmt package (for %#v)
42func (assetId AssetIdentifier) GoString() string {
43	return "<asset:" + hex.EncodeToString(assetId[:]) + ">"
44}
45
46// Scan - convert a little endian hex text representation to a assetId for use by the format package scan routines
47func (assetId *AssetIdentifier) Scan(state fmt.ScanState, verb rune) error {
48	token, err := state.Token(true, func(c rune) bool {
49		if c >= '0' && c <= '9' {
50			return true
51		}
52		if c >= 'A' && c <= 'F' {
53			return true
54		}
55		if c >= 'a' && c <= 'f' {
56			return true
57		}
58		return false
59	})
60	if nil != err {
61		return err
62	}
63	if len(token) != hex.EncodedLen(AssetIdentifierLength) {
64		return fault.NotAssetId
65	}
66
67	byteCount, err := hex.Decode(assetId[:], token)
68	if nil != err {
69		return err
70	}
71
72	if AssetIdentifierLength != byteCount {
73		return fault.NotAssetId
74	}
75	return nil
76}
77
78// MarshalText - convert assetId to little endian hex text
79func (assetId AssetIdentifier) MarshalText() ([]byte, error) {
80	size := hex.EncodedLen(len(assetId))
81	buffer := make([]byte, size)
82	hex.Encode(buffer, assetId[:])
83	return buffer, nil
84}
85
86// UnmarshalText - convert little endian hex text into a assetId
87func (assetId *AssetIdentifier) UnmarshalText(s []byte) error {
88	if len(assetId) != hex.DecodedLen(len(s)) {
89		return fault.NotLink
90	}
91	byteCount, err := hex.Decode(assetId[:], s)
92	if nil != err {
93		return err
94	}
95	if AssetIdentifierLength != byteCount {
96		return fault.NotAssetId
97	}
98	return nil
99}
100
101// AssetIdentifierFromBytes - convert and validate little endian binary byte slice to a assetId
102func AssetIdentifierFromBytes(assetId *AssetIdentifier, buffer []byte) error {
103	if AssetIdentifierLength != len(buffer) {
104		return fault.NotAssetId
105	}
106	copy(assetId[:], buffer)
107	return nil
108}
109