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 ownership
7
8import (
9	"bytes"
10	"encoding/binary"
11
12	"github.com/bitmark-inc/bitmarkd/account"
13	"github.com/bitmark-inc/bitmarkd/merkle"
14	"github.com/bitmark-inc/bitmarkd/storage"
15	"github.com/bitmark-inc/bitmarkd/transactionrecord"
16	"github.com/bitmark-inc/logger"
17)
18
19// Record - type to represent an ownership record
20type Record struct {
21	N           uint64                             `json:"n,string"`
22	TxId        merkle.Digest                      `json:"txId"`
23	IssueTxId   merkle.Digest                      `json:"issue"`
24	Item        OwnedItem                          `json:"item"`
25	AssetId     *transactionrecord.AssetIdentifier `json:"assetId,omitempty"`
26	BlockNumber *uint64                            `json:"blockNumber,omitempty"`
27}
28
29// listBitmarksFor - fetch a list of bitmarks for an owner
30func listBitmarksFor(owner *account.Account, start uint64, count int) ([]Record, error) {
31
32	startBytes := make([]byte, uint64ByteSize)
33	binary.BigEndian.PutUint64(startBytes, start)
34
35	ownerBytes := owner.Bytes()
36	prefix := append(ownerBytes, startBytes...)
37
38	cursor := storage.Pool.OwnerList.NewFetchCursor().Seek(prefix)
39
40	// owner ⧺ count → txId
41	items, err := cursor.Fetch(count)
42	if nil != err {
43		return nil, err
44	}
45
46	records := make([]Record, 0, len(items))
47
48loop:
49	for _, item := range items {
50		n := len(item.Key)
51		split := n - uint64ByteSize
52		if split <= 0 {
53			logger.Panicf("split cannot be <= 0: %d", split)
54		}
55		itemOwner := item.Key[:n-uint64ByteSize]
56		if !bytes.Equal(ownerBytes, itemOwner) {
57			break loop
58		}
59
60		record := Record{
61			N: binary.BigEndian.Uint64(item.Key[split:]),
62		}
63
64		merkle.DigestFromBytes(&record.TxId, item.Value)
65
66		ownerData, err := GetOwnerData(nil, record.TxId, storage.Pool.OwnerData)
67		if nil != err {
68			return nil, err
69		}
70
71		switch od := ownerData.(type) {
72		case *AssetOwnerData:
73			record.Item = OwnedAsset
74			record.IssueTxId = od.issueTxId
75			record.AssetId = &od.assetId
76
77		case *BlockOwnerData:
78			record.Item = OwnedBlock
79			record.IssueTxId = od.issueTxId
80			record.BlockNumber = &od.issueBlockNumber
81
82		case *ShareOwnerData:
83			record.Item = OwnedShare
84			record.IssueTxId = od.issueTxId
85			record.AssetId = &od.assetId
86
87		default:
88			logger.Panicf("unsupported item type: %d", item)
89		}
90		records = append(records, record)
91	}
92
93	return records, nil
94}
95