मुख्य सामग्री पर जाएं
Change page

स्मार्ट अनुबंध सुरक्षा

स्मार्ट अनुबंध अत्यधिक लचीले होते हैं, और ब्लॉकचेन पर तैनात कोड के आधार पर अपरिवर्तनीय लॉजिक चलाते हुए बड़ी मात्रा में मूल्य और डेटा को नियंत्रित करने में सक्षम होते हैं। इसने विश्वासहीन और विकेंद्रीकृत एप्लिकेशन का एक जीवंत इकोसिस्टम बनाया है जो पुराने सिस्टम की तुलना में कई लाभ प्रदान करते हैं। वे उन हमलावरों के लिए अवसर भी प्रस्तुत करते हैं जो स्मार्ट अनुबंधों में कमजोरियों का फायदा उठाकर लाभ कमाना चाहते हैं।

सार्वजनिक ब्लॉकचेन, जैसे इथेरियम, स्मार्ट अनुबंधों को सुरक्षित करने के मुद्दे को और जटिल बनाते हैं। तैनात किए गए अनुबंध कोड को आमतौर पर सुरक्षा खामियों को ठीक करने के लिए बदला नहीं जा सकता है, जबकि स्मार्ट अनुबंधों से चुराई गई संपत्तियों को ट्रैक करना बेहद मुश्किल होता है और अपरिवर्तनीयता के कारण वे ज्यादातर अप्राप्य होती हैं।

हालांकि आंकड़े अलग-अलग हैं, यह अनुमान है कि स्मार्ट अनुबंधों में सुरक्षा दोषों के कारण चुराए गए या खोए गए मूल्य की कुल राशि आसानी से $1 बिलियन से अधिक है। इसमें हाई-प्रोफाइल घटनाएं शामिल हैं, जैसे कि DAO हैक (opens in a new tab) (3.6M ETH चुराए गए, जिनकी आज की कीमत $1B से अधिक है), Parity मल्टीसिग वॉलेट हैक (opens in a new tab) (हैकर्स द्वारा $30M का नुकसान), और Parity फ्रोजन वॉलेट समस्या (opens in a new tab) (हमेशा के लिए लॉक किए गए $300M से अधिक के ETH)।

उपर्युक्त समस्याएं डेवलपर्स के लिए सुरक्षित, मजबूत और लचीले स्मार्ट अनुबंध बनाने में प्रयास करना अनिवार्य बनाती हैं। स्मार्ट अनुबंध सुरक्षा एक गंभीर विषय है, और इसे सीखना हर डेवलपर के लिए फायदेमंद होगा। यह मार्गदर्शिका इथेरियम डेवलपर्स के लिए सुरक्षा संबंधी विचारों को कवर करेगी और स्मार्ट अनुबंध सुरक्षा में सुधार के लिए संसाधनों का पता लगाएगी।

पूर्वापेक्षाएँ

सुरक्षा से निपटने से पहले सुनिश्चित करें कि आप स्मार्ट अनुबंध विकास के मूल सिद्धांतों से परिचित हैं।

सुरक्षित इथेरियम स्मार्ट अनुबंध बनाने के लिए दिशानिर्देश

1. उचित एक्सेस कंट्रोल डिज़ाइन करें

स्मार्ट अनुबंधों में, public या external के रूप में चिह्नित फ़ंक्शंस को किसी भी बाहरी स्वामित्व वाले खातों (EOA) या अनुबंध खातों द्वारा कॉल किया जा सकता है। यदि आप चाहते हैं कि अन्य लोग आपके अनुबंध के साथ इंटरैक्ट करें, तो फ़ंक्शंस के लिए सार्वजनिक दृश्यता (public visibility) निर्दिष्ट करना आवश्यक है। हालाँकि, private के रूप में चिह्नित फ़ंक्शंस को केवल स्मार्ट अनुबंध के भीतर के फ़ंक्शंस द्वारा ही कॉल किया जा सकता है, बाहरी खातों द्वारा नहीं। प्रत्येक नेटवर्क प्रतिभागी को अनुबंध फ़ंक्शंस तक पहुंच देने से समस्याएँ हो सकती हैं, खासकर यदि इसका मतलब है कि कोई भी संवेदनशील संचालन (जैसे, नए टोकन की मिंटिंग) कर सकता है।

स्मार्ट अनुबंध फ़ंक्शंस के अनधिकृत उपयोग को रोकने के लिए, सुरक्षित एक्सेस कंट्रोल लागू करना आवश्यक है। एक्सेस कंट्रोल तंत्र एक स्मार्ट अनुबंध में कुछ फ़ंक्शंस का उपयोग करने की क्षमता को स्वीकृत संस्थाओं तक सीमित करते हैं, जैसे कि अनुबंध के प्रबंधन के लिए जिम्मेदार खाते। Ownable पैटर्न और भूमिका-आधारित नियंत्रण (role-based control) स्मार्ट अनुबंधों में एक्सेस कंट्रोल लागू करने के लिए उपयोगी दो पैटर्न हैं:

Ownable पैटर्न

Ownable पैटर्न में, अनुबंध-निर्माण प्रक्रिया के दौरान एक पता को अनुबंध के “मालिक (owner)” के रूप में सेट किया जाता है। संरक्षित फ़ंक्शंस को एक OnlyOwner संशोधक (modifier) सौंपा जाता है, जो यह सुनिश्चित करता है कि अनुबंध फ़ंक्शन को निष्पादित करने से पहले कॉल करने वाले पते की पहचान को प्रमाणित करता है। अनुबंध के मालिक के अलावा अन्य पतों से संरक्षित फ़ंक्शंस पर कॉल हमेशा रिवर्ट हो जाती हैं, जिससे अवांछित पहुंच को रोका जा सकता है।

भूमिका-आधारित एक्सेस कंट्रोल

स्मार्ट अनुबंध में एक ही पते को Owner के रूप में पंजीकृत करने से केंद्रीकरण का जोखिम उत्पन्न होता है और यह विफलता के एकल बिंदु (single point-of-failure) का प्रतिनिधित्व करता है। यदि मालिक के खाते की कुंजियों (keys) से समझौता किया जाता है, तो हमलावर स्वामित्व वाले अनुबंध पर हमला कर सकते हैं। यही कारण है कि कई प्रशासनिक खातों के साथ भूमिका-आधारित एक्सेस कंट्रोल पैटर्न का उपयोग करना एक बेहतर विकल्प हो सकता है।

