Trending News

BTC
$23,416.10
-0.06
ETH
$1,680.57
+0.76
LTC
$99.17
-0.6
DASH
$61.83
-1.36
XMR
$173.10
+0.7
NXT
$0.00
-0.06
ETC
$24.25
+4.66

स्मार्ट अनुबंध सुरक्षा | एथेरियम फाउंडेशन ब्लॉग

0


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

मैं यहां छोटी-मोटी समस्याओं के बारे में बात नहीं करूंगा, कृपया उन्हें इस लेख में पढ़ें प्रलेखन.

ईथर भेजा जा रहा है

ईथर को भेजना सॉलिडिटी में सबसे सरल चीजों में से एक माना जाता है, लेकिन यह पता चला है कि इसमें कुछ बारीकियां हैं जो ज्यादातर लोगों को पता नहीं है।

यह महत्वपूर्ण है कि अधिक से अधिक ईथर के प्राप्तकर्ता पेआउट की शुरुआत करें। निम्नलिखित एक है खराब नीलामी अनुबंध का उदाहरण:

// THIS IS A NEGATIVE EXAMPLE! DO NOT USE!
contract auction {
  address highestBidder;
  uint highestBid;
  function bid() {
    if (msg.value < highestBid) throw;
    if (highestBidder != 0)
      highestBidder.send(highestBid); // refund previous bidder
    highestBidder = msg.sender;
    highestBid = msg.value;
  }
}

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

/// THIS IS STILL A NEGATIVE EXAMPLE! DO NOT USE!
if (highestBidder != 0)
  if (!highestBidder.send(highestBid))
    throw;

throw

बयान वर्तमान कॉल को वापस करने का कारण बनता है। यह एक बुरा विचार है, क्योंकि प्राप्तकर्ता, उदाहरण के लिए फ़ॉलबैक फ़ंक्शन को कार्यान्वित करके

function() { throw; }

हमेशा ईथर हस्तांतरण को विफल करने के लिए मजबूर कर सकता है और इसका प्रभाव यह होगा कि कोई भी उससे अधिक बोली नहीं लगा सकता है।

दोनों स्थितियों को रोकने का एकमात्र तरीका प्राप्तकर्ता को हस्तांतरण पर नियंत्रण देकर भेजने के पैटर्न को वापस लेने के पैटर्न में परिवर्तित करना है:

/// THIS IS STILL A NEGATIVE EXAMPLE! DO NOT USE!
contract auction {
  address highestBidder;
  uint highestBid;
  mapping(address => uint) refunds;
  function bid() {
    if (msg.value < highestBid) throw;
    if (highestBidder != 0)
      refunds[highestBidder] += highestBid;
    highestBidder = msg.sender;
    highestBid = msg.value;
  }
  function withdrawRefund() {
    if (msg.sender.send(refunds[msg.sender]))
      refunds[msg.sender] = 0;
  }
}
 

यह अभी भी अनुबंध के ऊपर “नकारात्मक उदाहरण” क्यों कहता है? गैस यांत्रिकी के कारण, अनुबंध वास्तव में ठीक है, लेकिन यह अभी भी एक अच्छा उदाहरण नहीं है। कारण यह है कि भेजने के भाग के रूप में प्राप्तकर्ता पर कोड निष्पादन को रोकना असंभव है। इसका मतलब यह है कि जब सेंड फंक्शन अभी भी प्रगति पर है, तो प्राप्तकर्ता रिफंड वापस लेने के लिए कॉल कर सकता है। उस समय, धनवापसी राशि अभी भी वही है और इस प्रकार उन्हें राशि फिर से मिल जाएगी और इसी तरह। इस विशिष्ट उदाहरण में, यह काम नहीं करता है, क्योंकि प्राप्तकर्ता को केवल गैस स्टाइपेंड (2100 गैस) मिलता है और इस गैस की मात्रा के साथ दूसरा प्रेषण करना असंभव है। हालांकि, निम्न कोड इस हमले के लिए असुरक्षित है: msg.sender.call.value (धनवापसी[msg.sender])().

इस सब पर विचार करने के बाद, निम्नलिखित कोड ठीक होना चाहिए (बेशक यह अभी भी नीलामी अनुबंध का पूर्ण उदाहरण नहीं है):

contract auction {
  address highestBidder;
  uint highestBid;
  mapping(address => uint) refunds;
  function bid() {
    if (msg.value < highestBid) throw;
    if (highestBidder != 0)
      refunds[highestBidder] += highestBid;
    highestBidder = msg.sender;
    highestBid = msg.value;
  }
  function withdrawRefund() {
    uint refund = refunds[msg.sender];
    refunds[msg.sender] = 0;
    if (!msg.sender.send(refund))
     refunds[msg.sender] = refund;
  }
}

ध्यान दें कि हमने असफल प्रेषण पर फेंक का उपयोग नहीं किया क्योंकि हम सभी राज्य परिवर्तनों को मैन्युअल रूप से वापस करने में सक्षम हैं और फेंक का उपयोग न करने से बहुत कम दुष्प्रभाव होते हैं।

थ्रो का उपयोग करना

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

1. ईथर हस्तांतरण को वर्तमान कार्य में वापस लाएं

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

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

2. बुलाए गए कार्यों के प्रभावों को वापस करें

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

लूप्स और ब्लॉक गैस लिमिट

एक ब्लॉक में कितनी गैस खर्च की जा सकती है, इसकी एक सीमा होती है। यह सीमा लचीली है, लेकिन इसे बढ़ाना काफी कठिन है। इसका मतलब यह है कि आपके अनुबंध में हर एक कार्य सभी (उचित) स्थितियों में गैस की एक निश्चित मात्रा से नीचे रहना चाहिए। निम्नलिखित मतदान अनुबंध का एक खराब उदाहरण है:

/// THIS IS STILL A NEGATIVE EXAMPLE! DO NOT USE!
contract Voting {
  mapping(address => uint) voteWeight;
  address[] yesVotes;
  uint requiredWeight;
  address beneficiary;
  uint amount;
  function voteYes() { yesVotes.push(msg.sender); }
  function tallyVotes() {
    uint yesVotes;
    for (uint i = 0; i < yesVotes.length; ++i)
      yesVotes += voteWeight[yesVotes[i]];
    if (yesVotes > requiredWeight)
      beneficiary.send(amount);
  }
}

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

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

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

ईथर / फ़ॉलबैक फ़ंक्शन प्राप्त करना

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



Source link

Leave A Reply

Your email address will not be published.

Shares