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	"encoding/binary"
10	"sync"
11
12	"github.com/bitmark-inc/bitmarkd/account"
13	"github.com/bitmark-inc/bitmarkd/merkle"
14	"github.com/bitmark-inc/bitmarkd/mode"
15	"github.com/bitmark-inc/bitmarkd/storage"
16	"github.com/bitmark-inc/bitmarkd/transactionrecord"
17	"github.com/bitmark-inc/logger"
18)
19
20// to ensure synchronised ownership updates
21var toLock sync.Mutex
22
23// from storage/setup.go:
24//
25// Ownership:
26//   OwnerNextCount  BN   - next count value to use for appending to owned items
27//   OwnerList       txId - list of owned items
28//   OwnerTxIndex    BN   - position in list of owned items, for delete after transfer
29
30// Share - setup for share ownership transfer, must have a lock
31func Share(
32	trx storage.Transaction,
33	previousTxId merkle.Digest,
34	transferTxId merkle.Digest,
35	transferBlockNumber uint64,
36	currentOwner *account.Account,
37	balance uint64,
38) {
39
40	// ensure single threaded
41	toLock.Lock()
42	defer toLock.Unlock()
43
44	// delete current ownership
45	transfer(trx, previousTxId, transferTxId, transferBlockNumber, currentOwner, nil, balance)
46}
47
48// Transfer - transfer ownership
49func Transfer(
50	trx storage.Transaction,
51	previousTxId merkle.Digest,
52	transferTxId merkle.Digest,
53	transferBlockNumber uint64,
54	currentOwner *account.Account,
55	newOwner *account.Account,
56) {
57	// ensure single threaded
58	toLock.Lock()
59	defer toLock.Unlock()
60
61	transfer(trx, previousTxId, transferTxId, transferBlockNumber, currentOwner, newOwner, 0)
62}
63
64// need to hold the lock before calling this
65func transfer(
66	trx storage.Transaction,
67	previousTxId merkle.Digest,
68	transferTxId merkle.Digest,
69	transferBlockNumber uint64,
70
71	currentOwner *account.Account,
72	newOwner *account.Account,
73	quantity uint64,
74) {
75	// get count for current owner record
76	dKey := append(currentOwner.Bytes(), previousTxId[:]...)
77	dCount := trx.Get(storage.Pool.OwnerTxIndex, dKey)
78	if nil == dCount {
79		logger.Criticalf("ownership.Transfer: dKey: %x", dKey)
80		logger.Criticalf("ownership.Transfer: block number: %d", transferBlockNumber)
81		logger.Criticalf("ownership.Transfer: previous tx id: %#v", previousTxId)
82		logger.Criticalf("ownership.Transfer: transfer tx id: %#v", transferTxId)
83		logger.Criticalf("ownership.Transfer: current owner: %x  %v", currentOwner.Bytes(), currentOwner)
84		if nil != newOwner {
85			logger.Criticalf("ownership.Transfer: new     owner: %x  %v", newOwner.Bytes(), newOwner)
86		}
87
88		// ow, err := listBitmarksFor(currentOwner, 0, 999)
89		// if nil != err {
90		// 	logger.Criticalf("lbf: error: %s", err)
91		// } else {
92		// 	logger.Criticalf("lbf: %#v", ow)
93		// }
94
95		logger.Panic("ownership.Transfer: OwnerTxIndex database corrupt")
96	}
97
98	// delete the current owners records
99	ownerData, err := GetOwnerData(trx, previousTxId, storage.Pool.OwnerData)
100	if nil != err {
101		logger.Criticalf("ownership.Transfer: invalid owner data for tx id: %s  error: %s", previousTxId, err)
102		logger.Panic("ownership.Transfer: Ownership database corrupt")
103	}
104
105	oKey := append(currentOwner.Bytes(), dCount...)
106	trx.Delete(storage.Pool.OwnerList, oKey)
107	trx.Delete(storage.Pool.OwnerTxIndex, dKey)
108
109	// and the old owner data
110	trx.Delete(storage.Pool.OwnerData, previousTxId[:])
111
112	// if no new owner only above delete was needed
113	if nil == newOwner && 0 == quantity {
114		return
115	}
116
117	switch ownerData := ownerData.(type) {
118
119	case *AssetOwnerData:
120
121		// create a share - only from an asset
122		if 0 != quantity {
123
124			// convert initial quantity to 8 byte big endian
125			quantityBytes := make([]byte, 8)
126			binary.BigEndian.PutUint64(quantityBytes, quantity)
127
128			// the ID of the share is the issue id of the bitmark
129			shareId := ownerData.issueTxId
130
131			// the total quantity of this type of share
132			shareData := append(quantityBytes, transferTxId[:]...)
133			trx.Put(storage.Pool.Shares, shareId[:], shareData, []byte{})
134
135			// initially total quantity goes to the creator
136			fKey := append(currentOwner.Bytes(), shareId[:]...)
137			trx.Put(storage.Pool.ShareQuantity, fKey, quantityBytes, []byte{})
138
139			// convert to share and update
140			newOwnerData := ShareOwnerData{
141				transferBlockNumber: transferBlockNumber,
142				issueTxId:           ownerData.issueTxId,
143				issueBlockNumber:    ownerData.issueBlockNumber,
144				assetId:             ownerData.assetId,
145			}
146			create(trx, transferTxId, newOwnerData, currentOwner)
147			return
148		}
149
150		// otherwise create new ownership record
151		ownerData.transferBlockNumber = transferBlockNumber
152		create(trx, transferTxId, ownerData, newOwner)
153
154	case *BlockOwnerData:
155		// create a share - only from an asset
156		if 0 != quantity {
157
158			// panic if not an asset (this should have been checked earlier)
159			logger.Criticalf("ownership.Transfer: ownerData for key: %x is not an asset", oKey)
160			logger.Panic("ownership.Transfer: Ownership database corrupt")
161		}
162
163		// otherwise create new ownership record
164		ownerData.transferBlockNumber = transferBlockNumber
165		create(trx, transferTxId, ownerData, newOwner)
166
167	case *ShareOwnerData:
168
169		// create a share - only from an asset
170		if 0 != quantity {
171
172			// panic if not an asset (this should have been checked earlier)
173			logger.Criticalf("ownership.Transfer: ownerData for key: %x is not an asset", oKey)
174			logger.Panic("ownership.Transfer: Ownership database corrupt")
175		}
176
177		// Note: only called on delete (block/store.go prevents share back to asset)
178
179		// convert to transfer and update
180		newOwnerData := AssetOwnerData{
181			transferBlockNumber: transferBlockNumber,
182			issueTxId:           ownerData.issueTxId,
183			issueBlockNumber:    ownerData.issueBlockNumber,
184			assetId:             ownerData.assetId,
185		}
186		create(trx, transferTxId, newOwnerData, currentOwner)
187
188	default:
189		// panic if not an asset (this should have been checked earlier)
190		logger.Criticalf("ownership.Transfer: unhandled owner data type: %+v", ownerData)
191		logger.Panic("ownership.Transfer: missing owner data handler")
192	}
193}
194
195// internal creation routine, must be called with lock held
196// adds items to owner's list of properties and stores the owner data
197func create(
198	trx storage.Transaction,
199	txId merkle.Digest,
200	ownerData OwnerData,
201	owner *account.Account,
202) {
203	// increment the count for owner
204	nKey := owner.Bytes()
205	count := trx.Get(storage.Pool.OwnerNextCount, nKey)
206	if nil == count {
207		count = []byte{0, 0, 0, 0, 0, 0, 0, 0}
208	} else if uint64ByteSize != len(count) {
209		logger.Panic("OwnerNextCount database corrupt")
210	}
211	newCount := make([]byte, uint64ByteSize)
212	binary.BigEndian.PutUint64(newCount, binary.BigEndian.Uint64(count)+1)
213	trx.Put(storage.Pool.OwnerNextCount, nKey, newCount, []byte{})
214
215	// write to the owner list
216	oKey := append(owner.Bytes(), count...)
217	trx.Put(storage.Pool.OwnerList, oKey, txId[:], []byte{})
218
219	// write new index record
220	dKey := append(owner.Bytes(), txId[:]...)
221	trx.Put(storage.Pool.OwnerTxIndex, dKey, count, []byte{})
222
223	// save owner data record
224	trx.Put(storage.Pool.OwnerData, txId[:], ownerData.Pack(), []byte{})
225}
226
227// CreateAsset - create the ownership record for an asset
228func CreateAsset(
229	trx storage.Transaction,
230	issueTxId merkle.Digest,
231	issueBlockNumber uint64,
232	assetId transactionrecord.AssetIdentifier,
233	newOwner *account.Account,
234) {
235	// ensure single threaded
236	toLock.Lock()
237	defer toLock.Unlock()
238
239	newData := &AssetOwnerData{
240		transferBlockNumber: issueBlockNumber,
241		issueTxId:           issueTxId,
242		issueBlockNumber:    issueBlockNumber,
243		assetId:             assetId,
244	}
245
246	// store to database
247	create(trx, issueTxId, newData, newOwner)
248}
249
250// CreateBlock - create the ownership record for a block
251func CreateBlock(
252	trx storage.Transaction,
253	issueTxId merkle.Digest,
254	blockNumber uint64,
255	newOwner *account.Account,
256) {
257	// ensure single threaded
258	toLock.Lock()
259	defer toLock.Unlock()
260
261	newData := &BlockOwnerData{
262		transferBlockNumber: blockNumber,
263		issueTxId:           issueTxId,
264		issueBlockNumber:    blockNumber,
265	}
266
267	// store to database
268	create(trx, issueTxId, newData, newOwner)
269}
270
271// OwnerOf - find the owner of a specific transaction
272func OwnerOf(trx storage.Transaction, txId merkle.Digest) (uint64, *account.Account) {
273	var blockNumber uint64
274	var packed []byte
275
276	if nil == trx {
277		blockNumber, packed = storage.Pool.Transactions.GetNB(txId[:])
278	} else {
279		blockNumber, packed = trx.GetNB(storage.Pool.Transactions, txId[:])
280	}
281
282	if nil == packed {
283		return 0, nil
284	}
285
286	transaction, _, err := transactionrecord.Packed(packed).Unpack(mode.IsTesting())
287	logger.PanicIfError("ownership.OwnerOf", err)
288
289	switch tx := transaction.(type) {
290	case *transactionrecord.BitmarkIssue:
291		return blockNumber, tx.Owner
292
293	case *transactionrecord.BitmarkTransferUnratified:
294		return blockNumber, tx.Owner
295
296	case *transactionrecord.BitmarkTransferCountersigned:
297		return blockNumber, tx.Owner
298
299	case *transactionrecord.BlockFoundation:
300		return blockNumber, tx.Owner
301
302	case *transactionrecord.BlockOwnerTransfer:
303		return blockNumber, tx.Owner
304
305	default:
306		logger.Panicf("block.OwnerOf: incorrect transaction: %v", transaction)
307		return 0, nil
308	}
309}
310
311// CurrentlyOwns - check owner currently owns this transaction id
312func CurrentlyOwns(
313	trx storage.Transaction,
314	owner *account.Account,
315	txId merkle.Digest,
316	pool storage.Handle,
317) bool {
318	dKey := append(owner.Bytes(), txId[:]...)
319
320	if nil == trx {
321		return pool.Has(dKey)
322	}
323	return trx.Has(pool, dKey)
324}
325