भूमिका-आधारित एक्सेस कंट्रोल में, संवेदनशील फ़ंक्शंस तक पहुंच विश्वसनीय प्रतिभागियों के एक समूह के बीच वितरित की जाती है। उदाहरण के लिए, एक खाता टोकन की मिंटिंग के लिए जिम्मेदार हो सकता है, जबकि दूसरा खाता अपग्रेड करता है या अनुबंध को रोकता है। इस तरह एक्सेस कंट्रोल को विकेंद्रीकृत करने से विफलता के एकल बिंदु समाप्त हो जाते हैं और उपयोगकर्ताओं के लिए विश्वास मान्यताएँ कम हो जाती हैं।

बहु-हस्ताक्षर वॉलेट का उपयोग करना

सुरक्षित एक्सेस कंट्रोल लागू करने का एक अन्य दृष्टिकोण अनुबंध का प्रबंधन करने के लिए बहु-हस्ताक्षर खाते का उपयोग करना है। एक नियमित EOA के विपरीत, बहु-हस्ताक्षर खातों का स्वामित्व कई संस्थाओं के पास होता है और लेन-देन निष्पादित करने के लिए न्यूनतम संख्या में खातों—मान लीजिए 5 में से 3—से हस्ताक्षर की आवश्यकता होती है।

एक्सेस कंट्रोल के लिए मल्टीसिग का उपयोग करने से सुरक्षा की एक अतिरिक्त परत जुड़ जाती है क्योंकि लक्ष्य अनुबंध पर कार्रवाई के लिए कई पक्षों की सहमति की आवश्यकता होती है। यह विशेष रूप से उपयोगी है यदि Ownable पैटर्न का उपयोग करना आवश्यक है, क्योंकि यह किसी हमलावर या दुष्ट अंदरूनी सूत्र के लिए दुर्भावनापूर्ण उद्देश्यों के लिए संवेदनशील अनुबंध फ़ंक्शंस में हेरफेर करना अधिक कठिन बना देता है।

2. अनुबंध संचालन की सुरक्षा के लिए require(), assert(), और revert() कथनों का उपयोग करें

जैसा कि उल्लेख किया गया है, एक बार ब्लॉकचेन पर तैनात होने के बाद कोई भी आपके स्मार्ट अनुबंध में सार्वजनिक फ़ंक्शंस को कॉल कर सकता है। चूँकि आप पहले से नहीं जान सकते कि बाहरी खाते किसी अनुबंध के साथ कैसे इंटरैक्ट करेंगे, इसलिए तैनाती से पहले समस्याग्रस्त संचालन के खिलाफ आंतरिक सुरक्षा उपाय लागू करना आदर्श है। यदि निष्पादन कुछ आवश्यकताओं को पूरा करने में विफल रहता है, तो आप अपवादों (exceptions) को ट्रिगर करने और स्थिति परिवर्तनों को रिवर्ट करने के लिए require(), assert(), और revert() कथनों का उपयोग करके स्मार्ट अनुबंधों में सही व्यवहार लागू कर सकते हैं।

require(): require फ़ंक्शंस की शुरुआत में परिभाषित किए जाते हैं और यह सुनिश्चित करते हैं कि कॉल किए गए फ़ंक्शन के निष्पादित होने से पहले पूर्व-निर्धारित शर्तें पूरी हों। एक require कथन का उपयोग उपयोगकर्ता इनपुट को मान्य करने, स्थिति चर (state variables) की जांच करने, या किसी फ़ंक्शन के साथ आगे बढ़ने से पहले कॉल करने वाले खाते की पहचान को प्रमाणित करने के लिए किया जा सकता है।

assert(): assert() का उपयोग आंतरिक त्रुटियों का पता लगाने और आपके कोड में “इनवेरिएंट्स (invariants)” के उल्लंघन की जांच करने के लिए किया जाता है। एक इनवेरिएंट किसी अनुबंध की स्थिति के बारे में एक तार्किक दावा है जो सभी फ़ंक्शन निष्पादन के लिए सत्य होना चाहिए। एक उदाहरण इनवेरिएंट किसी टोकन अनुबंध की अधिकतम कुल आपूर्ति या शेष राशि है। assert() का उपयोग यह सुनिश्चित करता है कि आपका अनुबंध कभी भी असुरक्षित स्थिति में न पहुंचे, और यदि ऐसा होता है, तो स्थिति चर में किए गए सभी परिवर्तन वापस ले लिए जाते हैं (rolled back)।

revert(): revert() का उपयोग if-else कथन में किया जा सकता है जो आवश्यक शर्त पूरी न होने पर अपवाद को ट्रिगर करता है। नीचे दिया गया नमूना अनुबंध फ़ंक्शंस के निष्पादन की सुरक्षा के लिए revert() का उपयोग करता है:

3. स्मार्ट अनुबंधों का परीक्षण करें और कोड की शुद्धता सत्यापित करें

इथेरियम वर्चुअल मशीन में चलने वाले कोड की अपरिवर्तनीयता का मतलब है कि स्मार्ट अनुबंध विकास चरण के दौरान उच्च स्तर के गुणवत्ता मूल्यांकन की मांग करते हैं। अपने अनुबंध का बड़े पैमाने पर परीक्षण करना और किसी भी अप्रत्याशित परिणाम के लिए इसका अवलोकन करना सुरक्षा में काफी सुधार करेगा और लंबे समय में आपके उपयोगकर्ताओं की रक्षा करेगा।

सामान्य तरीका मॉक डेटा का उपयोग करके छोटे यूनिट परीक्षण लिखना है जो अनुबंध को उपयोगकर्ताओं से प्राप्त होने की उम्मीद है। यूनिट परीक्षण कुछ फ़ंक्शंस की कार्यक्षमता का परीक्षण करने और यह सुनिश्चित करने के लिए अच्छा है कि एक स्मार्ट अनुबंध अपेक्षा के अनुरूप काम करता है।

दुर्भाग्य से, जब अलग से उपयोग किया जाता है तो स्मार्ट अनुबंध सुरक्षा में सुधार के लिए यूनिट परीक्षण न्यूनतम रूप से प्रभावी होता है। एक यूनिट परीक्षण यह साबित कर सकता है कि एक फ़ंक्शन मॉक डेटा के लिए ठीक से निष्पादित होता है, लेकिन यूनिट परीक्षण केवल उतने ही प्रभावी होते हैं जितने कि लिखे गए परीक्षण। इससे छूटे हुए एज केस (edge cases) और कमजोरियों का पता लगाना मुश्किल हो जाता है जो आपके स्मार्ट अनुबंध की सुरक्षा को तोड़ सकते हैं।

