From 587ab2499a8826a642e4f9915d8e5b093719f004 Mon Sep 17 00:00:00 2001 From: Andrea Corbellini Date: Thu, 10 Aug 2023 13:57:44 -0700 Subject: [PATCH] Update the blockchain db whenever a mint with `transferOwnershipTo` is seen When a `MintDescription` with `transferOwnershipTo` is seen, the relevant asset on the chain db is updated to reflect the new owner. --- ironfish/src/assert.ts | 9 + .../__fixtures__/blockchain.test.ts.fixture | 208 ++++++++++++++++++ ironfish/src/blockchain/blockchain.test.ts | 95 ++++++++ ironfish/src/blockchain/blockchain.ts | 25 ++- ironfish/src/testUtilities/fixtures/blocks.ts | 2 + 5 files changed, 335 insertions(+), 4 deletions(-) diff --git a/ironfish/src/assert.ts b/ironfish/src/assert.ts index 602dca9487..c102e37a65 100644 --- a/ironfish/src/assert.ts +++ b/ironfish/src/assert.ts @@ -91,4 +91,13 @@ export class Assert { throw new Error(`We must have forgotten a falsey value: ${String(x)}`) } } + + static bufferEquals(x: Buffer, y: Buffer, message?: string): void { + if (!x.equals(y)) { + // Not including the buffer contents in the message because they can + // potentially be huge (also choosing the proper encoding may be a + // problem) + throw new Error(message || 'Expected buffers to have the same contents') + } + } } diff --git a/ironfish/src/blockchain/__fixtures__/blockchain.test.ts.fixture b/ironfish/src/blockchain/__fixtures__/blockchain.test.ts.fixture index ae431c542f..3554520e09 100644 --- a/ironfish/src/blockchain/__fixtures__/blockchain.test.ts.fixture +++ b/ironfish/src/blockchain/__fixtures__/blockchain.test.ts.fixture @@ -4197,5 +4197,213 @@ } ] } + ], + "Blockchain asset updates with a mint description with transferOwnershipTo changes the ownership of an asset in the database": [ + { + "version": 2, + "id": "19dec1a5-58d4-4fa6-ac65-1db50b1eaed7", + "name": "accountA", + "spendingKey": "b53833b658744385ff8c8682e54c6a210090317fc3bb9a0db109123053f78318", + "viewKey": "cc94f625a5a0ad6683c6ba0708a75decdf2b299c8d7249717502f4daba9914d3b9e8e67ec246e4c5f5bb22fae11579da1b9fcbc3cc40af09e4bcb486f898e5b7", + "incomingViewKey": "17850150a94e75e12a8cd4d8adb4cd260a36a5af8d5fe48f6cb24214cb023200", + "outgoingViewKey": "1fbaf91f34614cad84d14ec6bd9f4e1a7b4913de2ada8f9cc532d093b8e4c633", + "publicAddress": "858e2deed05a37ef0ae4c8968ddd7f73abfb25e90b50674a69d5aa08c4e0f265", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:iLb6jXRaTlO9oAExjmCwTuLk7gajgJVojVgEnLbxWso=" + }, + "sequence": 1 + } + }, + { + "version": 2, + "id": "b6960680-b658-4846-a9c2-d2e6ca46d70c", + "name": "accountB", + "spendingKey": "523586ccc245c93b1ea9eda843c850922b7e9c7bb19ae2b4242e6a44ebee8629", + "viewKey": "278603f5678c194daa408d19dae994ef87df82beaf6eeb5b30a01b6fab11e8858ebdf9bc45a8e3854f85d1e6aeffd382c6f713a840b73b82539a4e33bdc88859", + "incomingViewKey": "3026e116889ce19043e986fe071e4aa85620661552b8567941a23874c7f93904", + "outgoingViewKey": "d610e9da4c2f015a621048e68d857c303b5b5d8f2488c664bc15e5cc9e14d4f9", + "publicAddress": "da1acaf586105a4e5e34968a6afd43b47d90ac29d6b7627da124eed851ef34e8", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:iLb6jXRaTlO9oAExjmCwTuLk7gajgJVojVgEnLbxWso=" + }, + "sequence": 1 + } + }, + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9lHKE8k8DEgKBSUbhnHV9lkEiIdFE+GP+xRcJDYcIyvbXfaYdUZVeMYI3U2hEESHs1a+XZ9xjjYzibLFVl+uAGTeNgp/BONHp9SyTUVMt2jSAkFC+U6KHEGJiZTbolGFFUlv3QkZCFE/i4tyArjG/RexD5GgqK6DIPBG+bNkhEErg/AnfcVhk0REAj0d+Sd4ux0/kQh1LdzDGTaypbLGLGfIuH5hyRQBrAPoXpO/zCwk7cm3gHj+F4Psg3q0498KT2JwMd/kWGFYFXooSFgmtUDFWcGrvtFEMI08Gm7EJsqWgEv0qoGCPPsSd5neb6jalpz6/nt8z9HAwS0uXBFpDpTduyfS4qi6ul9s89rCRaQVHk/PKy5FfzHEhV96HwYc7fIB0l3FzWkU9DM7Ibj/eBHr4LdG1ua+jLenKRA26+zBm9ArT49PAeCC8S33r6Zd4eTd7Zbf9DdBdgdvFpBhBn9m+HfvgybTcPlomgxwB7KW68ioq8Nsuldz05P+GVcC/mro9aZtn3lPnzADd7vQ15ueC+dgLKm/7tZLlFgUvaFYKVF7j35Wj4lSwztqomm94iOwfkSyz1sngbPxUacwPjsUJ2eo8zQRXg2+HkPDE6tLLj9FnyrWs2KVdWl5uAhdaRi27pB2Y54fRg/hd0Fp4QWienl4NPEvcHips/Z5mk8Xkr9ye5oubMPDJjIauevhHpWQOEd+yD/AME+n+GB3saSJCAtabW3rIzQevznfHRIMN0DYpw1+M4uUkFg8v6NvEpcm/xOIfE+io1P6bY06CD4qfKlfAebqxAyALe+UhBacp6kV3vlrtN8CgA6PgHa6wECTY8uKFS9ztzkCmGLHAe0Q0Gbpfd7CVaopK9thyhCu35fw9zkAAuURNIyIUHGA2KtUjYHAyXbKlKItncl3oGvTVgphcB5h+D+JGu/4V3oogUCozSgjs7lM2QQu/RqP6JkfVtYlXD+hSnevrh14uL4NjjiTMyhhY4t7tBaN+8K5MiWjd1/c6v7JekLUGdKadWqCMTg8mVtaW50LWFzc2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKAAAAAAAAAIWOLe7QWjfvCuTIlo3df3Or+yXpC1BnSmnVqgjE4PJlAHTfvEDrdktw1UkDcGvbKpfbkXagehdqE+7qULK+RkLg9yB58dMYe2evxaeAuM90sM5yWoLDDcchumv/6nvcxQn0lz8+H4EgyahgOC1FBDGdSdyYmDTv4qrieFSN+1rZSi5Q/1rgiqGWdKLs/adsM4bMwkU68VEeZtE/oozwI+wN" + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:mS+LUWcEYNdaMCk+sI0mSEmnHvbnSaFqN61KTM7iQgk=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:UY1S2J6cGwFqWd0O/y5Q8VxZwsyI5BWwszS6vGY+mes=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1692991389103, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAI3pgnknys/jcWheSOKkV+V79gsquVI4FE37B5i+45k+jUQ4eVkDugzeXWmdcLsULOXupg0HSs0syw8ccypn9m6gtcD10R7FxVndfbJWEtLunzei6tYobHYUB7A2MJqqFaOKqBglUbs7/gZK+Z3mZz1YbhCejEtmxuQGMFzJZZy0U1ArxwNSJuW7NlGzfJ7T8IK9qku1nUsLF1Ifnq44CwjLVZVvXiBTUvRVItMV3hSC5FJXp6nBI1mUYqjXg/vendUKoaSle7/9w8SG16lAEXLFF00XJ3Lgz9rUNlD0oT8k/7Q8oabFuzqwFtf0H9PJs3xK2sXAh2lBblbCyseE2vcjuudQyqRbLG/5UPf00RF00q0CJiT9Q9lBjeIprTwBOKNPytiexCB0iE/8nGn7Lq7D3+j01XJD5BraiJMVghtRJ2hSNrE4zalKG4kxE8HfCPOB0vT5hPpoSBT8BFkl40LzcTWyKsYbp1rgJnI4xZu6mJLGoZCdNhYFRG49wpV4A5cygdruIqmTeLY23Uq9vZ3mEt/fgPO6I/Ypp3ECKQiOo9CmLU9lx/16W5sM2SZPfN17pE3idgPmvDKvM/KwAjTEk6iuyxHpmaTCS7ngRNTB2SFfIi6Jnw0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAws46aqJnPBFul4x8fTe/g2c15BYpGOnB1iGbsSdIhCQAlazURVJyPGF/gnXgIpWQ3UFUkWm0lsL1DLAvRAXrLCA==" + }, + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9lHKE8k8DEgKBSUbhnHV9lkEiIdFE+GP+xRcJDYcIyvbXfaYdUZVeMYI3U2hEESHs1a+XZ9xjjYzibLFVl+uAGTeNgp/BONHp9SyTUVMt2jSAkFC+U6KHEGJiZTbolGFFUlv3QkZCFE/i4tyArjG/RexD5GgqK6DIPBG+bNkhEErg/AnfcVhk0REAj0d+Sd4ux0/kQh1LdzDGTaypbLGLGfIuH5hyRQBrAPoXpO/zCwk7cm3gHj+F4Psg3q0498KT2JwMd/kWGFYFXooSFgmtUDFWcGrvtFEMI08Gm7EJsqWgEv0qoGCPPsSd5neb6jalpz6/nt8z9HAwS0uXBFpDpTduyfS4qi6ul9s89rCRaQVHk/PKy5FfzHEhV96HwYc7fIB0l3FzWkU9DM7Ibj/eBHr4LdG1ua+jLenKRA26+zBm9ArT49PAeCC8S33r6Zd4eTd7Zbf9DdBdgdvFpBhBn9m+HfvgybTcPlomgxwB7KW68ioq8Nsuldz05P+GVcC/mro9aZtn3lPnzADd7vQ15ueC+dgLKm/7tZLlFgUvaFYKVF7j35Wj4lSwztqomm94iOwfkSyz1sngbPxUacwPjsUJ2eo8zQRXg2+HkPDE6tLLj9FnyrWs2KVdWl5uAhdaRi27pB2Y54fRg/hd0Fp4QWienl4NPEvcHips/Z5mk8Xkr9ye5oubMPDJjIauevhHpWQOEd+yD/AME+n+GB3saSJCAtabW3rIzQevznfHRIMN0DYpw1+M4uUkFg8v6NvEpcm/xOIfE+io1P6bY06CD4qfKlfAebqxAyALe+UhBacp6kV3vlrtN8CgA6PgHa6wECTY8uKFS9ztzkCmGLHAe0Q0Gbpfd7CVaopK9thyhCu35fw9zkAAuURNIyIUHGA2KtUjYHAyXbKlKItncl3oGvTVgphcB5h+D+JGu/4V3oogUCozSgjs7lM2QQu/RqP6JkfVtYlXD+hSnevrh14uL4NjjiTMyhhY4t7tBaN+8K5MiWjd1/c6v7JekLUGdKadWqCMTg8mVtaW50LWFzc2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKAAAAAAAAAIWOLe7QWjfvCuTIlo3df3Or+yXpC1BnSmnVqgjE4PJlAHTfvEDrdktw1UkDcGvbKpfbkXagehdqE+7qULK+RkLg9yB58dMYe2evxaeAuM90sM5yWoLDDcchumv/6nvcxQn0lz8+H4EgyahgOC1FBDGdSdyYmDTv4qrieFSN+1rZSi5Q/1rgiqGWdKLs/adsM4bMwkU68VEeZtE/oozwI+wN" + } + ] + }, + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1jPVrp0wf05U5jJ6l2fqqKDqAaZye3SQ69Ey4LK6mgK5e8sWPx7yOAN142LcSqI3DMbJF7wFKhowJi0sy3FMFSeneLughsnEgu2kY1Fd5/+S7+IifAG8V6K79jOKPQL4sxC1f384YYiAf9FdA0hqHwRrqnvZBXTvoYvu9QpPxrUCn4p3FWBe35pSofd/D0yV94y3Qbv0yaSPiloSKjk1ekkHmrZcJyQ6rSq2YhnWW96zMYCdmc4m7n/0zH3NWQrrdpt/sPJ/RITnnRWAHGrCiC8L4AvdTq1oOQv0adPYBt26RW8O4zh5NBM3lj45VkOdsi8182aeM+zI002DWglxrYdTPYOVt6UGIaRbjQ8E9NIDXldIKaOIWcQrX7gcObYBBboGCK50pF4xIikyjALBuyoMuWOD/L/AJdTaBuWw+oxtuuAsNBGcZJ0nIo00p4GoDt94YnvAGyE8dYg/rZrRwZ5y/egIEB/FxhZWDd/9KWNziPwaDyjPvkOJZsgWZzwp00xIuZ2kMvUH9NuNGHWbKyBi9E/9NgichtOdNYITUYQtePnbp67sh1p8zyY1xcTXqN/crzOVmtoHa4D43Nk9dMrf3DQy05oubpY85WDzEE5+1Qazf9A2PLqVlJ0vfmEJE3oTvlwtRg/1D5T0lHwLVPpGGLoVPrxG6i9y0QwmV5LezCulIThBxRG0NiA1+zrOf+KImMwQ9jVMunRoajph/5zm5+m5Io1pqgmnyn1Z8GRgTcKM5XtBegaMpAQP77KSOb90ReKwxdVUvT5Z2Dn9oSpjeL/koeNJuaIWS0e1NxehFvfVHWEEWOhZk4U8RFnAjXfhUzmUYyQmLbeLPezZGy8pMZbdhElZFSuPJxU+n3GiB4C9mG8tn+GiPQ/Z93rKPmQKgkAFTftZr3SPWzRgEEmRO1lvWIl6iANK8pkvf++D+8ScY9tK7qKy3e+3QiH9GxFVXj6BvVeJRS8/9gcu/6+xgKs1kDPohY4t7tBaN+8K5MiWjd1/c6v7JekLUGdKadWqCMTg8mVtaW50LWFzc2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUAAAAAAAAAIWOLe7QWjfvCuTIlo3df3Or+yXpC1BnSmnVqgjE4PJlAdoayvWGEFpOXjSWimr9Q7R9kKwp1rdifaEk7thR7zTob5wz+YCAjfgCP4prfTOPk3WYcIWolvz2Rjmv+8UavB2ufzzi1aUFXQ3dwkpq9Jk6oYVfrF+EX3qfLgpBgspuBYTORhbW4oVQPlsP9jQUf0lQMeNwpySbqLkURgrqzH1qLEBTU+/pvTA0dxsYTc3/ytIn2Q9fnBL3ABJw4vIBAgk=" + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "FBB51D0F98066C20F2FEFBC186AD3B43EA53F0968195030870154D9F55F71F9D", + "noteCommitment": { + "type": "Buffer", + "data": "base64:PeKKpv7oIJXdD5q2ExEW4qhw/9a+5kjwqpoBUtlzpGo=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:U4rHhxQBoisAW5zJxr5ZlRHgQQXJH0Z+0F0sPcMDOmw=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1692991504643, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAgAW/Ml8MLxSay1Iv+FER6lCu7zWEjztVSwBvaAKkuwSviHau2I7+TQqSSoVTwkMDJ/S/9PdGXcMexW7c3DtC6kETSvR/CDWXeaGz+YbMr+OsSoAz+sog6fVHpreX0t3z0jfguajl07aMu8FdMSu2+/KU1QEclX3PgLLWuFyl8SIO1U/DMW789FKV5pLn2dH8iEU+fczDopbGTPaJOG+xDu7vBP7gL2u5yItn0T4kC/mxu2kOlPDgOXIJK4QTWIA4Yml4w8Zd6wcROqcOFzpsCfso3Csvc9klvFBn3lxXJ9QG7e3UIo1GtwgHRE1HplEdG/NojeWewUxD4ox4KmFwL0IEnui1bSIr0stPmqjeEiRbE7M9zguZ5TlB6vsOuM8YzktA6hj0VW90S2uTujKOMIUxJRJMXjsh70b9pVHoUkYCCQs0SFKyErfgoXtBmWde/4+cmB+d+PifYfJhqXyvqLPYF+KzsYgAb5fZ3iqtwrO738PDoGeVlVvwTTJeZ3xFXJCevHNSRhbS3eUylhCn6sCo0HPTRN1D8ZBCXHKmCeHD2QKfcQ+ObmOWFX4GkaL9DsiE9jEYXTvO4G6VnRhD5W8I1iRTQPX6rXpn2LtI7147Oz//RIfAbElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwwVj/LyTMuiBhQ+N6TuZOuIsNMS7OqGnnKhtXNlU2ozj73GIx6nXTfroYmNAFS///LIirMNCoNioDb0hGabPPBA==" + }, + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1jPVrp0wf05U5jJ6l2fqqKDqAaZye3SQ69Ey4LK6mgK5e8sWPx7yOAN142LcSqI3DMbJF7wFKhowJi0sy3FMFSeneLughsnEgu2kY1Fd5/+S7+IifAG8V6K79jOKPQL4sxC1f384YYiAf9FdA0hqHwRrqnvZBXTvoYvu9QpPxrUCn4p3FWBe35pSofd/D0yV94y3Qbv0yaSPiloSKjk1ekkHmrZcJyQ6rSq2YhnWW96zMYCdmc4m7n/0zH3NWQrrdpt/sPJ/RITnnRWAHGrCiC8L4AvdTq1oOQv0adPYBt26RW8O4zh5NBM3lj45VkOdsi8182aeM+zI002DWglxrYdTPYOVt6UGIaRbjQ8E9NIDXldIKaOIWcQrX7gcObYBBboGCK50pF4xIikyjALBuyoMuWOD/L/AJdTaBuWw+oxtuuAsNBGcZJ0nIo00p4GoDt94YnvAGyE8dYg/rZrRwZ5y/egIEB/FxhZWDd/9KWNziPwaDyjPvkOJZsgWZzwp00xIuZ2kMvUH9NuNGHWbKyBi9E/9NgichtOdNYITUYQtePnbp67sh1p8zyY1xcTXqN/crzOVmtoHa4D43Nk9dMrf3DQy05oubpY85WDzEE5+1Qazf9A2PLqVlJ0vfmEJE3oTvlwtRg/1D5T0lHwLVPpGGLoVPrxG6i9y0QwmV5LezCulIThBxRG0NiA1+zrOf+KImMwQ9jVMunRoajph/5zm5+m5Io1pqgmnyn1Z8GRgTcKM5XtBegaMpAQP77KSOb90ReKwxdVUvT5Z2Dn9oSpjeL/koeNJuaIWS0e1NxehFvfVHWEEWOhZk4U8RFnAjXfhUzmUYyQmLbeLPezZGy8pMZbdhElZFSuPJxU+n3GiB4C9mG8tn+GiPQ/Z93rKPmQKgkAFTftZr3SPWzRgEEmRO1lvWIl6iANK8pkvf++D+8ScY9tK7qKy3e+3QiH9GxFVXj6BvVeJRS8/9gcu/6+xgKs1kDPohY4t7tBaN+8K5MiWjd1/c6v7JekLUGdKadWqCMTg8mVtaW50LWFzc2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUAAAAAAAAAIWOLe7QWjfvCuTIlo3df3Or+yXpC1BnSmnVqgjE4PJlAdoayvWGEFpOXjSWimr9Q7R9kKwp1rdifaEk7thR7zTob5wz+YCAjfgCP4prfTOPk3WYcIWolvz2Rjmv+8UavB2ufzzi1aUFXQ3dwkpq9Jk6oYVfrF+EX3qfLgpBgspuBYTORhbW4oVQPlsP9jQUf0lQMeNwpySbqLkURgrqzH1qLEBTU+/pvTA0dxsYTc3/ytIn2Q9fnBL3ABJw4vIBAgk=" + } + ] + } + ], + "Blockchain asset updates when a subsequent mint gets rolled back should restore the original owner": [ + { + "version": 2, + "id": "1c0ac506-2cad-45b6-a779-e736de16690e", + "name": "accountA", + "spendingKey": "933f93a1f2d8177b162382b8add9b6cc070b824868b21d5428e8555e9abe00ac", + "viewKey": "fe54e704c89f9d5dccb6692eac9aaac525774f417647cd24f57df4082dc2475a63c5bfc4ef1d1658b10341fff2b87536adeb57e1130c9c234e7bedaca1e3e13a", + "incomingViewKey": "d2bb7e93b26ea7682679af29684a7c03555d09b4e5ebf0e2339ef7cc1f2ade05", + "outgoingViewKey": "283949c66cb08a2215b866a6878f9ba727f70c29f1467b4b2dc27fd7d9151575", + "publicAddress": "dd433d1365f65078030f4d535dcd5bf7d669382f2915cbe1260528c4ecdd0b05", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:iLb6jXRaTlO9oAExjmCwTuLk7gajgJVojVgEnLbxWso=" + }, + "sequence": 1 + } + }, + { + "version": 2, + "id": "5faa026e-102b-4ae0-97e9-056a7fb9b29c", + "name": "accountB", + "spendingKey": "6952e4d26ec03ef71a0b5de0effda4dc697de6e1e5c3e8a5be7c6e9066fb492c", + "viewKey": "3fc8eecb1b0adc0c4de976f7fd4504335ecd473ee059a84da2f3a238c295b7dce9176ad4f5530f5f3a9ca43fed57ef28442309cf9358e481f8f64cde90962bd7", + "incomingViewKey": "bd3764e32e9844631e9fc10267e502ec3dcf2c9652371221eb1a0b5df2011b02", + "outgoingViewKey": "603e4b182cc516003f91c2a7ee517cf8c034139d909fb743adf3af28a0c98b7b", + "publicAddress": "96601b8c7e9504fae57664f80fc2df11aedec3e7f27a29a12bd383e09a192bc8", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:iLb6jXRaTlO9oAExjmCwTuLk7gajgJVojVgEnLbxWso=" + }, + "sequence": 1 + } + }, + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJz3MGATn9SckjNZfLG3L+kqg3qas7lB+YSd+YgtY0FO27qZwAstobE1BmxlWnDNjOaXIxpne6cwhnJ8SbKIPfCpVPBLN08M2xxbLkK8sX7eSi1RrK6EIy+Hw5GyeunKGz4mcxYbU4tRu24BhcgZC1Z4YPx8OcHNOefm8Ly3OdtAPUR/fwSTMFnVPCmlA8Ah6cnqllqmRNcX8A5C1Vs7/D1TEnWypgIrbmJbY8TPV7jaodwtD3rYuuoinEK/wC3IDnfe7VslF/RypPBxPczjYj19WRJCUFPx7dmmf1nYAY8mR+AQLPHkTyzNByq6kUtGlb3DcXGtxWf9cKaBxPHt+xHffdR4yJ8LLJkftGYe1TJGGzE/3ZbGofsvsdGpeVn0UCtNA6W4TnwCF3sjuIBCD+hhWMcUiz+a9nvWOif5Pyi30WmKVY9QHEYAxG9dn7EdLnA1XOMCAQ78PXwicrTG8SUn0xxJmkKOZ46qf+t4+DtwKzYweYsSBLWcF152YMBpMQYTS116joB6GPMmZ0Ct2XoQRPOqtjjrbUX3ycVA1vsJkFB93XqPGQAzWDJntNnIGut8cv5QSBJtfIe0SPC6Vr2MCbpf0Cchr8F/83V2S5O6cy+WkrB+zE+ZJcDBowx6IbwHSXgQ7tad7uBN6+Gzar5nY6xQ++5P+S6vuSJgGQ/DGcnc6QGMvyzvt8edDgrAxbl5Ozty682nK5ZSJXO+HPq9bCfVVxopxrLpoJrNiljoEAmztAisZG/Cdzzdbdy8+U7+XK03+lgG56mBeyu7u0nQFmeyHiJztl3qfDRAc4WsrgSiYGmUaZElqshtbjCZ5/gqETiMohrJLT+2yNcECJEGaQpDRfr34BqxRoPOfgS5CNId9mHkpsHtENzfAazVNq+0W61lpIcOc2KKjasa1DmsHXX4LBhBrtK1WxmGrxyeKTSdbGJlOtp4el6PJBxkAjfvraCqJFi3rgXOW/BVxnRmYP22x0IyH3UM9E2X2UHgDD01TXc1b99ZpOC8pFcvhJgUoxOzdCwVtaW50LWFzc2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUKAAAAAAAAAN1DPRNl9lB4Aw9NU13NW/fWaTgvKRXL4SYFKMTs3QsFABoByYyafxKsSeDasfZerN0Fijbod45ZgV6FGj3OuEIS/XxAHk+SCLKuYPy+7zFZA6aNUNRwLbO5FED6dCZlcQxgj8pJWqUnxx5mu/V02id8eB5Ob8Vhz2BsjqEWgRRPZs8g4oVTTPTLmUWeSHlbBOKX43PPmtVs6G8Ca678GC0H" + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:wLLqhCFDCtPPWj30iyBj531iW4mNEi0IjV1NjmYrmjI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:ifgFtNNj+mMHKrjeBqLVmKVBDssGX2MNdapo1k4MfmE=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1692991394463, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAJy31E6M6sJJIN+fJsrusNVeV/IaIM1r0xF2rDo694ya1lxJa1nBI4QTJ1Depuln4Cn6lpLhPIDJX4AO6Fl8K/WlgKgO4mLb8dqN/xudshiGrBFuzORuMZmotienz+bbA+4i5WnvFzSx+g1DLTYXoazfFJVHePHUphE4nrIr6OQ4MQWBN4ZAggJzLx+mNYeIlIsm2clowMIopjypwnrVmYBrwem3oHY8/IbCaRNQAT3CtGZyOqKeA9zT94dVis83PDCUDoUtjQ9YlAn1RFZaPH3a94sR5hg5I1HaeiBFwcbQuoSmKI3ykwviJh6N+ivzr7cboyd37Y9xJbvQ1Hq0P0x3MN5RyvWmqMcxWd6e6S7D2ONbBK2b8BvVVYycgLaFf+266pXCW/IEiDMBXw/3J8bcBoWRXGmPKCdJyUISi3fFzH4Rhla5twrpKrmJkGBMZh5p24h9Ptbl49Z+xGKUuZItuWySWtWv/uc7a3ft1yKK9wgDySkKurSROWsLk0EByLZVhWdXj56NbnFoKa768+WE1aa7dAtRpYODCfODlQcOGNiZVmWaAz7Rj6zXziaw20bsbkwQDsR+pkZCSm5+nibF52Kj6CBCLpqVOxbzvuJpk8SuWAw9lpklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwkW2ywR/BYHTjzzMlsZhkW5sB/UciB3jXNkJ/C3D+LwXlb9pATa59oPPvZW7dpRA8Oy/xCeI/Ry3TwD4mDhk/AQ==" + }, + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJz3MGATn9SckjNZfLG3L+kqg3qas7lB+YSd+YgtY0FO27qZwAstobE1BmxlWnDNjOaXIxpne6cwhnJ8SbKIPfCpVPBLN08M2xxbLkK8sX7eSi1RrK6EIy+Hw5GyeunKGz4mcxYbU4tRu24BhcgZC1Z4YPx8OcHNOefm8Ly3OdtAPUR/fwSTMFnVPCmlA8Ah6cnqllqmRNcX8A5C1Vs7/D1TEnWypgIrbmJbY8TPV7jaodwtD3rYuuoinEK/wC3IDnfe7VslF/RypPBxPczjYj19WRJCUFPx7dmmf1nYAY8mR+AQLPHkTyzNByq6kUtGlb3DcXGtxWf9cKaBxPHt+xHffdR4yJ8LLJkftGYe1TJGGzE/3ZbGofsvsdGpeVn0UCtNA6W4TnwCF3sjuIBCD+hhWMcUiz+a9nvWOif5Pyi30WmKVY9QHEYAxG9dn7EdLnA1XOMCAQ78PXwicrTG8SUn0xxJmkKOZ46qf+t4+DtwKzYweYsSBLWcF152YMBpMQYTS116joB6GPMmZ0Ct2XoQRPOqtjjrbUX3ycVA1vsJkFB93XqPGQAzWDJntNnIGut8cv5QSBJtfIe0SPC6Vr2MCbpf0Cchr8F/83V2S5O6cy+WkrB+zE+ZJcDBowx6IbwHSXgQ7tad7uBN6+Gzar5nY6xQ++5P+S6vuSJgGQ/DGcnc6QGMvyzvt8edDgrAxbl5Ozty682nK5ZSJXO+HPq9bCfVVxopxrLpoJrNiljoEAmztAisZG/Cdzzdbdy8+U7+XK03+lgG56mBeyu7u0nQFmeyHiJztl3qfDRAc4WsrgSiYGmUaZElqshtbjCZ5/gqETiMohrJLT+2yNcECJEGaQpDRfr34BqxRoPOfgS5CNId9mHkpsHtENzfAazVNq+0W61lpIcOc2KKjasa1DmsHXX4LBhBrtK1WxmGrxyeKTSdbGJlOtp4el6PJBxkAjfvraCqJFi3rgXOW/BVxnRmYP22x0IyH3UM9E2X2UHgDD01TXc1b99ZpOC8pFcvhJgUoxOzdCwVtaW50LWFzc2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUKAAAAAAAAAN1DPRNl9lB4Aw9NU13NW/fWaTgvKRXL4SYFKMTs3QsFABoByYyafxKsSeDasfZerN0Fijbod45ZgV6FGj3OuEIS/XxAHk+SCLKuYPy+7zFZA6aNUNRwLbO5FED6dCZlcQxgj8pJWqUnxx5mu/V02id8eB5Ob8Vhz2BsjqEWgRRPZs8g4oVTTPTLmUWeSHlbBOKX43PPmtVs6G8Ca678GC0H" + } + ] + }, + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZkZWzT+5jpJycD1sXnrRUJ7iuSK0HsVpWQw1Dm4aVwWjFJbz09yYAPZRmmKSIKK5QFwxYFLOPsJv7JZ86ki44GyjeDvRFZQOwWaFQjSmK+iBedIcwojVz9QE5VTrL+521TghwpUFKk8DR9PaoygXq0rsTzx5xHeO+pCeEvYLbf8C+AYx8ekBhuGTLU+n/uIBruc1BWUAj3E5E+loPowl4x0NsOiJ5DKZnIvjNEHhmP+QWWD58SDSKJjAGIc0Aw18lVht9RY1QJ4xwBXghlXZfmPA3Eb9D9Xq0nOrEZIebMRKBwJBnQq8tTdiuKRxFrXsTERiW9QN9Dy6rvLZnyBq1i19GJ7XZp+xB5QuDAi6aPVomInRnS1ZK4kX6YuNs2cPxEwBHc7IBSHimJXDAVzj8tUJudyj6ebjuw6IeMocgpo7tZoN1OfZrD2uOerHg1ygc2S40UoZmLj4AEa9RnPznWj5px9tqPDbuVbOAGMukTmaissqm9b3GSU/xKstgrJhaiQN6U9ciuAstLE0bYyEPsecBsUZ8TU5KfaDe9W5p7mn5qwJ7qvAka2Prma70wEqazdJmt9LYaK2CDUS49FEo+qtvxK+t96sz28w5pKQByJCxpvqc/kpULa71S9Vja2rM6u4wwdO/i8wS1Xr5nemZk32Cra/wVMHBCFs3jRRcBAgCRUdGlbZawFbJhHHWWKy/rge1Syn3CPbAsHZujyE7CQinmierODDuYjuxklFRurH35ECHze7qFppci6LyB8ngEJ8twJ3z9CjsCsWk4IT8vGpQQ7QK/H1lusczTV/4ETm3Z1b50wDTzJ3tnLLNudK7C5jBULyFtO7KI1oB4OZzJEKISeEL+Q1Cb1cz6HHEIQwTuYaVFiOi9WWqyi9ewf/iuG77IzDQnTQNtY6BC1QEQXQHVQnPr8ytlXMl+sHPCVTxrJuFW0RzTZjvw20nHhmT3aw+UWAEN6rT5FMe+FVBVi+7jp/J3Mt3UM9E2X2UHgDD01TXc1b99ZpOC8pFcvhJgUoxOzdCwVtaW50LWFzc2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUUAAAAAAAAAN1DPRNl9lB4Aw9NU13NW/fWaTgvKRXL4SYFKMTs3QsFAZZgG4x+lQT65XZk+A/C3xGu3sPn8nopoSvTg+CaGSvIstGik0XIMB1d5cvWNCAjN79fsig4GWZut6QI+v27KNU9Zv6vZ7WTF/NZJ9/KGvEbmzcXkoTX4tCECbTcH8B/A7EudYEhZsaOveb78whfEKHtfWiCjyl7uEcKIreY+8vt2K0sKv0uHkCL0eOvPBOUxbEm5WWdqRUg10bOZvgJMgw=" + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "40DEDF5A9FDAE5D5EE43D028C6F1DB9CB5E8B4E8EA526F98C63DA4ADB4D2B133", + "noteCommitment": { + "type": "Buffer", + "data": "base64://nEN+DsOhgcLbB3+VZSdx9xjEW5qTKZNxvKMCNdDwM=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:a69p5FFfD2/Z01JUFclLphSo1JLPoX9cwhJWvUimoQ4=" + }, + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", + "randomness": "0", + "timestamp": 1692991395731, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAKSIUlmmSVXqsTQhvDkH6PdtYXzcz+x/6970jOf8kbG65yzrZQW4AFdq2lHqne1G9I5agnB5T4t6/ByxTbVmcL+CV2+TPEnDQHI7pQVpO6FKlJMv2Bd/0gWINfL4r8n5bPd2aT9r74veMoyHWshdoKRAhaQXcEme9ZEwWKJG1UokIgzPVWec10j580tY9WCoTHzw3oKa7NGR4nH2GK6p0SHsaFneuSS8h16gsYZJN11eh+2jJLrq/UpA5Baqbbmi7NDAHMwgK70SVRrbei0l2JoYihPQbh9qYKkJ56Qpc1lrwbG8Akj79+W09sZESXQDoClY4jMLdU1RXEboEERzr8kqJRzpFYfL/UvCrxoCHFJCMFpNaktiToiLbrfD9xvwDfqOCaTc6hwOxII2L72TaeNcE9OaXkQckumA/8Ic03jxVI8ys6GC8ACK19yRQy//Wvm08x7+KklFb7FNsc3W66I1tU8o+/GZSkd98YvUHNZSJ9tUApBPAvR07Qy8LI1++wXcTTGHl7CbWUBrWVwfRNQ76byyk1qPypJ9w2QNST9/8QPDqmgz5SE5MGd/etKDjEBtp3VCWT8EgZydH5Xfss0qXHzXNPdivNCZQAlrFo/ByXwSbzRFGF0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwltsBxe49oPmA9uVV690opbaRDgPM8Mnz304PXpnD43BdhdktfhOuhdL7f/U2tGLlvLK3mPD9PT2kwqZa0F8rCA==" + }, + { + "type": "Buffer", + "data": "base64:AgAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZkZWzT+5jpJycD1sXnrRUJ7iuSK0HsVpWQw1Dm4aVwWjFJbz09yYAPZRmmKSIKK5QFwxYFLOPsJv7JZ86ki44GyjeDvRFZQOwWaFQjSmK+iBedIcwojVz9QE5VTrL+521TghwpUFKk8DR9PaoygXq0rsTzx5xHeO+pCeEvYLbf8C+AYx8ekBhuGTLU+n/uIBruc1BWUAj3E5E+loPowl4x0NsOiJ5DKZnIvjNEHhmP+QWWD58SDSKJjAGIc0Aw18lVht9RY1QJ4xwBXghlXZfmPA3Eb9D9Xq0nOrEZIebMRKBwJBnQq8tTdiuKRxFrXsTERiW9QN9Dy6rvLZnyBq1i19GJ7XZp+xB5QuDAi6aPVomInRnS1ZK4kX6YuNs2cPxEwBHc7IBSHimJXDAVzj8tUJudyj6ebjuw6IeMocgpo7tZoN1OfZrD2uOerHg1ygc2S40UoZmLj4AEa9RnPznWj5px9tqPDbuVbOAGMukTmaissqm9b3GSU/xKstgrJhaiQN6U9ciuAstLE0bYyEPsecBsUZ8TU5KfaDe9W5p7mn5qwJ7qvAka2Prma70wEqazdJmt9LYaK2CDUS49FEo+qtvxK+t96sz28w5pKQByJCxpvqc/kpULa71S9Vja2rM6u4wwdO/i8wS1Xr5nemZk32Cra/wVMHBCFs3jRRcBAgCRUdGlbZawFbJhHHWWKy/rge1Syn3CPbAsHZujyE7CQinmierODDuYjuxklFRurH35ECHze7qFppci6LyB8ngEJ8twJ3z9CjsCsWk4IT8vGpQQ7QK/H1lusczTV/4ETm3Z1b50wDTzJ3tnLLNudK7C5jBULyFtO7KI1oB4OZzJEKISeEL+Q1Cb1cz6HHEIQwTuYaVFiOi9WWqyi9ewf/iuG77IzDQnTQNtY6BC1QEQXQHVQnPr8ytlXMl+sHPCVTxrJuFW0RzTZjvw20nHhmT3aw+UWAEN6rT5FMe+FVBVi+7jp/J3Mt3UM9E2X2UHgDD01TXc1b99ZpOC8pFcvhJgUoxOzdCwVtaW50LWFzc2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUUAAAAAAAAAN1DPRNl9lB4Aw9NU13NW/fWaTgvKRXL4SYFKMTs3QsFAZZgG4x+lQT65XZk+A/C3xGu3sPn8nopoSvTg+CaGSvIstGik0XIMB1d5cvWNCAjN79fsig4GWZut6QI+v27KNU9Zv6vZ7WTF/NZJ9/KGvEbmzcXkoTX4tCECbTcH8B/A7EudYEhZsaOveb78whfEKHtfWiCjyl7uEcKIreY+8vt2K0sKv0uHkCL0eOvPBOUxbEm5WWdqRUg10bOZvgJMgw=" + } + ] + } ] } \ No newline at end of file diff --git a/ironfish/src/blockchain/blockchain.test.ts b/ironfish/src/blockchain/blockchain.test.ts index 464ae46c7f..c9b14b8106 100644 --- a/ironfish/src/blockchain/blockchain.test.ts +++ b/ironfish/src/blockchain/blockchain.test.ts @@ -1148,6 +1148,60 @@ describe('Blockchain', () => { supply: 10n, }) }) + + describe('with transferOwnershipTo', () => { + it('changes the ownership of an asset in the database', async () => { + const { node: nodeA } = await nodeTest.createSetup() + const { node: nodeB } = await nodeTest.createSetup() + const accountA = await useAccountFixture(nodeA.wallet, 'accountA') + const accountB = await useAccountFixture(nodeB.wallet, 'accountB') + expect(accountA.publicAddress).not.toEqual(accountB.publicAddress) + + const asset = new Asset(accountA.publicAddress, 'mint-asset', 'metadata') + const assetId = asset.id() + + // Mint an asset from accountA. Initially, both the owner and creator + // should be set to accountA + + const block1 = await useMintBlockFixture({ + node: nodeA, + account: accountA, + asset, + value: 10n, + }) + await expect(nodeA.chain).toAddBlock(block1) + await expect(nodeB.chain).toAddBlock(block1) + + const mintedAsset1 = await nodeA.chain.getAssetById(assetId) + expect(mintedAsset1).toEqual(await nodeB.chain.getAssetById(assetId)) + expect(mintedAsset1).toMatchObject({ + creator: Buffer.from(accountA.publicAddress, 'hex'), + owner: Buffer.from(accountA.publicAddress, 'hex'), + supply: 10n, + }) + + // Now change the ownership of the asset from accountA to accountB. + // Creator should stay the same, but owner should change + + const block2 = await useMintBlockFixture({ + node: nodeA, + account: accountA, + asset, + value: 20n, + transferOwnershipTo: accountB.publicAddress, + }) + await expect(nodeA.chain).toAddBlock(block2) + await expect(nodeB.chain).toAddBlock(block2) + + const mintedAsset2 = await nodeA.chain.getAssetById(assetId) + expect(mintedAsset2).toEqual(await nodeB.chain.getAssetById(assetId)) + expect(mintedAsset2).toMatchObject({ + creator: Buffer.from(accountA.publicAddress, 'hex'), + owner: Buffer.from(accountB.publicAddress, 'hex'), + supply: 30n, + }) + }) + }) }) describe('with a burn description', () => { @@ -1289,6 +1343,47 @@ describe('Blockchain', () => { supply: mintValueA, }) }) + + it('should restore the original owner', async () => { + const { node: nodeA } = await nodeTest.createSetup() + const { node: nodeB } = await nodeTest.createSetup() + const accountA = await useAccountFixture(nodeA.wallet, 'accountA') + const accountB = await useAccountFixture(nodeB.wallet, 'accountB') + expect(accountA.publicAddress).not.toEqual(accountB.publicAddress) + + const asset = new Asset(accountA.publicAddress, 'mint-asset', 'metadata') + const assetId = asset.id() + + const block1 = await useMintBlockFixture({ + node: nodeA, + account: accountA, + asset, + value: 10n, + }) + await expect(nodeA.chain).toAddBlock(block1) + await expect(nodeB.chain).toAddBlock(block1) + + const block2 = await useMintBlockFixture({ + node: nodeA, + account: accountA, + asset, + value: 20n, + transferOwnershipTo: accountB.publicAddress, + }) + await expect(nodeA.chain).toAddBlock(block2) + await expect(nodeB.chain).toAddBlock(block2) + + await nodeA.chain.removeBlock(block2.header.hash) + await nodeB.chain.removeBlock(block2.header.hash) + + const mintedAsset = await nodeA.chain.getAssetById(assetId) + expect(mintedAsset).toEqual(await nodeB.chain.getAssetById(assetId)) + expect(mintedAsset).toMatchObject({ + creator: Buffer.from(accountA.publicAddress, 'hex'), + owner: Buffer.from(accountA.publicAddress, 'hex'), + supply: 10n, + }) + }) }) describe('when a burn gets rolled back', () => { diff --git a/ironfish/src/blockchain/blockchain.ts b/ironfish/src/blockchain/blockchain.ts index 9b16239b04..ba845ddb0e 100644 --- a/ironfish/src/blockchain/blockchain.ts +++ b/ironfish/src/blockchain/blockchain.ts @@ -1303,17 +1303,30 @@ export class Blockchain { transaction: Transaction, tx: IDatabaseTransaction, ): Promise { - for (const { asset, value } of transaction.mints) { + for (const { + asset, + value, + owner: currentOwner, + transferOwnershipTo, + } of transaction.mints) { const assetId = asset.id() const existingAsset = await this.blockchainDb.getAsset(assetId, tx) let createdTransactionHash = transaction.hash() let supply = BigInt(0) + if (existingAsset) { createdTransactionHash = existingAsset.createdTransactionHash supply = existingAsset.supply + Assert.bufferEquals( + existingAsset.owner, + currentOwner, + 'Stored owner does not match owner on the transaction', + ) } + const updatedOwner = transferOwnershipTo || currentOwner + await this.blockchainDb.putAsset( assetId, { @@ -1323,7 +1336,7 @@ export class Blockchain { name: asset.name(), nonce: asset.nonce(), creator: asset.creator(), - owner: asset.creator(), + owner: updatedOwner, supply: supply + value, }, tx, @@ -1392,10 +1405,14 @@ export class Blockchain { transaction: Transaction, tx: IDatabaseTransaction, ): Promise { - for (const { asset, value } of transaction.mints.slice().reverse()) { + for (const { asset, value, owner: originalOwner, transferOwnershipTo } of transaction.mints + .slice() + .reverse()) { const assetId = asset.id() + const updatedOwner = transferOwnershipTo || originalOwner const existingAsset = await this.blockchainDb.getAsset(assetId, tx) Assert.isNotUndefined(existingAsset) + Assert.bufferEquals(existingAsset.owner, updatedOwner) const existingSupply = existingAsset.supply const supply = existingSupply - value @@ -1418,7 +1435,7 @@ export class Blockchain { name: asset.name(), nonce: asset.nonce(), creator: asset.creator(), - owner: asset.creator(), + owner: originalOwner, supply, }, tx, diff --git a/ironfish/src/testUtilities/fixtures/blocks.ts b/ironfish/src/testUtilities/fixtures/blocks.ts index 2da4b68b1d..e16e2d315f 100644 --- a/ironfish/src/testUtilities/fixtures/blocks.ts +++ b/ironfish/src/testUtilities/fixtures/blocks.ts @@ -95,6 +95,7 @@ export async function useMintBlockFixture(options: { account: Account asset: Asset value: bigint + transferOwnershipTo?: string sequence?: number }): Promise { if (!options.sequence) { @@ -111,6 +112,7 @@ export async function useMintBlockFixture(options: { name: options.asset.name().toString('utf8'), metadata: options.asset.metadata().toString('utf8'), value: options.value, + transferOwnershipTo: options.transferOwnershipTo, }, ], })