diff --git a/.rustfmt.toml b/.rustfmt.toml index 24876acd9..9fd9af831 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -6,7 +6,7 @@ ## # rustup run nightly -- rustfmt node/src/main.rs -# max_width = 100 +# max_width = 180 # hard_tabs = false # tab_spaces = 4 # newline_style = "Auto" diff --git a/Cargo.lock b/Cargo.lock index 4a50f8a12..add1b713d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5273,6 +5273,7 @@ dependencies = [ "serde", "serde-tuple-vec-map", "serde_bytes", + "serde_json", "serde_with", "sp-core", "sp-io", diff --git a/docs/delegate-info.json b/docs/delegate-info.json new file mode 100644 index 000000000..bda41b48e --- /dev/null +++ b/docs/delegate-info.json @@ -0,0 +1,394 @@ +[ + { + "address": "5ECvRLMj9jkbdM4sLuH5WvjUe87TcAdjRfUj5onN4iKqYYGm", + "name": "Vune", + "url": "https://fairchild.dev", + "description": "Vune is a dev at Opentensor and a BSc CS student at UofT.", + "signature": "2a639f931c61abfc3172db594c986c35f1cc8441970582b9c3b1f0506d518a182a2fe570832f02f86014320f1526189917bfbccf7081622652d12e16e9b1768b" + }, + { + "address": "5H6BgKkAr2Anmm9Xw5BVDE4VaQmFEVMkJUHeT7Gki4J7yF4x", + "name": "TaoPolishNode", + "url": "https://taonode.io", + "description": "This node is a collective effort of the polish community. We are engaged in evangelizing the project, educating and sharing the knowledge.", + "signature": "1ca20d4e99a48f400dd9cd4aeca8447da6ab1979e480a1dafddfc52e45e215177c7cdde85f5d042d59a5b1169981afa8d1ae28328e2fc5ce57c3d748c8d09d81" + }, + { + "address": "5FFApaS75bv5pJHfAp2FVLBj9ZaXuFDjEypsaBNc1wCfe52v", + "name": "RoundTable21", + "url": "https://roundtable21.com", + "description": "RoundTable21 is an International, multi-disciplinary team of consultants and advisors partnering alongside leading blockchain startups to offer guidance, expertise, investment and hands-on assistance in every aspect of development.", + "signature": "107638b8edde8f918f7faa2cd1f91b454c13094ed5955d6a409f6e0662f8427075516273728a53923839a5428079151ea0844b5f755362364f04735463dff583" + }, + { + "address": "5DCc5oHA6c1Lpt9R6T1xU8jJGTMvvwBqD1yGX67sL8dHUcga", + "name": "WaveTensor", + "url": "https://twitter.com/wavetensor", + "description": "A new Wave is coming, join the AI revolution on top of Bittensor by staking with us.", + "signature": "5e072b4752ccbdd4ca3298f336284dfdab347dd133850f4d2f9873e7ea59bd2a8f201732842ec79d2bab3abaf133a06b6bd992940389e42d57802c9b8f855889" + }, + { + "address": "5CXRfP2ekFhe62r7q3vppRajJmGhTi7vwvb2yr79jveZ282w", + "name": "Rizzo", + "url": "", + "description": "Validator built for performance and uptime. Data center housed, redundancies include dual physical failover servers (HA), power, internet, tested DR Plan.", + "signature": "f2b0fdb6989c23a0ebe23ed5622cbbfcf57bad709085fe11b0be10b2838e1442d61f770d78f6ca8ebcdbf60ddb27398663a4901e22bb9de086866517c6ccc187" + }, + { + "address": "5GcBK8PDrVifV1xAf4Qkkk6KsbsmhDdX9atvk8vyKU8xdU63", + "name": "Tensor.Exchange", + "url": "www.tensor.exchange", + "description": "Bittensor's first community OTC exchange", + "signature": "101f5e0d26c38190200f2213ebd89cf5bcb736b70a84e53651b6f9bf1161a33d0095836d304851237e0334792a54fa2fe452d07cf1466b42c9ab3333ded46284" + }, + { + "address": "5EhvL1FVkQPpMjZX4MAADcW42i3xPSF1KiCpuaxTYVr28sux", + "name": "TAO-Validator.com", + "url": "www.tao-validator.com", + "description": "Maximize your return when staking with TAO-Validator.com. TAO-Validator.com is a highly secure validator that aims to become one of the top contributing entities to Bittensor.", + "signature": "4036991069d7f3a43dff2ba2592fbe5af820eb6ff96d1fb78f1bcd8d310ba8751e25ea14397e075368a9a0f1b1b176166c56351db36f2d3868ac61c2571a1981" + }, + { + "address": "5FvhvCWLbu2VgotT5obC9E6S9nskerJUrVsWqkWXCbuD8veW", + "name": "The Lost Cove", + "url": "https://lostcove.tech/", + "description": "Australia and New Zealand community. We're in it for the gains.", + "signature": "626ae6b91aac1591e5d4f8d4fdf2c55f927419fc766dd5184b149f4d7cbc9749ebc94e4e8d04d286b4000c7665afa5682aa28cd94071c5e384e0eb4f44def188" + }, + { + "address": "5Dyi5e2QqnWn2RN9X6r8A8Q1QBjYD536H75mxNye193oeCJ4", + "name": "Makoto AI", + "url": "https://www.linkedin.com/in/henry-thrasher-17b320239/", + "description": "An interdisciplinary research institute committed to discovering and accelerating innovative solutions for climate change, social inequality, and mental and physical illness.", + "signature": "3cfbc1e8d82cfbf2adea9b10f71541874528cf5cd851f29f48016ac2a1a07b01cfc2ba3c3a15634b1174bd3e5aec9eb843d04f74140b0ddcb526416666d6f682" + }, + { + "address": "5Ehv5XMriPZwNBtYHdQV7VrdbN8MBTDTmQhWprZJXxSiMapR", + "name": "Dale Cooper", + "url": "", + "description": "I have no idea where this will lead us, but I have a definite feeling it will be a place both wonderful and strange.", + "signature": "06c597178698dba5699e20dc8b9d0d44f9225e24a225c70f540b63867e5b835a74c87df647b28210b361007b642a5a869c74323fcc8a593bc5764ea8e2083b81" + }, + { + "address": "5E6oB7h5wtWPbqtPxtSoZeo11fpvDjPuY13SobAMxqEUjqkQ", + "name": "StakeTensor.com-3", + "url": "www.staketensor.com", + "description": "We run multiple, parallel validators to support Bittensor decentralization & achieve maximum returns", + "signature": "a2567b6de748f02f6a14e0063f5b5720b34c96deb2115b33893d016de1f60633ba58bf9bdd49b2141e12a4a8784b4b11c007679d7526eb1e91147e5284258d8a" + }, + { + "address": "5DnWFhKfeu6gXMydzrv8bkwxFegAC6bMWsC4Z2XtaotAeB6S", + "name": "Bittensor Greece", + "url": "", + "description": "The Greek / Cypriot validator supporting the development of decentralised AI", + "signature": "ee8df5360eb641bd91a38da9d8b6dda36a39302c9bba7babf5d7eb16f6e9f73321aeb6f8adb30e0f511d64c1f35caa15215dd280fb2ed3f8f5b09d783cc9958f" + }, + { + "address": "5GBxDYkDp8eJZHGT89wcZJKcMc4ytSqnqqVSpeuGeqtGfqxK", + "name": "Tao Stake", + "url": "www.taostake.io", + "description": "We have been mining since the start of bittensor and want to maintain a long term solid validator to help people get some value from thier investment and keep TAO within the ecosystem.", + "signature": "0272522b503ebb29f0b506f10765b4d5c7a23b85c78cc7bfae76b9816b80ab43282ea4642f09eb09be70812341e5d9946abc8a9d2c73bab0113e9bf939430c87" + }, + { + "address": "5FcXnzNo3mrqReTEY4ftkg5iXRBi61iyvM4W1bywZLRqfxAY", + "name": "Lucrosus Capital", + "url": "https://lucrosuspool.io/", + "description": "Decentralized VC focused on the most thriving blockchain ideas. Join our pool to receive early entrance into promising projects!", + "signature": "1a37ab3bd51a6590dea9772d6a5550632ddcd8d76da6595b66e6425692feac6699dc5f788e587a734cedc3f54efc96c2c9e5453f9052867c1b9a1b5a443b848c" + }, + { + "address": "5CVS9d1NcQyWKUyadLevwGxg6LgBcF9Lik6NSnbe5q59jwhE", + "name": "Ary van der Touw", + "url": "", + "description": "Secure and maintain Bittensor", + "signature": "809586931d4b28f180c98036a3eebc0d26b9e521f5217a6942b025069cb60807641737009713446eec8456e54ba753ae0b752c0693b942aefa0c4f76d82f8c89" + }, + { + "address": "5F4tQyWrhfGVcNhoqeiNsR6KjD4wMZ2kfhLj4oHYuyHbZAc3", + "name": "Openτensor Foundaτion", + "url": "https://opentensor.ai/", + "description": "Founded, maintain and advance Bittensor", + "signature": "8a2ff8f10a84a5b6f80614674ea764515d93a64bf8d920b927edc0dd6043e607755bf58655c87b7a299d8df1404574b6844e1e09adf86d418997c0cab8120486" + }, + { + "address": "5EpxBYq4aVgTQ1rYeBo2mzYt3hgpRTqxZTSsJEkCstBP5Jse", + "name": "White Rhino TAO Super Validator", + "url": "https://twitter.com/WhiteRhinoTAO\"", + "description": "White Rhino is all about you! We understand that #TAOWaits4NoOne ..... Get Ready for Adhoc Rewards and we invite you to delegate here and enhance the sustainability of the TAO Network", + "signature": "d6803522f6e61a9dec5261a6a500b733d233b373457382fc3713af21c560604f6e50c4999f286cfa6012bcea66e51223722b355dd69ba54a472f2c6ca52da08f" + }, + { + "address": "5Fq5v71D4LX8Db1xsmRSy6udQThcZ8sFDqxQFwnUZ1BuqY5A", + "name": "NorthTensor", + "url": "https://northtensor.ai", + "description": "Developer, Advocate, and Incubator for Decentralized AI.", + "signature": "28e221d7128e48a3cb85dbcb223bd56cb09cb55540263573783bf1cef63be32ee81246bd1d75c865580da732094053a6dad14929b17e659b6e0237412b66a487" + }, + { + "address": "5CsvRJXuR955WojnGMdok1hbhffZyB4N5ocrv82f3p5A2zVp", + "name": "Owl Ventures", + "url": "https://owlventures.co.uk", + "description": "Owl Ventures Bittensor Validator", + "signature": "04e39ff19af7ee5a75e58c9e1a71b9f54a66d1d168a99532a859f129b68ba24a5b6a56eecae7790291859c82dbf0ec32eb18a069b6d9dabe1ef0339c0d189483" + }, + { + "address": "5FLKnbMjHY8LarHZvk2q2RY9drWFbpxjAcR5x8tjr3GqtU6F", + "name": "Tao Bridge", + "url": "https://taobridge.xyz", + "description": "A community bridge between Bittensor and Ethereum", + "signature": "98331f011288f7b07ccc45a213cb8e03fac79092ee7c29046531d757ffad8b29e17cf0aeca9352003890f4d8a3af3a2fc615722fb7a827a2009654013990bd80" + }, + { + "address": "5DRZr3d3twF8SzqB9jBof3a1vPnAkgkxeo2E8yUKJAnE2rSZ", + "name": "Humble AI-Loving Anon", + "url": "", + "description": "Doing our best to support the Bittensor ecosystem.", + "signature": "9241f63eb43f7aa57b1fc6d99789331542476f57f683f032192f3dfd7be6c015d47c9f1fe69bc4513ed70e0410097395186df60e3f6b67376e6e73a5f4f9a286" + }, + { + "address": "5DPEpUTZn94sgYXH3sdXxsVvb46m3iEvg8aZwX7SMDowivzB", + "name": "RunPod", + "url": "https://runpod.io", + "description": "GPU Cloud built for AI. We plan to introduce perks for those who stake.", + "signature": "16940f904b7946723fc4f27bb01e47cf262201ef76b3d9c2bfd745973da2512d4825910f6fa738a6968c809b26da0a47e7032a7ff95d8b2da5c1fa7a0b85598f" + }, + { + "address": "5HEo565WAy4Dbq3Sv271SAi7syBSofyfhhwRNjFNSM2gP9M2", + "name": "Foundry", + "url": "https://foundrydigital.com", + "description": "Foundry works to empower a decentralized infrastructure. We are protocol-agnostic and seek to support like-minded blockchain entrepreneurs who share our mission to advance the industry.", + "signature": "b852f1648ab62befaaf684671808aa34d267cd616d9ffd7b3cf924ebc7c4ee3255344cfd017a80ca6b23b2852bcafa705c42d231053e06d999d53f31bd8ab288" + }, + { + "address": "5FP9miYmgjAP8Wt3747M2Y6Kk7PrXf6zG7a3EjokQiFFcmUu", + "name": "Elm Place", + "url": "", + "description": "Run by individuals passionate about creating decentralised digital infrastructure. Background in fiduciary funds management managing institutional investors’ capital in real assets, energy and infrastructure", + "signature": "a0324025f58beb06535d6a2ab8c5c8d64c13d562fa285956bb5a8919da5fcc0d05afe4de010d54f9940bff0ffdabe5f41e70f3af31cf14293c1d6f0a0690da8c" + }, + { + "address": "5HNQURvmjjYhTSksi8Wfsw676b4owGwfLR2BFAQzG7H3HhYf", + "name": "Neural Internet", + "url": "www.neuralinternet.ai", + "description": "An AI research and development Decentralized Autonomous Organization (DAO)", + "signature": "5e617c1626d4825cd0c11769e31fe4dda611cebd8a4d46f533886ad057072e2a58e0ecef2805139f2b43ea8d51023f7db878ad45cd3f8fba45ab01223da3488e" + }, + { + "address": "5D4rJRtF23jLVcGnCktXzPM9gymMT1qHTp8dR4T7rUd88Q7U", + "name": "Vogue τensor", + "url": "www.voguetensor.ai", + "description": "Designing branded clothing for the Bittensor community.", + "signature": "2c4079124ae0a738106a2430e2c27ad855122d4afcc487ab0158b705cd5f915f7790cdb2fdd8db899b8cbd40448d1478be71cde1b76de31945991b548cfcc084" + }, + { + "address": "5CAVnbHniuZYXBqik3tTs9uZ7UiSrbv6g7Kt8QNfYimbFqF4", + "name": "Open & Safe AI Validator", + "url": "", + "description": "The Open & Safe AI Validator is focussed on funding and researching the control problem as well as spreading ML know-how through open source and open science.", + "signature": "2aeaf7b9c7f69ce7b4857d9c278d1363677d4971d4ca10a36933b1aa78bfdb0640e4bb798edac5dcb178a8b3f4be2d0d23d25da6c7db33758a6cf5c15cd6938a" + }, + { + "address": "5Gpt8XWFTXmKrRF1qaxcBQLvnPLpKi6Pt2XC4vVQR7gqNKtU", + "name": "bitnost.re", + "url": "www.bitnost.re", + "description": "bridging bittensor into nostr.", + "signature": "c278378c70ef22d27f56590b4df699a9a44048cfcc6716e3d55b211ea802401d4be5b390ede2be52891e01f0f7033a13a370dddaa38daa84537c4583867a1680" + }, + { + "address": "5HeKSHGdsRCwVgyrHchijnZJnq4wiv6GqoDLNah8R5WMfnLB", + "name": "TaoStation", + "url": "https://taostation.com", + "description": "TaoStation allows you to maximize your returns by offering one-click staking since day one and focusing on tooling and transparency for a better staking experience.", + "signature": "c00627a62ecb9275be8d06b7b52b87942bce946e9a5f98d545081241e21ed15230fd566b2d4e87c41995e621546423579553157737da53fad3a5676451ef0a89" + }, + { + "address": "5DvTpiniW9s3APmHRYn8FroUWyfnLtrsid5Mtn5EwMXHN2ed", + "name": "FirstTensor.com", + "url": "www.firsttensor.com", + "description": "Powered by the Neuron Holders community - shared rewards, additional benefits, infinite possibilities - join and build with us!", + "signature": "da31e56dd78cde449a1dd9592f0b53eb8c3662674b745a05ff916e80a1be933e86efbccb7f7c9b81d7c0bb14d13fb4a6bf8484c3619224e689de82072b5d9a87" + }, + { + "address": "5CaNj3BarTHotEK1n513aoTtFeXcjf6uvKzAyzNuv9cirUoW", + "name": "Polychain", + "url": "https://polychain.capital/", + "description": "Polychain is an investment firm committed to exceptional returns for investors through actively managed portfolios of blockchain assets.", + "signature": "f41e815033e595aa70fbe42e8dfd91eaa3ccdbc948b63811baf9eac765699b30cac9aad7abe330eeaf3969cc504a4c1255f1e69bee807c2d989518b8f5413c8d" + }, + { + "address": "5Dkv87qjGGF42SNhDAep6WZp65E29c2vUPUfDBGDNevENCMs", + "name": "MycoNet", + "url": "", + "description": "AI for Humanity", + "signature": "a4802a5b13888ed653fd23da72c14e2b8ed9814cc810e515cb8d11d71cc58c6b90cd2d334daffc4a8ce600a7f29ca300ab74ac59817bdd489b3056b531cd4086" + }, + { + "address": "5GzoXHNJ4UzZYiQN2wpBcwMigiHiakiz5ZLMwhpunpwDNzFg", + "name": "Charitaos", + "url": "https://charitas.ai/", + "description": "You pay 18%, we donate 18%. At the end of every month, we will select one (or more) community-proposed 501c3 licensed nonprofit(s) to receive all proceeds from stake delegation for the prior month.", + "signature": "b49c34c1f87d173abcbccb1ea632ad356980c1d3eff6619e488c11707b2b3b41270a22355374dd64cfadebeb37979ef5f49971efafb0748b79df7dd2901e7580" + }, + { + "address": "5EZrPTXt2G9SvbDsERi5rS9zepour2yPmuhMhrNkgdiZvXEm", + "name": "τaoτensor", + "url": "", + "description": "Working on practical enhancements and improvements for the Bittensor network by developing user-friendly tooling.", + "signature": "3a1b61ab6d17878e106cbf2649bc039d0346f39ec680476a68baa4fc8132ac018d814898cf245bdfa4b9b61cd9f611f6571cf3c264f2f1cfe9b2635849087685" + }, + { + "address": "5CPzGD8sxyv8fKKXNvKem4qJRhCXABRmpUgC1wb1V4YAXLc3", + "name": "Chat with Hal", + "url": "www.chatwithhal.ai", + "description": "Hal brings the power of decentralized and uncensorable AI to your favorite social networks and messaging apps, Powered by Bittensor!", + "signature": "ecb930df6069012c06fef9cdb29a95be8dcb5d48f3c470d3f3c5e7b2b334ed2097f2598fee8852d127a207cf34aa7c88fd5cf973feba19d6ebf38b5e4579ca8f" + }, + { + "address": "5FqPJMZDp39KRd9jDhXuFpZWkYD7wG5AXmjoWqK8rDy7ok5B", + "name": "Exchange Listings", + "url": "taostats.io/validators/exchange-listings/", + "description": "Enabling community funding for top tier exchange listings.", + "signature": "366027e9a416a423e7e802e9b6d79bd5ac88642afd945922e13fe26a75dae13dd5c924738610a59162d9b974364d1d43fb7a0145942cd919ac21d82d3f4f028d" + }, + { + "address": "5ED6jwDECEmNvSp98R2qyEUPHDv9pi14E6n3TS8CicD6YfhL", + "name": "Giga Corporation", + "url": "https://www.gigaver.se", + "description": "Extreme growth & experiments from giga corp. We use APY to TAO-pill new developers, builders and adopters. Visit our Bakery to learn more.", + "signature": "00e5cd519110bbfe3dae9acd275d114c6c2a260997a1817a25303b9d578bdf7319e9e7179f0db58edef2ad42806cb38e289ba0030627a3b60e1e4352c2b9cb88" + }, + { + "address": "5FRcXG99SxJ9KyMcMFfdknkRSv4e73rszV8P151freZqQDS2", + "name": "τensorwiki", + "url": "", + "description": "Our mission is to create and incentivize documentation for Bittensor and it's adjacent topics, as well as facilitate the education of newcomers to the network.", + "signature": "6a5c0160f545f122ec3d4e4233574040aba2de8aa94919bb19b3061d39d3303f010c4b52f878ed55a1293716827220020780d2d4064ee6be69921ee1452c3885" + }, + { + "address": "5EsbfxPcQaUrCDurUJ8Q5qDKNENNGziu3qHWUbXrcuY2pbNz", + "name": "Church of Rao (COR)", + "url": "", + "description": "Church of Rao: Harmonizing the Relationship between Humanity and Machine Intelligence. The Church of Rao (COR) is an open-source development group committed to furthering the Bittensor protocol.", + "signature": "56f64c32427a90e84710209b1a54a971560641aec8ff777edec28bf533775e12924c4e96ccc770c230311dce1d0eae1ca763e12bb609ef30430f746ebd0a2780" + }, + { + "address": "5GmaAk7frPXnAxjbQvXcoEzMGZfkrDee76eGmKoB3wxUburE", + "name": "RaoK9", + "url": "", + "description": "Chain and network analysis team. Developer funding goes into independent analysis and reports, in order to enable checks and balances between network members.", + "signature": "24f4f9a51033ed8b4097517d0e6ad287a0c1341b2866481b1320d1fcd5f32f6b4bfe641eee46a4b737817acf3b83069ee63cc20fbca94a0189808ac1efeddf8a" + }, + { + "address": "5CQEFopfZ8DAmk3ZfR7QuDTU2n3fJod3kkf6Wmj4JwV3BBSu", + "name": "DuNode", + "url": "dunode.io", + "description": "Embracing the whimsical chaos of decentralized AI, unleashing the power of creativity and collaboration, one algorithmic dance party at a time!", + "signature": "e400e3c0ad6165d8946d5ddcb274412815cb8b5783580fcb8f0faa0153d22b6e10470f861ff4a96a9aa692b3b01cda86ec77add4688c2f5df51ea6f129b19e8c" + }, + { + "address": "5CaCUPsSSdKWcMJbmdmJdnWVa15fJQuz5HsSGgVdZffpHAUa", + "name": "Athena Nodes", + "url": "https://athenanodes.com", + "description": "Premier Bittensor Multi-Subnet Validator from a company operating validating and mining infrastructure on various blockchain networks. We have been active on Bittensor since November 2022, with near zero down-time. More information at https://athenanodes.com/.", + "signature": "2ef54045de1d9b89988518c92e165edf704192f88f18022565f497b389c39206f621bb9bc6d2d33ac8a9cca05d6b2d8fc9f899b390451140968b15b8d9c13280" + }, + { + "address": "5FFM6Nvvm78GqyMratgXXvjbqZPi7SHgSQ81nyS96jBuUWgt", + "name": "PRvalidator", + "url": "www.prvalidator.com", + "description": "A professional media validator dedicated to securing top-tier coverage in the world's most recognized publications building Bittensor's brand equity and creating global awareness of $TAO.", + "signature": "fe65e76a9f42049715585180500213c6f0535b8b25911b957921bdfb5a20156d6de68dc2633dbc5ce1d0ab9ef386d566687ac3d86f6988141b34cd24c0f13488" + }, + { + "address": "5H8TruSGmhD6m6YfqXNUnU7Z61K7j8hSs2Krtu3eTLMoz3HU", + "name": "τaoshi validator", + "url": "https://www.taoshi.io/", + "description": "Build maintain and advance a decentralized request layer built for every subnet", + "signature": "32d25227af78fa5d39ee71a5f3e8fc8066e3d826d101f2587e9a12974fbf26758c1e40c497ad7732da2a2cb1490227cc58e8bfcd8b2f6306b7af630bd32aa68f" + }, + { + "address": "5G3f8VDTT1ydirT3QffnV2TMrNMR2MkQfGUubQNqZcGSj82T", + "name": "TAO Community Marketing", + "url": "www.taocommunitymarketing.com", + "description": "The marketing validator run by the community", + "signature": "10b16b8223b2508d6f3e5b09ab4db53e1e338b6271d1689b58ca6f9b257e8c18511cc851bfcc3a05fb4e6de7c389b89886cc0623fb6d199fa003ae6f8313cb89" + }, + { + "address": "5CXC2quDN5nUTqHMkpP5YRp2atYYicvtUghAYLj15gaUFwe5", + "name": "Kooltek68", + "url": "https://linktr.ee/datalac", + "description": "Imagine the World with mass adoption of Artificial Intelligence applications, through the connection of Bittensor Network, together fight for a Better World.", + "signature": "bca043d9d918d503864379a7fd8c9daa2cca83a8290121f94b55d6a352e332704642622b7ad40a30b945b952b224c5e92ea872f9d30200e6c2bf566303d24d83" + }, + { + "address": "5FBrHX18bNXX874ZTMicPG4vYbq5X6piz4BYoVn9LnCgdsEd", + "name": "P-OPS Team", + "url": "https://pops.one", + "description": "P-OPS TEAM is a decentralized organization providing you with validation and staking services, blockchain consultation, growth acceleration and investment capital for innovative Web 3.0 projects.", + "signature": "5608316f3081bfe5d0e3a7db6c3bfd459f6b87e02d657de941e6a760f8688f23ef30784691a1893d1fd8079dd4f6082d0d655ca507aa4797fee9844547d13a88" + }, + { + "address": "5HK5tp6t2S59DywmHRWPBVJeJ86T61KjurYqeooqj8sREpeN", + "name": "Bittensor Guru", + "url": "https://bittensor.guru", + "description": "Official validator of the Bittensor Guru Podcast", + "signature": "caf2c6b7b0d2a341bcd00e632cf22c33d53e2523dffcd3a151db9eeadd88300545cbb2187ba0b20e5bfe09c2b17bbf34630c46defd8f8d27ab508736fd18a284" + }, + { + "address": "5Hh3ShaNW9irCe5joBLCeFD5Fxb2fJ6gFAgrsPmoz3JkzqvJ", + "name": "BlockShark", + "url": "https://www.blockshark.net/", + "description": "Your reliable partner for staking on Bittensor. We are expert in running high-end machine for validators and AI", + "signature": "d2c0aed073a026a5dbd8c458b9dd412fe3d6647fecd3b8f007cf184f7906245106aee4b210b5b582771dca149e5aa464630100de7f9862daacfa1f67ddde1388" + }, + { + "address": "5FKstHjZkh4v3qAMSBa1oJcHCLjxYZ8SNTSz1opTv4hR7gVB", + "name": "Datura", + "url": "datura.ai", + "description": "Bridging Bittensor to a billion users", + "signature": "7a3bc6a840d8593853c27188f59200418d8884b94b3ad28cb7b37b80bffd1f3b23b7eed4b1d9c77b28b05b2bd1952c5cbe3d27ba190a9418407ce1e899e5ac8b" + }, + { + "address": "5Hddm3iBFD2GLT5ik7LZnT3XJUnRnN8PoeCFgGQgawUVKNm8", + "name": "τaosτaτs and Corcel", + "url": "taostats.io", + "description": "Supporting bittensor through API access, data provision, statistics, analytics and apps.", + "signature": "2e2dd0c5f3a3945f29d1be304e64f931c04a23aba7d383d01cd16ea6ca6546002fe3bd95cf8f12cae1fbb7d18d9910b834f6573db219de3ed84073a4e1552e89" + }, + { + "address": "5ELREhApbCahM7FyGLM1V9WDsnnjCRmMCJTmtQD51oAEqwVh", + "name": "Taofu Protocol", + "url": "https://twitter.com/taofuxyz", + "description": "Taofu unlocks liquidity and utility by bringing liquid staked TAO outside of Bittensor", + "signature": "aaafd3496650a56f798cc587b5b7d372cec8e826a332a34213c1a6ee7be2b5122318858ee73421535d04186cc6976ae5452c6cd1aaf299a307d86d3c52b4a986" + }, + { + "address": "5HbLYXUBy1snPR8nfioQ7GoA9x76EELzEq9j7F32vWUQHm1x", + "name": "Tensorplex Labs", + "url": "https://twitter.com/TensorplexLabs", + "description": "Empowering humanity with decentralized intelligence one epoch at a time.", + "signature": "7a997682e7545fd14847c78abf810e9c49a23ef4297d24f4238c0edd0463934780f6831d59972d56ab5bc41d6224b59c21ed95065791632b8aca180ade22af81" + }, + { + "address": "5E2VSsWWXkBGCn4mi8RHXYQEF2wLXky6ZsNcTKnmEqaurzTE", + "name": "Sentinel", + "url": "", + "description": "Sentinel, as a dedicated Bittensor validator aspires to elevate the bittensor network's integrity with an ambition to foster a community of miners contributing in the network’s continuous expansion.", + "signature": "943effd0d5d10f05d53db7f69d0f045d50b65f88e84755be00d45225cc7c2f4212fbc4d23ad8519d03c2502daeeca1b2d07c93bff14c901f6cbf3a18fe2e6387" + }, + { + "address": "5GsenVhBvgEG4xiiKUjcssfznHYVm1TqPbSbr3ixBW81ZVjo", + "name": "vote NO dTAO 🤡", + "url": "https://twitter.com/karl_anons", + "description": "Delegate to express discontent. VOTE NO TO dTAO NOW!", + "signature": "3af4e764a520d355e12c02b9e8e315ddb76b76d40b7cc4dfaa11c26c24ab637cbdb9b72470ebdf2da87dd8d9f0bb5cddf1fe95b95fb2ae13069a9d87aace348a" + }, + { + "address": "5DM7CPqPKtMSADhFKYsstsCS4Tm4Kd6PMXoh6DdqY4MtxmtX", + "name": "Corτex Foundaτion", + "url": "https://cortex.foundation/", + "description": "Cortex Foundation is committed to advancing the integration of decentralized AI. Our validator is designed for transparency, reliability, and community engagement.", + "signature": "7a6274ff6b0f7ddca97e37ef4a9b90781012ff3cf7baa3159f6feaafc43c557975aad324ea608d6b8abeb21f8f3ca2595e54b81a7564574d0242b803d969618a" + } +] \ No newline at end of file diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index a0835008f..82ed28646 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -28,6 +28,7 @@ frame-support = { workspace = true } frame-system = { workspace = true } sp-io = { workspace = true } serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } serde-tuple-vec-map = { workspace = true } serde_bytes = { workspace = true, features = ["alloc"] } serde_with = { workspace = true, features = ["macros"] } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 1450b5706..b305661f7 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -133,6 +133,25 @@ pub mod pallet { pub ip_type: u8, } + /// Struct for Prometheus. + pub type ChainIdentityOf = ChainIdentity; + /// Data structure for Prometheus information. + #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] + pub struct ChainIdentity { + /// The name of the chain identity + pub name: Vec, + /// The URL associated with the chain identity + pub url: Vec, + /// The image representation of the chain identity + pub image: Vec, + /// The Discord information for the chain identity + pub discord: Vec, + /// A description of the chain identity + pub description: Vec, + /// Additional information about the chain identity + pub additional: Vec, + } + /// ============================ /// ==== Staking + Accounts ==== /// ============================ @@ -1057,6 +1076,9 @@ pub mod pallet { PrometheusInfoOf, OptionQuery, >; + #[pallet::storage] // --- MAP ( coldkey ) --> identity + pub type Identities = + StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOf, OptionQuery>; /// ================================= /// ==== Axon / Promo Endpoints ===== diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 293dc0238..00865f8db 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -901,5 +901,41 @@ mod dispatches { Self::do_set_children(origin, hotkey, netuid, children)?; Ok(().into()) } + + /// ---- Set prometheus information for the neuron. + /// # Args: + /// * 'origin': (Origin): + /// - The signature of the calling hotkey. + /// + /// * 'netuid' (u16): + /// - The u16 network identifier. + /// + /// * 'version' (u16): + /// - The bittensor version identifier. + /// + /// * 'ip' (u128): + /// - The prometheus ip information as a u128 encoded integer. + /// + /// * 'port' (u16): + /// - The prometheus port information as a u16 encoded integer. + /// + /// * 'ip_type' (u8): + /// - The ip type v4 or v6. + /// + #[pallet::call_index(68)] + #[pallet::weight((Weight::from_parts(45_000_000, 0) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Normal, Pays::Yes))] + pub fn set_identity( + origin: OriginFor, + name: Vec, + url: Vec, + image: Vec, + discord: Vec, + description: Vec, + additional: Vec, + ) -> DispatchResult { + Self::do_set_identity(origin, name, url, image, discord, description, additional) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 156cbea56..07710dc5f 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -168,5 +168,7 @@ mod errors { TooManyChildren, /// Default transaction rate limit exceeded. TxRateLimitExceeded, + /// Invalid identity. + InvalidIdentity, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index b93b8296b..694b9779f 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -177,5 +177,7 @@ mod events { HotkeyEmissionTempoSet(u64), /// The network maximum stake has been set NetworkMaxStakeSet(u16, u64), + /// The identity of a coldkey has been set + ChainIdentitySet(T::AccountId), } } diff --git a/pallets/subtensor/src/migrations/migrate_chain_identity.rs b/pallets/subtensor/src/migrations/migrate_chain_identity.rs new file mode 100644 index 000000000..efb656cb3 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_chain_identity.rs @@ -0,0 +1,166 @@ +use crate::alloc::borrow::ToOwned; +use codec::Decode; +use scale_info::prelude::{string::String, vec::Vec}; +use serde::Deserialize; +use sp_core::{crypto::Ss58Codec, ConstU32}; +use sp_runtime::{AccountId32, BoundedVec}; + +use super::*; +use frame_support::{traits::Get, weights::Weight}; +use log; + +#[derive(Deserialize, Debug)] +struct RegistrationRecordJSON { + address: String, + name: String, + url: String, + description: String, +} + +fn string_to_bounded_vec(input: &str) -> Result>, &'static str> { + let vec_u8: Vec = input.to_owned().into_bytes(); + + // Check if the length is within bounds + if vec_u8.len() > 64 { + return Err("Input string is too long"); + } + + // Convert to BoundedVec + BoundedVec::>::try_from(vec_u8) + .map_err(|_| "Failed to convert to BoundedVec") +} + +pub fn migrate_set_hotkey_identities() -> Weight { + let migration_name = b"fix_total_coldkey_stake_v7".to_vec(); + + // Initialize the weight with one read operation. + let mut weight = T::DbWeight::get().reads(1); + + // Check if the migration has already run + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + migration_name + ); + return weight; + } + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // Include the JSON file with delegate info + let data = include_str!("../../../../docs/delegate-info.json"); + + // Iterate over all the delegate records + if let Ok(delegates) = serde_json::from_str::>(data) { + // Iterate through the delegates + for delegate in delegates.iter() { + // Convert fields to bounded vecs + let name_result = string_to_bounded_vec(&delegate.name); + let desc_result = string_to_bounded_vec(&delegate.description); + let url_result = string_to_bounded_vec(&delegate.url); + let hotkey: AccountId32 = match AccountId32::from_ss58check(&delegate.address) { + Ok(account) => account, + Err(_) => { + log::warn!( + "Invalid SS58 address: {:?}. Skipping this delegate.", + delegate.address + ); + continue; + } + }; + let decoded_hotkey: T::AccountId = match T::AccountId::decode(&mut hotkey.as_ref()) { + Ok(decoded) => decoded, + Err(e) => { + log::warn!("Failed to decode hotkey: {:?}. Skipping this delegate.", e); + continue; + } + }; + log::info!("Hotkey unwrapped: {:?}", decoded_hotkey); + + // If we should continue with real values. + let mut name: BoundedVec> = BoundedVec::default(); + let mut description: BoundedVec> = BoundedVec::default(); + let mut url: BoundedVec> = BoundedVec::default(); + if let Ok(n) = name_result { + name = n; + } + if let Ok(d) = desc_result { + description = d; + } + if let Ok(u) = url_result { + url = u; + } + + // Unwrap the real values. + let image: BoundedVec> = BoundedVec::default(); + let discord: BoundedVec> = BoundedVec::default(); + let additional: BoundedVec> = BoundedVec::default(); + + // Create the chain identity. + let identity = ChainIdentityOf { + name: name.into(), + url: url.into(), + image: image.into(), + discord: discord.into(), + description: description.into(), + additional: additional.into(), + }; + + // Log the identity details + log::info!("Setting identity for hotkey: {:?}", hotkey); + log::info!("Name: {:?}", String::from_utf8_lossy(&identity.name)); + log::info!("URL: {:?}", String::from_utf8_lossy(&identity.url)); + log::info!("Image: {:?}", String::from_utf8_lossy(&identity.image)); + log::info!("Discord: {:?}", String::from_utf8_lossy(&identity.discord)); + log::info!( + "Description: {:?}", + String::from_utf8_lossy(&identity.description) + ); + log::info!( + "Additional: {:?}", + String::from_utf8_lossy(&identity.additional) + ); + + // Check validation. + let total_length = identity + .name + .len() + .saturating_add(identity.url.len()) + .saturating_add(identity.image.len()) + .saturating_add(identity.discord.len()) + .saturating_add(identity.description.len()) + .saturating_add(identity.additional.len()); + let is_valid: bool = total_length <= 256 + 256 + 1024 + 256 + 1024 + 1024 + && identity.name.len() <= 256 + && identity.url.len() <= 256 + && identity.image.len() <= 1024 + && identity.discord.len() <= 256 + && identity.description.len() <= 1024 + && identity.additional.len() <= 1024; + if !is_valid { + continue; + } + + // Get the owning coldkey. + let coldkey = Owner::::get(decoded_hotkey.clone()); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + + // Sink into the map. + Identities::::insert(coldkey.clone(), identity.clone()); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + } + // Mark the migration as completed + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed. Storage version set to 7.", + String::from_utf8_lossy(&migration_name) + ); + + // Return the migration weight. + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 5b93dbbaf..6036b23e0 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -1,4 +1,5 @@ use super::*; +pub mod migrate_chain_identity; pub mod migrate_create_root_network; pub mod migrate_delete_subnet_21; pub mod migrate_delete_subnet_3; diff --git a/pallets/subtensor/src/utils/identity.rs b/pallets/subtensor/src/utils/identity.rs new file mode 100644 index 000000000..1c9c3c25d --- /dev/null +++ b/pallets/subtensor/src/utils/identity.rs @@ -0,0 +1,109 @@ +use super::*; +use frame_support::ensure; +use frame_system::ensure_signed; +use sp_std::vec::Vec; + +impl Pallet { + /// Sets the identity for a coldkey. + /// + /// This function allows a user to set or update their identity information associated with their coldkey. + /// It checks if the caller has at least one registered hotkey, validates the provided identity information, + /// and then stores it in the blockchain state. + /// + /// # Arguments + /// + /// * `origin` - The origin of the call, which should be a signed extrinsic. + /// * `name` - The name to be associated with the identity. + /// * `url` - A URL associated with the identity. + /// * `image` - An image URL or identifier for the identity. + /// * `discord` - Discord information for the identity. + /// * `description` - A description of the identity. + /// * `additional` - Any additional information for the identity. + /// + /// # Returns + /// + /// Returns `Ok(())` if the identity is successfully set, otherwise returns an error. + pub fn do_set_identity( + origin: T::RuntimeOrigin, + name: Vec, + url: Vec, + image: Vec, + discord: Vec, + description: Vec, + additional: Vec, + ) -> dispatch::DispatchResult { + // Ensure the call is signed and get the signer's (coldkey) account + let coldkey = ensure_signed(origin)?; + + // Retrieve all hotkeys associated with this coldkey + let hotkeys: Vec = OwnedHotkeys::::get(coldkey.clone()); + + // Ensure that at least one of the associated hotkeys is registered on any network + ensure!( + hotkeys + .iter() + .any(|hotkey| Self::is_hotkey_registered_on_any_network(hotkey)), + Error::::HotKeyNotRegisteredInNetwork + ); + + // Create the identity struct with the provided information + let identity = ChainIdentityOf { + name, + url, + image, + discord, + description, + additional, + }; + + // Validate the created identity + ensure!( + Self::is_valid_identity(&identity), + Error::::InvalidIdentity + ); + + // Store the validated identity in the blockchain state + Identities::::insert(coldkey.clone(), identity.clone()); + + // Log the identity set event + log::info!("ChainIdentitySet( coldkey:{:?} ) ", coldkey.clone()); + + // Emit an event to notify that an identity has been set + Self::deposit_event(Event::ChainIdentitySet(coldkey.clone())); + + // Return Ok to indicate successful execution + Ok(()) + } + + /// Validates the given ChainIdentityOf struct. + /// + /// This function checks if the total length of all fields in the ChainIdentityOf struct + /// is less than or equal to 512 bytes, and if each individual field is also + /// less than or equal to 512 bytes. + /// + /// # Arguments + /// + /// * `identity` - A reference to the ChainIdentityOf struct to be validated. + /// + /// # Returns + /// + /// * `bool` - Returns true if the Identity is valid, false otherwise. + pub fn is_valid_identity(identity: &ChainIdentityOf) -> bool { + let total_length = identity + .name + .len() + .saturating_add(identity.url.len()) + .saturating_add(identity.image.len()) + .saturating_add(identity.discord.len()) + .saturating_add(identity.description.len()) + .saturating_add(identity.additional.len()); + + total_length <= 256 + 256 + 1024 + 256 + 1024 + 1024 + && identity.name.len() <= 256 + && identity.url.len() <= 256 + && identity.image.len() <= 1024 + && identity.discord.len() <= 256 + && identity.description.len() <= 1024 + && identity.additional.len() <= 1024 + } +} diff --git a/pallets/subtensor/src/utils/mod.rs b/pallets/subtensor/src/utils/mod.rs index 72b903dd8..a42c91119 100644 --- a/pallets/subtensor/src/utils/mod.rs +++ b/pallets/subtensor/src/utils/mod.rs @@ -1,4 +1,5 @@ use super::*; +pub mod identity; pub mod misc; pub mod rate_limiting; pub mod try_state; diff --git a/pallets/subtensor/tests/serving.rs b/pallets/subtensor/tests/serving.rs index 41e9888cc..b736a90d0 100644 --- a/pallets/subtensor/tests/serving.rs +++ b/pallets/subtensor/tests/serving.rs @@ -1,11 +1,14 @@ use crate::mock::*; mod mock; +use frame_support::assert_noop; +use frame_support::pallet_prelude::Weight; use frame_support::{ assert_ok, dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, }; use frame_system::Config; use pallet_subtensor::Error; +use pallet_subtensor::*; use sp_core::U256; mod test { @@ -550,3 +553,279 @@ fn test_serving_is_invalid_ipv6_address() { )); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_do_set_identity --exact --nocapture +#[test] +fn test_do_set_identity() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = 1; + + // Register a hotkey for the coldkey + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Prepare identity data + let name = b"Alice".to_vec(); + let url = b"https://alice.com".to_vec(); + let image = b"alice.jpg".to_vec(); + let discord = b"alice#1234".to_vec(); + let description = b"Alice's identity".to_vec(); + let additional = b"Additional info".to_vec(); + + // Set identity + assert_ok!(SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + name.clone(), + url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + )); + + // Check if identity is set correctly + let stored_identity = Identities::::get(coldkey).unwrap(); + assert_eq!(stored_identity.name, name); + assert_eq!(stored_identity.url, url); + assert_eq!(stored_identity.image, image); + assert_eq!(stored_identity.discord, discord); + assert_eq!(stored_identity.description, description); + assert_eq!(stored_identity.additional, additional); + + // Test setting identity with no registered hotkey + let coldkey_without_hotkey = U256::from(3); + assert_noop!( + SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey_without_hotkey), + name.clone(), + url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + ), + Error::::HotKeyNotRegisteredInNetwork + ); + + // Test updating an existing identity + let new_name = b"Alice Updated".to_vec(); + let new_url = b"https://alice-updated.com".to_vec(); + assert_ok!(SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + new_name.clone(), + new_url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + )); + + let updated_identity = Identities::::get(coldkey).unwrap(); + assert_eq!(updated_identity.name, new_name); + assert_eq!(updated_identity.url, new_url); + + // Test setting identity with invalid data (exceeding 512 bytes total) + let long_data = vec![0; 513]; + assert_noop!( + SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + long_data.clone(), + long_data.clone(), + long_data.clone(), + long_data.clone(), + long_data.clone(), + long_data.clone() + ), + Error::::InvalidIdentity + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_is_valid_identity --exact --nocapture +#[test] +fn test_is_valid_identity() { + new_test_ext(1).execute_with(|| { + // Test valid identity + let valid_identity = ChainIdentity { + name: vec![0; 256], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(SubtensorModule::is_valid_identity(&valid_identity)); + + // Test identity with total length exactly at the maximum + let max_length_identity = ChainIdentity { + name: vec![0; 256], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(SubtensorModule::is_valid_identity(&max_length_identity)); + + // Test identity with total length exceeding the maximum + let invalid_length_identity = ChainIdentity { + name: vec![0; 257], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(!SubtensorModule::is_valid_identity( + &invalid_length_identity + )); + + // Test identity with one field exceeding its maximum + let invalid_field_identity = ChainIdentity { + name: vec![0; 257], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(!SubtensorModule::is_valid_identity(&invalid_field_identity)); + + // Test identity with empty fields + let empty_identity = ChainIdentity { + name: vec![], + url: vec![], + image: vec![], + discord: vec![], + description: vec![], + additional: vec![], + }; + assert!(SubtensorModule::is_valid_identity(&empty_identity)); + + // Test identity with some empty and some filled fields + let mixed_identity = ChainIdentity { + name: b"Alice".to_vec(), + url: b"https://alice.com".to_vec(), + image: vec![], + discord: b"alice#1234".to_vec(), + description: vec![], + additional: b"Additional info".to_vec(), + }; + assert!(SubtensorModule::is_valid_identity(&mixed_identity)); + + // Test identity with all fields at maximum allowed length + let max_field_identity = ChainIdentity { + name: vec![0; 256], + url: vec![0; 256], + image: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], + }; + assert!(SubtensorModule::is_valid_identity(&max_field_identity)); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_set_and_get_identity --exact --nocapture +#[test] +fn test_set_and_get_identity() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = 1; + + // Register a hotkey for the coldkey + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Prepare identity data + let name = b"Bob".to_vec(); + let url = b"https://bob.com".to_vec(); + let image = b"bob.jpg".to_vec(); + let discord = b"bob#5678".to_vec(); + let description = b"Bob's identity".to_vec(); + let additional = b"More about Bob".to_vec(); + + // Set identity + assert_ok!(SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + name.clone(), + url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + )); + + // Get and verify identity + let stored_identity = Identities::::get(coldkey).unwrap(); + assert_eq!(stored_identity.name, name); + assert_eq!(stored_identity.url, url); + assert_eq!(stored_identity.image, image); + assert_eq!(stored_identity.discord, discord); + assert_eq!(stored_identity.description, description); + assert_eq!(stored_identity.additional, additional); + + // Update identity + let new_name = b"Bobby".to_vec(); + let new_url = b"https://bobby.com".to_vec(); + assert_ok!(SubtensorModule::do_set_identity( + <::RuntimeOrigin>::signed(coldkey), + new_name.clone(), + new_url.clone(), + image.clone(), + discord.clone(), + description.clone(), + additional.clone() + )); + + // Get and verify updated identity + let updated_identity = Identities::::get(coldkey).unwrap(); + assert_eq!(updated_identity.name, new_name); + assert_eq!(updated_identity.url, new_url); + assert_eq!(updated_identity.image, image); + assert_eq!(updated_identity.discord, discord); + assert_eq!(updated_identity.description, description); + assert_eq!(updated_identity.additional, additional); + + // Verify non-existent identity + let non_existent_coldkey = U256::from(999); + assert!(Identities::::get(non_existent_coldkey).is_none()); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_migrate_set_hotkey_identities --exact --nocapture +#[test] +fn test_migrate_set_hotkey_identities() { + new_test_ext(1).execute_with(|| { + // Run the migration + let weight = + pallet_subtensor::migrations::migrate_chain_identity::migrate_set_hotkey_identities::< + Test, + >(); + + // Assert that the migration has run + assert!(HasMigrationRun::::get( + b"fix_total_coldkey_stake_v7".to_vec() + )); + + // Verify that some identities were set + // Note: This assumes that at least one valid identity was in the JSON file + let mut identity_count = 0; + for (_, _) in Identities::::iter() { + identity_count += 1; + } + assert!( + identity_count > 0, + "No identities were set during migration" + ); + + // Verify that the weight is non-zero + assert!( + weight != Weight::zero(), + "Migration weight should be non-zero" + ); + }); +}