एक बेहतर दृष्टिकोण यूनिट परीक्षण को स्थिर और गतिशील विश्लेषण (static and dynamic analysis) का उपयोग करके किए गए संपत्ति-आधारित परीक्षण (property-based testing) के साथ जोड़ना है। स्थिर विश्लेषण पहुंच योग्य प्रोग्राम स्थितियों और निष्पादन पथों का विश्लेषण करने के लिए निम्न-स्तरीय अभ्यावेदन, जैसे नियंत्रण प्रवाह ग्राफ़ (control flow graphs) (opens in a new tab) और अमूर्त सिंटैक्स ट्री (abstract syntax trees) (opens in a new tab) पर निर्भर करता है। इस बीच, गतिशील विश्लेषण तकनीकें, जैसे स्मार्ट अनुबंध फ़ज़िंग (fuzzing) (opens in a new tab), सुरक्षा गुणों का उल्लंघन करने वाले संचालन का पता लगाने के लिए यादृच्छिक इनपुट मानों के साथ अनुबंध कोड निष्पादित करती हैं।

स्मार्ट अनुबंधों में सुरक्षा गुणों को सत्यापित करने के लिए औपचारिक सत्यापन एक अन्य तकनीक है। नियमित परीक्षण के विपरीत, औपचारिक सत्यापन निर्णायक रूप से एक स्मार्ट अनुबंध में त्रुटियों की अनुपस्थिति को साबित कर सकता है। यह एक औपचारिक विनिर्देश (formal specification) बनाकर प्राप्त किया जाता है जो वांछित सुरक्षा गुणों को कैप्चर करता है और यह साबित करता है कि अनुबंधों का एक औपचारिक मॉडल इस विनिर्देश का पालन करता है।

4. अपने कोड की स्वतंत्र समीक्षा के लिए पूछें

अपने अनुबंध का परीक्षण करने के बाद, दूसरों से किसी भी सुरक्षा समस्या के लिए स्रोत कोड की जांच करने के लिए कहना अच्छा है। परीक्षण एक स्मार्ट अनुबंध में हर खामी को उजागर नहीं करेगा, लेकिन एक स्वतंत्र समीक्षा प्राप्त करने से कमजोरियों को पहचानने की संभावना बढ़ जाती है।

ऑडिट

स्मार्ट अनुबंध ऑडिट शुरू करना एक स्वतंत्र कोड समीक्षा आयोजित करने का एक तरीका है। ऑडिटर यह सुनिश्चित करने में महत्वपूर्ण भूमिका निभाते हैं कि स्मार्ट अनुबंध सुरक्षित हैं और गुणवत्ता दोषों और डिज़ाइन त्रुटियों से मुक्त हैं।

इसके बावजूद, आपको ऑडिट को रामबाण (silver bullet) मानने से बचना चाहिए। स्मार्ट अनुबंध ऑडिट हर बग को नहीं पकड़ेंगे और ज्यादातर समीक्षाओं का एक अतिरिक्त दौर प्रदान करने के लिए डिज़ाइन किए गए हैं, जो प्रारंभिक विकास और परीक्षण के दौरान डेवलपर्स द्वारा छूटे गए मुद्दों का पता लगाने में मदद कर सकते हैं। स्मार्ट अनुबंध ऑडिट के लाभ को अधिकतम करने के लिए आपको ऑडिटर्स के साथ काम करने के लिए सर्वोत्तम प्रथाओं का भी पालन करना चाहिए, जैसे कि कोड का ठीक से दस्तावेजीकरण करना और इनलाइन टिप्पणियां जोड़ना।

बग बाउंटी

बग बाउंटी प्रोग्राम स्थापित करना बाहरी कोड समीक्षाओं को लागू करने का एक और दृष्टिकोण है। बग बाउंटी एक वित्तीय पुरस्कार है जो उन व्यक्तियों (आमतौर पर व्हाइटहैट हैकर्स) को दिया जाता है जो किसी एप्लिकेशन में कमजोरियों की खोज करते हैं।

जब ठीक से उपयोग किया जाता है, तो बग बाउंटी हैकर समुदाय के सदस्यों को महत्वपूर्ण खामियों के लिए आपके कोड का निरीक्षण करने के लिए प्रोत्साहन देती है। एक वास्तविक जीवन का उदाहरण “अनंत धन बग (infinite money bug)” है जो एक हमलावर को इथेरियम पर चलने वाले लेयर 2 (L2) प्रोटोकॉल, ऑप्टिमिज़्म (opens in a new tab) पर असीमित मात्रा में ईथर बनाने देता। सौभाग्य से, एक व्हाइटहैट हैकर ने खामी की खोज की (opens in a new tab) और टीम को सूचित किया, इस प्रक्रिया में एक बड़ा भुगतान अर्जित किया (opens in a new tab)

एक उपयोगी रणनीति दांव पर लगे धन की मात्रा के अनुपात में बग बाउंटी प्रोग्राम का भुगतान निर्धारित करना है। “स्केलिंग बग बाउंटी (opens in a new tab)” के रूप में वर्णित, यह दृष्टिकोण व्यक्तियों को कमजोरियों का फायदा उठाने के बजाय जिम्मेदारी से उनका खुलासा करने के लिए वित्तीय प्रोत्साहन प्रदान करता है।

5. स्मार्ट अनुबंध विकास के दौरान सर्वोत्तम प्रथाओं का पालन करें

