Skip to content

Commit

Permalink
Extra Data on Posts, NFTs, Messages, Messaging Groups, and Derived Ke…
Browse files Browse the repository at this point in the history
…ys (#213)

* Add ProfileExtraData to ProfileEntry struct

* add test to ensure extra data comes through. update calls to CreateUpdateProfileTxn, add postgres support

* Update lib/block_view_profile_test.go

* Review and cleanup

* Add one more fixme

* update profile test for extra data

* ExtraData on NFT / Message / GroupMessagingKey / DerivedKey entries (#221)

* Return error if error during txindex db update (#220)

* Add extra data to NFT, Message, GroupMessagingKey, and DerivedKey
entries

Return error if error during txindex db update (#220)

add tests for adding extra data on NFT, private message, messaging group, and derived key entries

add comment on extra data

* Fix Issue with ParentStakeID set when modifying Post (#222)

* move ComputeMetadata to a switch-case and glog errors for Like and SubmitPost Txns

* don't return an error from ComputeTransactionMetadata, just glog it

* Create accept bid for buynow when create bid in postgres (#226)

* Create accept bid for buynow when create bid in postgres

* Update lib/postgres.go

Co-authored-by: Lazy Nina <[email protected]>

* add AcceptedBlockHeight to NFTBidEntry (REQUIRES RESYNC) (#224)

* add AcceptedBlockHeight to NFTBidEntry

* Set Accepted on PGNFTBid is AcceptedBlockHeight is not nil

* only copy AcceptedBlockHeight if not nil

* add txindex metadata for Burns and Accept NFT Transfers (#228)

* Update nodes.go (#202)

* Update nodes.go

* Update nodes.go

* Add upcoming overclout node (#219)

* Add upcoming overclout node

* Update lib/nodes.go

Co-authored-by: Lazy Nina <[email protected]>

* Fix get-all-messaging-keys & add base point test (#217)

* add remaining UtxoTypes to UtxoType.String() (#229)

* diamondhands touchups with some FIXMEs for @lazynina

* delete fixmes about deleting protected keys

* add test for encoding/decoding message entries for backwards compatibility

* add test for encoding/decoding messaging group entries for backwards compatibility

Co-authored-by: Martijn van Halen <[email protected]>
Co-authored-by: elmasapp <[email protected]>
Co-authored-by: Don Hardman <[email protected]>
Co-authored-by: Piotr Nojszewski <[email protected]>
Co-authored-by: diamondhands <[email protected]>

* Update lib/blockchain.go

Co-authored-by: diamondhands <[email protected]>
Co-authored-by: Martijn van Halen <[email protected]>
Co-authored-by: elmasapp <[email protected]>
Co-authored-by: Don Hardman <[email protected]>
Co-authored-by: Piotr Nojszewski <[email protected]>
  • Loading branch information
6 people authored Mar 3, 2022
1 parent c971166 commit d6ff118
Show file tree
Hide file tree
Showing 17 changed files with 952 additions and 118 deletions.
18 changes: 18 additions & 0 deletions lib/block_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -2676,3 +2676,21 @@ func (bav *UtxoView) GetSpendableDeSoBalanceNanosForPublicKey(pkBytes []byte,
}
return balanceNanos - immatureBlockRewards, nil
}

func mergeExtraData(oldMap map[string][]byte, newMap map[string][]byte) map[string][]byte {
// Always create the map from scratch, since modifying the map on
// newMap could modify the map on the oldMap otherwise.
retMap := make(map[string][]byte)

// Add the values from the oldMap
for kk, vv := range oldMap {
retMap[kk] = vv
}
// Add the values from the newMap. Allow the newMap values to overwrite the
// oldMap values during the merge.
for kk, vv := range newMap {
retMap[kk] = vv
}

return retMap
}
10 changes: 10 additions & 0 deletions lib/block_view_derived_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ func (bav *UtxoView) _connectAuthorizeDerivedKey(
}
}

var extraData map[string][]byte
if blockHeight >= bav.Params.ForkHeights.ExtraDataOnEntriesBlockHeight {
var prevExtraData map[string][]byte
if prevDerivedKeyEntry != nil && !prevDerivedKeyEntry.isDeleted {
prevExtraData = prevDerivedKeyEntry.ExtraData
}
extraData = mergeExtraData(prevExtraData, txn.ExtraData)
}

// This is the new state of transaction spending limits after merging in the transaction spending limit object
// defined in extra data
var newTransactionSpendingLimit *TransactionSpendingLimit
Expand Down Expand Up @@ -234,6 +243,7 @@ func (bav *UtxoView) _connectAuthorizeDerivedKey(
OperationType: AuthorizeDerivedKeyOperationValid,
TransactionSpendingLimitTracker: newTransactionSpendingLimit,
Memo: memo,
ExtraData: extraData,
isDeleted: false,
}
bav._setDerivedKeyMapping(&derivedKeyEntry)
Expand Down
56 changes: 45 additions & 11 deletions lib/block_view_derived_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ func _doTxn(
buyNowPriceNanos,
additionalDESORoyaltyMap,
additionalCoinRoyaltyMap,
nil,
feeRateNanosPerKB,
nil,
nil,
Expand Down Expand Up @@ -239,6 +240,7 @@ func _doTxn(
realTxMeta.AccessSignature,
deleteKey,
false,
nil,
memo,
transactionSpendingLimit,
feeRateNanosPerKB,
Expand All @@ -258,6 +260,7 @@ func _doTxn(
realTxMeta.NewStakeMultipleBasisPoints,
realTxMeta.IsHidden,
0,
nil,
feeRateNanosPerKB,
nil,
nil,
Expand Down Expand Up @@ -516,11 +519,23 @@ func _getAuthorizeDerivedKeyMetadataWithTransactionSpendingLimitAndDerivedPrivat
}, derivedPrivateKey
}

// Create a new AuthorizeDerivedKey txn and connect it to the utxoView
func _doAuthorizeTxn(t *testing.T, chain *Blockchain, db *badger.DB,
params *DeSoParams, utxoView *UtxoView, feeRateNanosPerKB uint64, ownerPublicKey []byte,
derivedPublicKey []byte, derivedPrivBase58Check string, expirationBlock uint64,
accessSignature []byte, deleteKey bool, memo []byte, transactionSpendingLimit *TransactionSpendingLimit) (_utxoOps []*UtxoOperation,
accessSignature []byte, deleteKey bool,
memo []byte, transactionSpendingLimit *TransactionSpendingLimit) (_utxoOps []*UtxoOperation,
_txn *MsgDeSoTxn, _height uint32, _err error) {
return _doAuthorizeTxnWithExtraDataAndSpendingLimits(t, chain, db, params, utxoView, feeRateNanosPerKB, ownerPublicKey,
derivedPublicKey, derivedPrivBase58Check, expirationBlock, accessSignature, deleteKey,
nil, memo, transactionSpendingLimit)
}

// Create a new AuthorizeDerivedKey txn and connect it to the utxoView
func _doAuthorizeTxnWithExtraDataAndSpendingLimits(t *testing.T, chain *Blockchain, db *badger.DB,
params *DeSoParams, utxoView *UtxoView, feeRateNanosPerKB uint64, ownerPublicKey []byte,
derivedPublicKey []byte, derivedPrivBase58Check string, expirationBlock uint64,
accessSignature []byte, deleteKey bool, extraData map[string][]byte,
memo []byte, transactionSpendingLimit *TransactionSpendingLimit) (_utxoOps []*UtxoOperation,
_txn *MsgDeSoTxn, _height uint32, _err error) {

assert := assert.New(t)
Expand All @@ -535,6 +550,7 @@ func _doAuthorizeTxn(t *testing.T, chain *Blockchain, db *badger.DB,
accessSignature,
deleteKey,
false,
extraData,
memo,
transactionSpendingLimit,
feeRateNanosPerKB,
Expand Down Expand Up @@ -572,7 +588,9 @@ func _doAuthorizeTxn(t *testing.T, chain *Blockchain, db *badger.DB,
if blockHeight >= params.ForkHeights.DerivedKeyTrackSpendingLimitsBlockHeight {
transactionSpendingLimitCount++
}
require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1+transactionSpendingLimitCount, len(utxoOps))
// We should have one SPEND UtxoOperation for each input, one ADD operation
// for each output, and one OperationTypeUpdateProfile operation at the end.
require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+transactionSpendingLimitCount+1, len(utxoOps))
for ii := 0; ii < len(txn.TxInputs); ii++ {
require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
}
Expand All @@ -591,6 +609,7 @@ func TestAuthorizeDerivedKeyBasic(t *testing.T) {
mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)

params.ForkHeights.NFTTransferOrBurnAndDerivedKeysBlockHeight = uint32(0)
params.ForkHeights.ExtraDataOnEntriesBlockHeight = uint32(0)

// Mine two blocks to give the sender some DeSo.
_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
Expand Down Expand Up @@ -665,15 +684,16 @@ func TestAuthorizeDerivedKeyBasic(t *testing.T) {
}

// Verify that the balance and expiration block in the db match expectation.
_verifyTest := func(derivedPublicKey []byte, expirationBlockExpected uint64,
balanceExpected uint64, operationTypeExpected AuthorizeDerivedKeyOperationType, mempool *DeSoMempool) {
_verifyTestWithExtraData := func(derivedPublicKey []byte, expirationBlockExpected uint64,
balanceExpected uint64, operationTypeExpected AuthorizeDerivedKeyOperationType, extraData map[string][]byte,
mempool *DeSoMempool) {
// Verify that expiration block was persisted in the db or is in mempool utxoView
if mempool == nil {
derivedKeyEntry := DBGetOwnerToDerivedKeyMapping(db, *NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey))
// If we removed the derivedKeyEntry from utxoView altogether, it'll be nil.
// To pass the tests, we initialize it to a default struct.
if derivedKeyEntry == nil || derivedKeyEntry.isDeleted {
derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, transactionSpendingLimit, nil, false}
derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, nil, transactionSpendingLimit, nil, false}
}
assert.Equal(derivedKeyEntry.ExpirationBlock, expirationBlockExpected)
assert.Equal(derivedKeyEntry.OperationType, operationTypeExpected)
Expand All @@ -684,7 +704,7 @@ func TestAuthorizeDerivedKeyBasic(t *testing.T) {
// If we removed the derivedKeyEntry from utxoView altogether, it'll be nil.
// To pass the tests, we initialize it to a default struct.
if derivedKeyEntry == nil || derivedKeyEntry.isDeleted {
derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, transactionSpendingLimit, nil, false}
derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, nil, transactionSpendingLimit, nil, false}
}
assert.Equal(derivedKeyEntry.ExpirationBlock, expirationBlockExpected)
assert.Equal(derivedKeyEntry.OperationType, operationTypeExpected)
Expand All @@ -694,6 +714,12 @@ func TestAuthorizeDerivedKeyBasic(t *testing.T) {
assert.Equal(_getBalance(t, chain, mempool, recipientPkString), balanceExpected)
}

_verifyTest := func(derivedPublicKey []byte, expirationBlockExpected uint64,
balanceExpected uint64, operationTypeExpected AuthorizeDerivedKeyOperationType, mempool *DeSoMempool) {
_verifyTestWithExtraData(derivedPublicKey, expirationBlockExpected, balanceExpected,
operationTypeExpected, nil, mempool)
}

// We will use these to keep track of added utxo ops and txns
testUtxoOps := [][]*UtxoOperation{}
testTxns := []*MsgDeSoTxn{}
Expand Down Expand Up @@ -788,7 +814,11 @@ func TestAuthorizeDerivedKeyBasic(t *testing.T) {
{
utxoView, err := NewUtxoView(db, params, nil)
require.NoError(err)
utxoOps, txn, _, err := _doAuthorizeTxn(

extraData := map[string][]byte{
"test": []byte("result"),
}
utxoOps, txn, _, err := _doAuthorizeTxnWithExtraDataAndSpendingLimits(
t,
chain,
db,
Expand All @@ -801,6 +831,7 @@ func TestAuthorizeDerivedKeyBasic(t *testing.T) {
authTxnMeta.ExpirationBlock,
authTxnMeta.AccessSignature,
false,
extraData,
nil,
transactionSpendingLimit,
)
Expand All @@ -811,7 +842,9 @@ func TestAuthorizeDerivedKeyBasic(t *testing.T) {
testTxns = append(testTxns, txn)

// Verify that expiration block was persisted in the db
_verifyTest(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 0, AuthorizeDerivedKeyOperationValid, nil)
_verifyTestWithExtraData(authTxnMeta.DerivedPublicKey, authTxnMeta.ExpirationBlock, 0, AuthorizeDerivedKeyOperationValid, extraData, nil)
derivedKeyEntry := DBGetOwnerToDerivedKeyMapping(db, *NewPublicKey(senderPkBytes), *NewPublicKey(authTxnMeta.DerivedPublicKey))
require.Equal(derivedKeyEntry.ExtraData["test"], []byte("result"))
fmt.Println("Passed connecting AuthorizeDerivedKey txn signed with an authorized private key. Flushed to Db.")
}
// Check basic transfer signed by the owner key.
Expand Down Expand Up @@ -1564,7 +1597,8 @@ func TestAuthorizeDerivedKeyBasicWithTransactionLimits(t *testing.T) {
// If we removed the derivedKeyEntry from utxoView altogether, it'll be nil.
// To pass the tests, we initialize it to a default struct.
if derivedKeyEntry == nil || derivedKeyEntry.isDeleted {
derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, transactionSpendingLimit, nil, false}
derivedKeyEntry = &DerivedKeyEntry{
*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, nil, transactionSpendingLimit, nil, false}
}
assert.Equal(derivedKeyEntry.ExpirationBlock, expirationBlockExpected)
assert.Equal(derivedKeyEntry.OperationType, operationTypeExpected)
Expand All @@ -1575,7 +1609,7 @@ func TestAuthorizeDerivedKeyBasicWithTransactionLimits(t *testing.T) {
// If we removed the derivedKeyEntry from utxoView altogether, it'll be nil.
// To pass the tests, we initialize it to a default struct.
if derivedKeyEntry == nil || derivedKeyEntry.isDeleted {
derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, transactionSpendingLimit, nil, false}
derivedKeyEntry = &DerivedKeyEntry{*NewPublicKey(senderPkBytes), *NewPublicKey(derivedPublicKey), 0, AuthorizeDerivedKeyOperationValid, nil, transactionSpendingLimit, nil, false}
}
assert.Equal(derivedKeyEntry.ExpirationBlock, expirationBlockExpected)
assert.Equal(derivedKeyEntry.OperationType, operationTypeExpected)
Expand Down
29 changes: 26 additions & 3 deletions lib/block_view_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ func (bav *UtxoView) GetMessagingGroupKeyToMessagingGroupEntryMapping(
}

messagingGroupEntry := &MessagingGroupEntry{
GroupOwnerPublicKey: pgMessagingGroup.GroupOwnerPublicKey,
MessagingPublicKey: pgMessagingGroup.MessagingPublicKey,
GroupOwnerPublicKey: pgMessagingGroup.GroupOwnerPublicKey,
MessagingPublicKey: pgMessagingGroup.MessagingPublicKey,
MessagingGroupKeyName: pgMessagingGroup.MessagingGroupKeyName,
MessagingGroupMembers: memberEntries,
}
Expand Down Expand Up @@ -425,6 +425,14 @@ func (bav *UtxoView) _connectPrivateMessage(
return 0, 0, nil, errors.Wrapf(err, "_connectPrivateMessage: ")
}

// If we're past the ExtraData block height then merge the extraData in from the
// txn ExtraData.
var extraData map[string][]byte
if blockHeight >= bav.Params.ForkHeights.ExtraDataOnEntriesBlockHeight {
// There's no previous entry to look up for messages
extraData = mergeExtraData(nil, txn.ExtraData)
}

// Create a MessageEntry, we do this now because we might modify some of the fields
// based on the version of the message.
messageEntry := &MessageEntry{
Expand All @@ -437,6 +445,7 @@ func (bav *UtxoView) _connectPrivateMessage(
SenderMessagingGroupKeyName: BaseGroupKeyName(),
RecipientMessagingPublicKey: NewPublicKey(txMeta.RecipientPublicKey),
RecipientMessagingGroupKeyName: BaseGroupKeyName(),
ExtraData: extraData,
}

// If message was encrypted using DeSo V3 Messages, we will look for messaging keys in
Expand Down Expand Up @@ -542,6 +551,7 @@ func (bav *UtxoView) _connectPrivateMessage(
RecipientPublicKey: txMeta.RecipientPublicKey,
EncryptedText: txMeta.EncryptedText,
TimestampNanos: txMeta.TimestampNanos,
ExtraData: extraData,
}

bav.setMessageMappings(message)
Expand Down Expand Up @@ -903,6 +913,15 @@ func (bav *UtxoView) _connectMessagingGroup(
messagingMembers = append(messagingMembers, messagingMember)
}

var extraData map[string][]byte
if blockHeight >= bav.Params.ForkHeights.ExtraDataOnEntriesBlockHeight {
var existingExtraData map[string][]byte
if existingEntry != nil && !existingEntry.isDeleted {
existingExtraData = existingEntry.ExtraData
}
extraData = mergeExtraData(existingExtraData, txn.ExtraData)
}

// TODO: Currently, it is technically possible for any user to add *any other* user to *any group* with
// a garbage EncryptedKey. This can be filtered out at the app layer, though, and for now it leaves the
// app layer with more flexibility compared to if we implemented an explicit permissioning model at the
Expand All @@ -914,12 +933,16 @@ func (bav *UtxoView) _connectMessagingGroup(
MessagingPublicKey: messagingPublicKey,
MessagingGroupKeyName: NewGroupKeyName(txMeta.MessagingGroupKeyName),
MessagingGroupMembers: messagingMembers,
ExtraData: extraData,
}
// Create a utxoOps entry, we make a copy of the existing entry.
var prevMessagingKeyEntry *MessagingGroupEntry
if existingEntry != nil && !existingEntry.isDeleted {
prevMessagingKeyEntry = &MessagingGroupEntry{}
prevMessagingKeyEntry.Decode(existingEntry.Encode())
if err = prevMessagingKeyEntry.Decode(existingEntry.Encode()); err != nil {
return 0, 0, nil, errors.Wrapf(err,
"_connectMessagingGroup: Error decoding previous entry")
}
}
bav._setMessagingGroupKeyToMessagingGroupEntryMapping(&messagingGroupKey.OwnerPublicKey, &messagingGroupEntry)

Expand Down
Loading

0 comments on commit d6ff118

Please sign in to comment.