ऑडिट और बग बाउंटी का अस्तित्व आपको उच्च-गुणवत्ता वाला कोड लिखने की आपकी जिम्मेदारी से मुक्त नहीं करता है। अच्छी स्मार्ट अनुबंध सुरक्षा उचित डिज़ाइन और विकास प्रक्रियाओं का पालन करने से शुरू होती है:

  • सभी कोड को संस्करण नियंत्रण प्रणाली (version control system) में स्टोर करें, जैसे कि git

  • पुल अनुरोधों (pull requests) के माध्यम से सभी कोड संशोधन करें

  • सुनिश्चित करें कि पुल अनुरोधों में कम से कम एक स्वतंत्र समीक्षक हो—यदि आप किसी प्रोजेक्ट पर अकेले काम कर रहे हैं, तो अन्य डेवलपर्स को खोजने और कोड समीक्षाओं का व्यापार करने पर विचार करें

  • स्मार्ट अनुबंधों के परीक्षण, संकलन, और तैनाती के लिए एक विकास वातावरण (development environment) का उपयोग करें

  • अपने कोड को बुनियादी कोड विश्लेषण टूल, जैसे Cyfrin Aderyn (opens in a new tab), Mythril और स्लिथर के माध्यम से चलाएं। आदर्श रूप से, आपको प्रत्येक पुल अनुरोध के मर्ज होने से पहले ऐसा करना चाहिए और आउटपुट में अंतर की तुलना करनी चाहिए

  • सुनिश्चित करें कि आपका कोड बिना किसी त्रुटि के संकलित होता है, और Solidity कंपाइलर कोई चेतावनी नहीं देता है

  • अपने कोड का ठीक से दस्तावेजीकरण करें (NatSpec (opens in a new tab) का उपयोग करके) और अनुबंध वास्तुकला के बारे में विवरण को समझने में आसान भाषा में वर्णित करें। इससे दूसरों के लिए आपके कोड का ऑडिट और समीक्षा करना आसान हो जाएगा।

6. मजबूत आपदा रिकवरी योजनाएं लागू करें

सुरक्षित एक्सेस कंट्रोल डिज़ाइन करना, फ़ंक्शन संशोधक लागू करना, और अन्य सुझाव स्मार्ट अनुबंध सुरक्षा में सुधार कर सकते हैं, लेकिन वे दुर्भावनापूर्ण कारनामों की संभावना से इंकार नहीं कर सकते हैं। सुरक्षित स्मार्ट अनुबंध बनाने के लिए “विफलता की तैयारी” और हमलों का प्रभावी ढंग से जवाब देने के लिए एक फ़ॉलबैक योजना की आवश्यकता होती है। एक उचित आपदा रिकवरी योजना में निम्नलिखित में से कुछ या सभी घटक शामिल होंगे:

अनुबंध अपग्रेड

जबकि इथेरियम स्मार्ट अनुबंध डिफ़ॉल्ट रूप से अपरिवर्तनीय हैं, अपग्रेड पैटर्न का उपयोग करके कुछ हद तक परिवर्तनशीलता प्राप्त करना संभव है। उन मामलों में अनुबंधों को अपग्रेड करना आवश्यक है जहां एक महत्वपूर्ण खामी आपके पुराने अनुबंध को अनुपयोगी बना देती है और नया लॉजिक तैनात करना सबसे व्यवहार्य विकल्प है।

अनुबंध अपग्रेड तंत्र अलग तरह से काम करते हैं, लेकिन “प्रॉक्सी पैटर्न” स्मार्ट अनुबंधों को अपग्रेड करने के लिए अधिक लोकप्रिय दृष्टिकोणों में से एक है। प्रॉक्सी पैटर्न (opens in a new tab) किसी एप्लिकेशन की स्थिति और लॉजिक को दो अनुबंधों के बीच विभाजित करते हैं। पहला अनुबंध (जिसे 'प्रॉक्सी कॉन्ट्रैक्ट' कहा जाता है) स्थिति चर (जैसे, उपयोगकर्ता शेष) संग्रहीत करता है, जबकि दूसरा अनुबंध (जिसे 'लॉजिक अनुबंध' कहा जाता है) अनुबंध फ़ंक्शंस को निष्पादित करने के लिए कोड रखता है।

खाते प्रॉक्सी कॉन्ट्रैक्ट के साथ इंटरैक्ट करते हैं, जो delegatecall() (opens in a new tab) निम्न-स्तरीय कॉल का उपयोग करके सभी फ़ंक्शन कॉल को लॉजिक अनुबंध पर भेजता है। एक नियमित संदेश कॉल के विपरीत, delegatecall() यह सुनिश्चित करता है कि लॉजिक अनुबंध के पते पर चलने वाला कोड कॉलिंग अनुबंध के संदर्भ में निष्पादित हो। इसका मतलब है कि लॉजिक अनुबंध हमेशा प्रॉक्सी के स्टोरेज (अपने स्वयं के स्टोरेज के बजाय) में लिखेगा और msg.sender और msg.value के मूल मान संरक्षित रहेंगे।

लॉजिक अनुबंध को कॉल सौंपने के लिए प्रॉक्सी कॉन्ट्रैक्ट के स्टोरेज में इसका पता संग्रहीत करने की आवश्यकता होती है। इसलिए, अनुबंध के लॉजिक को अपग्रेड करना केवल एक और लॉजिक अनुबंध तैनात करने और प्रॉक्सी कॉन्ट्रैक्ट में नया पता संग्रहीत करने का मामला है। चूंकि प्रॉक्सी कॉन्ट्रैक्ट के बाद के कॉल स्वचालित रूप से नए लॉजिक अनुबंध पर रूट किए जाते हैं, इसलिए आपने वास्तव में कोड को संशोधित किए बिना अनुबंध को “अपग्रेड” कर लिया होगा।

अनुबंधों को अपग्रेड करने के बारे में अधिक जानकारी

आपातकालीन रोक (Emergency stops)

जैसा कि उल्लेख किया गया है, व्यापक ऑडिटिंग और परीक्षण संभवतः एक स्मार्ट अनुबंध में सभी बग की खोज नहीं कर सकते हैं। यदि तैनाती के बाद आपके कोड में कोई भेद्यता दिखाई देती है, तो इसे पैच करना असंभव है क्योंकि आप अनुबंध पते पर चलने वाले कोड को नहीं बदल सकते हैं। इसके अलावा, अपग्रेड तंत्र (जैसे, प्रॉक्सी पैटर्न) को लागू करने में समय लग सकता है (उन्हें अक्सर विभिन्न पक्षों से अनुमोदन की आवश्यकता होती है), जो केवल हमलावरों को अधिक नुकसान पहुंचाने के लिए अधिक समय देता है।

परमाणु विकल्प (nuclear option) एक “आपातकालीन रोक” फ़ंक्शन को लागू करना है जो किसी अनुबंध में कमजोर फ़ंक्शंस के कॉल को अवरुद्ध करता है। आपातकालीन रोक में आमतौर पर निम्नलिखित घटक शामिल होते हैं:

  1. एक वैश्विक बूलियन चर (global Boolean variable) जो यह दर्शाता है कि स्मार्ट अनुबंध रुकी हुई स्थिति में है या नहीं। अनुबंध सेट करते समय यह चर false पर सेट होता है, लेकिन अनुबंध बंद होने के बाद यह true पर वापस आ जाएगा।

  2. फ़ंक्शंस जो अपने निष्पादन में बूलियन चर को संदर्भित करते हैं। ऐसे फ़ंक्शंस तब सुलभ होते हैं जब स्मार्ट अनुबंध बंद नहीं होता है, और आपातकालीन रोक सुविधा चालू होने पर दुर्गम हो जाते हैं।

  3. एक इकाई जिसकी आपातकालीन रोक फ़ंक्शन तक पहुंच है, जो बूलियन चर को true पर सेट करती है। दुर्भावनापूर्ण कार्यों को रोकने के लिए, इस फ़ंक्शन के कॉल को एक विश्वसनीय पते (जैसे, अनुबंध के मालिक) तक सीमित किया जा सकता है।

एक बार जब अनुबंध आपातकालीन रोक को सक्रिय कर देता है, तो कुछ फ़ंक्शंस कॉल करने योग्य नहीं होंगे। यह चुनिंदा फ़ंक्शंस को एक संशोधक में लपेटकर प्राप्त किया जाता है जो वैश्विक चर को संदर्भित करता है। नीचे एक उदाहरण (opens in a new tab) दिया गया है जो अनुबंधों में इस पैटर्न के कार्यान्वयन का वर्णन करता है:

यह उदाहरण आपातकालीन रोक की बुनियादी विशेषताएं दिखाता है:

  • isStopped एक बूलियन है जो शुरुआत में false और अनुबंध के आपातकालीन मोड में प्रवेश करने पर true का मूल्यांकन करता है।

  • फ़ंक्शन संशोधक onlyWhenStopped और stoppedInEmergency isStopped चर की जांच करते हैं। stoppedInEmergency का उपयोग उन फ़ंक्शंस को नियंत्रित करने के लिए किया जाता है जो अनुबंध के असुरक्षित होने पर दुर्गम होने चाहिए (जैसे, deposit())। इन फ़ंक्शंस के कॉल बस रिवर्ट हो जाएंगे।

onlyWhenStopped का उपयोग उन फ़ंक्शंस के लिए किया जाता है जिन्हें आपात स्थिति के दौरान कॉल करने योग्य होना चाहिए (जैसे, emergencyWithdraw())। ऐसे फ़ंक्शंस स्थिति को हल करने में मदद कर सकते हैं, इसलिए उन्हें “प्रतिबंधित फ़ंक्शंस” सूची से बाहर रखा गया है।

आपातकालीन रोक कार्यक्षमता का उपयोग करना आपके स्मार्ट अनुबंध में गंभीर कमजोरियों से निपटने के लिए एक प्रभावी स्टॉपगैप प्रदान करता है। हालाँकि, यह उपयोगकर्ताओं के लिए डेवलपर्स पर भरोसा करने की आवश्यकता को बढ़ाता है कि वे इसे स्वार्थी कारणों से सक्रिय न करें। इस उद्देश्य के लिए, आपातकालीन रोक के नियंत्रण को विकेंद्रीकृत करना या तो इसे ऑनचेन वोटिंग तंत्र, टाइमलॉक, या मल्टीसिग वॉलेट से अनुमोदन के अधीन करके संभावित समाधान हैं।

घटना निगरानी (Event monitoring)

घटनाएँ (opens in a new tab) आपको स्मार्ट अनुबंध फ़ंक्शंस के कॉल को ट्रैक करने और स्थिति चर में परिवर्तनों की निगरानी करने की अनुमति देती हैं। जब भी कोई पक्ष सुरक्षा-महत्वपूर्ण कार्रवाई (जैसे, धन की निकासी) करता है, तो घटना उत्सर्जित करने के लिए अपने स्मार्ट अनुबंध को प्रोग्राम करना आदर्श है।

घटनाओं को लॉग करना और ऑफचेन उनकी निगरानी करना अनुबंध संचालन पर अंतर्दृष्टि प्रदान करता है और दुर्भावनापूर्ण कार्यों की तेजी से खोज में सहायता करता है। इसका मतलब है कि आपकी टीम हैक का तेजी से जवाब दे सकती है और उपयोगकर्ताओं पर प्रभाव को कम करने के लिए कार्रवाई कर सकती है, जैसे फ़ंक्शंस को रोकना या अपग्रेड करना।

आप एक ऑफ-द-शेल्फ मॉनिटरिंग टूल का विकल्प भी चुन सकते हैं जो जब भी कोई आपके अनुबंधों के साथ इंटरैक्ट करता है तो स्वचालित रूप से अलर्ट अग्रेषित करता है। ये टूल आपको विभिन्न ट्रिगर्स के आधार पर कस्टम अलर्ट बनाने की अनुमति देंगे, जैसे लेन-देन की मात्रा, फ़ंक्शन कॉल की आवृत्ति, या शामिल विशिष्ट फ़ंक्शंस। उदाहरण के लिए, आप एक अलर्ट प्रोग्राम कर सकते हैं जो तब आता है जब एक ही लेन-देन में निकाली गई राशि एक विशेष सीमा को पार कर जाती है।

7. सुरक्षित शासन प्रणाली डिज़ाइन करें

आप समुदाय के सदस्यों को मुख्य स्मार्ट अनुबंधों का नियंत्रण सौंपकर अपने एप्लिकेशन को विकेंद्रीकृत करना चाह सकते हैं। इस मामले में, स्मार्ट अनुबंध प्रणाली में एक शासन मॉड्यूल शामिल होगा—एक तंत्र जो समुदाय के सदस्यों को ऑनचेन शासन प्रणाली के माध्यम से प्रशासनिक कार्यों को स्वीकृति देने की अनुमति देता है। उदाहरण के लिए, एक प्रॉक्सी कॉन्ट्रैक्ट को नए कार्यान्वयन में अपग्रेड करने के प्रस्ताव पर टोकन-धारकों द्वारा वोट किया जा सकता है।

विकेंद्रीकृत शासन फायदेमंद हो सकता है, खासकर क्योंकि यह डेवलपर्स और अंतिम-उपयोगकर्ताओं के हितों को संरेखित करता है। फिर भी, यदि गलत तरीके से लागू किया जाता है तो स्मार्ट अनुबंध शासन तंत्र नए जोखिम पेश कर सकते हैं। एक प्रशंसनीय परिदृश्य यह है कि यदि कोई हमलावर फ्लैश ऋण लेकर भारी मतदान शक्ति (रखे गए टोकन की संख्या में मापा गया) प्राप्त करता है और एक दुर्भावनापूर्ण प्रस्ताव को आगे बढ़ाता है।

ऑनचेन शासन से संबंधित समस्याओं को रोकने का एक तरीका टाइमलॉक का उपयोग करना (opens in a new tab) है। एक टाइमलॉक एक स्मार्ट अनुबंध को कुछ कार्यों को निष्पादित करने से रोकता है जब तक कि एक विशिष्ट समय बीत न जाए। अन्य रणनीतियों में प्रत्येक टोकन को इस आधार पर “वोटिंग वेट (voting weight)” सौंपना शामिल है कि इसे कितने समय के लिए लॉक किया गया है, या वर्तमान ब्लॉक के बजाय ऐतिहासिक अवधि (उदाहरण के लिए, अतीत में 2-3 ब्लॉक) में किसी पते की मतदान शक्ति को मापना शामिल है। दोनों तरीके ऑनचेन वोटों को स्विंग करने के लिए जल्दी से मतदान शक्ति इकट्ठा करने की संभावना को कम करते हैं।

साझा किए गए लिंक में सुरक्षित शासन प्रणाली डिज़ाइन करने (opens in a new tab), DAO में विभिन्न मतदान तंत्र (opens in a new tab), और DeFi का लाभ उठाने वाले सामान्य DAO हमले वैक्टर (opens in a new tab) के बारे में अधिक जानकारी।

8. कोड में जटिलता को न्यूनतम करें

पारंपरिक सॉफ्टवेयर डेवलपर KISS (“keep it simple, stupid”) सिद्धांत से परिचित हैं, जो सॉफ्टवेयर डिज़ाइन में अनावश्यक जटिलता पेश करने के खिलाफ सलाह देता है। यह लंबे समय से चली आ रही सोच का अनुसरण करता है कि “जटिल प्रणालियाँ जटिल तरीकों से विफल होती हैं” और महंगी त्रुटियों के प्रति अधिक संवेदनशील होती हैं।

स्मार्ट अनुबंध लिखते समय चीजों को सरल रखना विशेष महत्व का है, यह देखते हुए कि स्मार्ट अनुबंध संभावित रूप से बड़ी मात्रा में मूल्य को नियंत्रित कर रहे हैं। स्मार्ट अनुबंध लिखते समय सरलता प्राप्त करने के लिए एक टिप मौजूदा लाइब्रेरी का पुन: उपयोग करना है, जैसे कि ओपनजेपेलिन अनुबंध (opens in a new tab), जहां संभव हो। क्योंकि इन लाइब्रेरी का डेवलपर्स द्वारा बड़े पैमाने पर ऑडिट और परीक्षण किया गया है, इसलिए उनका उपयोग करने से खरोंच से नई कार्यक्षमता लिखकर बग पेश करने की संभावना कम हो जाती है।

एक और आम सलाह छोटे फ़ंक्शंस लिखना और कई अनुबंधों में व्यावसायिक लॉजिक को विभाजित करके अनुबंधों को मॉड्यूलर रखना है। सरल कोड लिखने से न केवल एक स्मार्ट अनुबंध में हमले की सतह कम हो जाती है, बल्कि समग्र प्रणाली की शुद्धता के बारे में तर्क करना और संभावित डिज़ाइन त्रुटियों का जल्दी पता लगाना भी आसान हो जाता है।

9. सामान्य स्मार्ट अनुबंध कमजोरियों से बचाव करें

पुन:प्रवेश (Reentrancy)

EVM समवर्ती (concurrency) की अनुमति नहीं देता है, जिसका अर्थ है कि संदेश कॉल में शामिल दो अनुबंध एक साथ नहीं चल सकते हैं। एक बाहरी कॉल कॉलिंग अनुबंध के निष्पादन और मेमोरी को तब तक रोक देता है जब तक कि कॉल वापस नहीं आ जाती, जिस बिंदु पर निष्पादन सामान्य रूप से आगे बढ़ता है। इस प्रक्रिया को औपचारिक रूप से किसी अन्य अनुबंध में नियंत्रण प्रवाह (control flow) (opens in a new tab) स्थानांतरित करने के रूप में वर्णित किया जा सकता है।

हालांकि ज्यादातर हानिरहित, अविश्वसनीय अनुबंधों में नियंत्रण प्रवाह को स्थानांतरित करने से पुन:प्रवेश जैसी समस्याएं हो सकती हैं। पुन:प्रवेश हमला तब होता है जब कोई दुर्भावनापूर्ण अनुबंध मूल फ़ंक्शन आमंत्रण पूरा होने से पहले किसी असुरक्षित अनुबंध में वापस कॉल करता है। इस प्रकार के हमले को एक उदाहरण के साथ सबसे अच्छी तरह समझाया गया है।

एक साधारण स्मार्ट अनुबंध ('Victim') पर विचार करें जो किसी को भी ईथर जमा करने और निकालने की अनुमति देता है:

यह अनुबंध उपयोगकर्ताओं को अनुबंध में पहले जमा किए गए ETH को निकालने की अनुमति देने के लिए एक withdraw() फ़ंक्शन को उजागर करता है। निकासी की प्रक्रिया करते समय, अनुबंध निम्नलिखित कार्य करता है:

  1. उपयोगकर्ता के ETH शेष की जांच करता है
  2. कॉलिंग पते पर धन भेजता है
  3. उनके शेष को 0 पर रीसेट करता है, जिससे उपयोगकर्ता से अतिरिक्त निकासी को रोका जा सके

Victim अनुबंध में withdraw() फ़ंक्शन “checks-interactions-effects” पैटर्न का अनुसरण करता है। यह जांच (checks) करता है कि क्या निष्पादन के लिए आवश्यक शर्तें पूरी होती हैं (यानी, उपयोगकर्ता के पास सकारात्मक ETH शेष है) और लेन-देन के प्रभावों (effects) को लागू करने (यानी, उपयोगकर्ता के शेष को कम करने) से पहले कॉलर के पते पर ETH भेजकर इंटरैक्शन (interaction) करता है।

यदि withdraw() को बाहरी स्वामित्व वाले खाते (EOA) से कॉल किया जाता है, तो फ़ंक्शन अपेक्षा के अनुरूप निष्पादित होता है: msg.sender.call.value() कॉलर को ETH भेजता है। हालाँकि, यदि msg.sender एक स्मार्ट अनुबंध खाता है जो withdraw() को कॉल करता है, तो msg.sender.call.value() का उपयोग करके धन भेजने से उस पते पर संग्रहीत कोड भी चलने के लिए ट्रिगर होगा।

कल्पना करें कि यह अनुबंध पते पर तैनात कोड है:

यह अनुबंध तीन काम करने के लिए डिज़ाइन किया गया है:

  1. किसी अन्य खाते (संभवतः हमलावर के EOA) से जमा स्वीकार करें
  2. Victim अनुबंध में 1 ETH जमा करें
  3. स्मार्ट अनुबंध में संग्रहीत 1 ETH निकालें

यहां कुछ भी गलत नहीं है, सिवाय इसके कि Attacker में एक और फ़ंक्शन है जो Victim में withdraw() को फिर से कॉल करता है यदि आने वाले msg.sender.call.value से बची हुई गैस 40,000 से अधिक है। यह Attacker को Victim में फिर से प्रवेश करने और withdraw के पहले आमंत्रण के पूरा होने से पहले अधिक धन निकालने की क्षमता देता है। चक्र इस तरह दिखता है:

सारांश यह है कि क्योंकि कॉलर का शेष फ़ंक्शन निष्पादन पूरा होने तक 0 पर सेट नहीं होता है, बाद के आमंत्रण सफल होंगे और कॉलर को कई बार अपना शेष निकालने की अनुमति देंगे। इस तरह के हमले का उपयोग स्मार्ट अनुबंध के धन को खत्म करने के लिए किया जा सकता है, जैसा कि 2016 DAO हैक (opens in a new tab) में हुआ था। पुन:प्रवेश हमले आज भी स्मार्ट अनुबंधों के लिए एक महत्वपूर्ण मुद्दा हैं जैसा कि पुन:प्रवेश कारनामों की सार्वजनिक सूची (opens in a new tab) दिखाती है।

पुन:प्रवेश हमलों को कैसे रोकें

पुन:प्रवेश से निपटने का एक दृष्टिकोण checks-effects-interactions पैटर्न (opens in a new tab) का पालन करना है। यह पैटर्न फ़ंक्शंस के निष्पादन को इस तरह से आदेश देता है कि निष्पादन के साथ आगे बढ़ने से पहले आवश्यक जांच करने वाला कोड पहले आता है, उसके बाद अनुबंध की स्थिति में हेरफेर करने वाला कोड आता है, और अन्य अनुबंधों या EOA के साथ इंटरैक्ट करने वाला कोड अंत में आता है।

checks-effect-interaction पैटर्न का उपयोग नीचे दिखाए गए Victim अनुबंध के संशोधित संस्करण में किया गया है:

contract NoLongerAVictim {
    function withdraw() external {
        uint256 amount = balances[msg.sender];
        balances[msg.sender] = 0;
        (bool success, ) = msg.sender.call.value(amount)("");
        require(success);
    }
}

यह अनुबंध उपयोगकर्ता के शेष पर एक जांच (check) करता है, withdraw() फ़ंक्शन के प्रभावों (effects) को लागू करता है (उपयोगकर्ता के शेष को 0 पर रीसेट करके), और इंटरैक्शन (interaction) (उपयोगकर्ता के पते पर ETH भेजना) करने के लिए आगे बढ़ता है। यह सुनिश्चित करता है कि अनुबंध बाहरी कॉल से पहले अपने स्टोरेज को अपडेट करता है, जिससे पुन:प्रवेश की स्थिति समाप्त हो जाती है जिसने पहले हमले को सक्षम किया था। Attacker अनुबंध अभी भी NoLongerAVictim में वापस कॉल कर सकता है, लेकिन चूंकि balances[msg.sender] को 0 पर सेट किया गया है, इसलिए अतिरिक्त निकासी एक त्रुटि फेंक देगी।

एक अन्य विकल्प पारस्परिक बहिष्करण लॉक (mutual exclusion lock) (आमतौर पर "म्यूटेक्स (mutex)" के रूप में वर्णित) का उपयोग करना है जो फ़ंक्शन आमंत्रण पूरा होने तक अनुबंध की स्थिति के एक हिस्से को लॉक कर देता है। इसे एक बूलियन चर का उपयोग करके लागू किया जाता है जिसे फ़ंक्शन निष्पादित होने से पहले true पर सेट किया जाता है और आमंत्रण पूरा होने के बाद false पर वापस आ जाता है। जैसा कि नीचे दिए गए उदाहरण में देखा गया है, म्यूटेक्स का उपयोग किसी फ़ंक्शन को पुनरावर्ती कॉल (recursive calls) से बचाता है जबकि मूल आमंत्रण अभी भी संसाधित हो रहा है, प्रभावी रूप से पुन:प्रवेश को रोकता है।

आप एक पुल भुगतान (pull payments) (opens in a new tab) प्रणाली का भी उपयोग कर सकते हैं जिसके लिए उपयोगकर्ताओं को स्मार्ट अनुबंधों से धन निकालने की आवश्यकता होती है, बजाय "पुश भुगतान (push payments)" प्रणाली के जो खातों में धन भेजती है। यह अज्ञात पतों पर अनजाने में कोड ट्रिगर करने की संभावना को दूर करता है (और कुछ डिनायल-ऑफ-सर्विस (denial-of-service) हमलों को भी रोक सकता है)।

इंटीजर अंडरफ़्लो और ओवरफ़्लो

एक इंटीजर ओवरफ़्लो तब होता है जब किसी अंकगणितीय संक्रिया का परिणाम मानों की स्वीकार्य सीमा से बाहर हो जाता है, जिससे यह सबसे कम प्रस्तुत करने योग्य मान पर "रोल ओवर" हो जाता है। उदाहरण के लिए, एक uint8 केवल 2^8-1=255 तक के मान संग्रहीत कर सकता है। अंकगणितीय संक्रियाएं जिनके परिणामस्वरूप 255 से अधिक मान होते हैं, वे ओवरफ़्लो हो जाएंगे और uint को 0 पर रीसेट कर देंगे, ठीक उसी तरह जैसे कार का ओडोमीटर अधिकतम माइलेज (999999) तक पहुंचने के बाद 0 पर रीसेट हो जाता है।

इंटीजर अंडरफ़्लो समान कारणों से होते हैं: अंकगणितीय संक्रिया का परिणाम स्वीकार्य सीमा से नीचे आ जाता है। मान लीजिए कि आपने uint8 में 0 को कम करने का प्रयास किया, तो परिणाम बस अधिकतम प्रस्तुत करने योग्य मान (255) पर रोल ओवर हो जाएगा।

इंटीजर ओवरफ़्लो और अंडरफ़्लो दोनों अनुबंध के स्थिति चर में अप्रत्याशित परिवर्तन ला सकते हैं और अनियोजित निष्पादन का परिणाम हो सकते हैं। नीचे एक उदाहरण दिया गया है जो दिखाता है कि कैसे एक हमलावर अमान्य संचालन करने के लिए स्मार्ट अनुबंध में अंकगणितीय ओवरफ़्लो का फायदा उठा सकता है:

इंटीजर अंडरफ़्लो और ओवरफ़्लो को कैसे रोकें

संस्करण 0.8.0 के अनुसार, Solidity कंपाइलर उस कोड को अस्वीकार कर देता है जिसके परिणामस्वरूप इंटीजर अंडरफ़्लो और ओवरफ़्लो होते हैं। हालाँकि, कम कंपाइलर संस्करण के साथ संकलित अनुबंधों को या तो अंकगणितीय संक्रियाओं वाले फ़ंक्शंस पर जांच करनी चाहिए या एक लाइब्रेरी (जैसे, SafeMath (opens in a new tab)) का उपयोग करना चाहिए जो अंडरफ़्लो/ओवरफ़्लो की जांच करती है।

ऑरेकल हेरफेर

ऑरेकल ऑफचेन जानकारी प्राप्त करते हैं और इसे स्मार्ट अनुबंधों के उपयोग के लिए ऑनचेन भेजते हैं। ऑरेकल के साथ, आप ऐसे स्मार्ट अनुबंध डिज़ाइन कर सकते हैं जो ऑफचेन सिस्टम, जैसे पूंजी बाजार, के साथ इंटरऑपरेट करते हैं, जिससे उनके एप्लिकेशन का काफी विस्तार होता है।

लेकिन अगर ऑरेकल दूषित हो जाता है और ऑनचेन गलत जानकारी भेजता है, तो स्मार्ट अनुबंध गलत इनपुट के आधार पर निष्पादित होंगे, जिससे समस्याएँ हो सकती हैं। यह “ओरेकल समस्या” का आधार है, जो यह सुनिश्चित करने के कार्य से संबंधित है कि ब्लॉकचेन ओरेकल से जानकारी सटीक, अद्यतित और समय पर हो।

एक संबंधित सुरक्षा चिंता किसी संपत्ति के लिए स्पॉट मूल्य प्राप्त करने के लिए ऑनचेन ऑरेकल, जैसे विकेंद्रीकृत एक्सचेंज, का उपयोग करना है। विकेंद्रीकृत वित्त (DeFi) उद्योग में ऋण देने वाले प्लेटफ़ॉर्म अक्सर उपयोगकर्ता के संपार्श्विक के मूल्य को निर्धारित करने के लिए ऐसा करते हैं ताकि यह निर्धारित किया जा सके कि वे कितना उधार ले सकते हैं।

DEX कीमतें अक्सर सटीक होती हैं, जिसका मुख्य कारण आर्बिट्रेजर्स (arbitrageurs) द्वारा बाजारों में समानता बहाल करना है। हालाँकि, वे हेरफेर के लिए खुले हैं, खासकर यदि ऑनचेन ऑरेकल ऐतिहासिक ट्रेडिंग पैटर्न के आधार पर संपत्ति की कीमतों की गणना करता है (जैसा कि आमतौर पर होता है)।

उदाहरण के लिए, एक हमलावर आपके ऋण देने वाले अनुबंध के साथ इंटरैक्ट करने से ठीक पहले फ्लैश ऋण लेकर कृत्रिम रूप से किसी संपत्ति के स्पॉट मूल्य को बढ़ा सकता है। संपत्ति की कीमत के लिए DEX से पूछताछ करने पर सामान्य से अधिक मूल्य वापस आएगा (हमलावर के बड़े “खरीद आदेश (buy order)” के कारण संपत्ति की मांग में वृद्धि), जिससे उन्हें अपनी क्षमता से अधिक उधार लेने की अनुमति मिलेगी। इस तरह के "फ्लैश ऋण हमलों" का उपयोग DeFi एप्लिकेशनों के बीच मूल्य ऑरेकल पर निर्भरता का फायदा उठाने के लिए किया गया है, जिससे प्रोटोकॉल को लाखों का नुकसान हुआ है।

ऑरेकल हेरफेर को कैसे रोकें

ऑरेकल हेरफेर से बचने (opens in a new tab) के लिए न्यूनतम आवश्यकता एक विकेंद्रीकृत ऑरेकल नेटवर्क का उपयोग करना है जो विफलता के एकल बिंदुओं से बचने के लिए कई स्रोतों से जानकारी प्राप्त करता है। ज्यादातर मामलों में, विकेंद्रीकृत ऑरेकल में ऑरेकल नोड्स को सही जानकारी रिपोर्ट करने के लिए प्रोत्साहित करने के लिए अंतर्निहित क्रिप्टोइकोनॉमिक प्रोत्साहन होते हैं, जिससे वे केंद्रीकृत ऑरेकल की तुलना में अधिक सुरक्षित हो जाते हैं।

यदि आप संपत्ति की कीमतों के लिए ऑनचेन ऑरेकल से पूछताछ करने की योजना बना रहे हैं, तो ऐसे ऑरेकल का उपयोग करने पर विचार करें जो समय-भारित औसत मूल्य (time-weighted average price - TWAP) तंत्र लागू करता है। एक TWAP ऑरेकल (opens in a new tab) दो अलग-अलग समय बिंदुओं (जिन्हें आप संशोधित कर सकते हैं) पर किसी संपत्ति की कीमत पूछता है और प्राप्त औसत के आधार पर स्पॉट मूल्य की गणना करता है। लंबी समयावधि चुनना आपके प्रोटोकॉल को मूल्य हेरफेर से बचाता है क्योंकि हाल ही में निष्पादित बड़े आदेश संपत्ति की कीमतों को प्रभावित नहीं कर सकते हैं।