यह वास्तव में स्मार्ट अनुबंधों के बारे में है।
लेकिन अगर आप काफी कल्पना नहीं करते हैं कि एक स्मार्ट अनुबंध क्या है, और सामान्य तौर पर क्रिप्टो से दूर हैं, तो एक डेटाबेस में संग्रहीत प्रक्रिया क्या है, आप पूरी तरह से कल्पना कर सकते हैं। उपयोगकर्ता कोड के टुकड़े बनाता है जो तब हमारे सर्वर पर काम करता है। उपयोगकर्ता के लिए उन्हें लिखना और प्रकाशित करना सुविधाजनक है, और हमारे लिए उन्हें निष्पादित करना सुरक्षित है।
दुर्भाग्य से, हमने अभी तक सुरक्षा विकसित नहीं की है, इसलिए अब मैं इसका वर्णन नहीं करूंगा, लेकिन मैं कुछ संकेत दूंगा।
हम गो पर भी लिखते हैं, और इसका रनटाइम कुछ बहुत ही विशिष्ट प्रतिबंध लगाता है, जिनमें से मुख्य एक है, द्वारा और बड़े, हम जाने पर नहीं लिखे गए किसी अन्य प्रोजेक्ट से लिंक नहीं कर सकते हैं, यह हर बार जब हम तीसरे पक्ष के कोड को निष्पादित करते हैं तो हमारा रनटाइम रोक देगा। सामान्य तौर पर, हमारे पास कुछ प्रकार के दुभाषिया का उपयोग करने का विकल्प होता है, जिसके लिए हमें पूरी तरह से समझदार लुआ और पूरी तरह से सेंस WASM मिलता है, लेकिन किसी तरह मैं लुआ में ग्राहकों को जोड़ना नहीं चाहता, लेकिन WASM के साथ अब लाभ की तुलना में अधिक समस्याएं हैं, यह एक मसौदा राज्य में है , जो हर महीने अपडेट किया जाता है, इसलिए हम तब तक इंतजार करेंगे जब तक कि स्पेसिफिकेशन निपट नहीं जाता। हम इसे दूसरे इंजन के रूप में उपयोग करते हैं।
अपने स्वयं के विवेक के साथ लंबी लड़ाई के परिणामस्वरूप, गो पर स्मार्ट अनुबंध लिखने का निर्णय लिया गया था। तथ्य यह है कि यदि आप संकलित जीओ कोड को निष्पादित करने के लिए वास्तुकला का निर्माण करते हैं, तो आपको इस निष्पादन को एक अलग प्रक्रिया में स्थानांतरित करना होगा, जैसा कि आपको याद है, सुरक्षा के लिए, और एक अलग प्रक्रिया में स्थानांतरित करना आईपीसी पर प्रदर्शन का नुकसान है, हालांकि भविष्य में, जब हम निष्पादन योग्य की मात्रा को समझते हैं। कोड, यह किसी भी तरह सुखद था कि हमने इस समाधान को चुना। बात यह है कि यह स्केलेबल है, हालांकि यह प्रत्येक व्यक्तिगत कॉल में देरी जोड़ता है। हम कई रिमोट रनटाइम बढ़ा सकते हैं।
किए गए निर्णयों के बारे में थोड़ा और अधिक ताकि यह स्पष्ट हो। प्रत्येक स्मार्ट कॉन्ट्रैक्ट में दो भाग होते हैं, एक भाग क्लास कोड होता है, और दूसरा ऑब्जेक्ट डेटा होता है, इसलिए उसी कोड पर, जब हम कोड प्रकाशित करते हैं, तो कई कॉन्ट्रैक्ट बनाते हैं जो मूल रूप से समान व्यवहार करेंगे, लेकिन अलग-अलग सेटिंग्स के साथ , और एक अलग राज्य के साथ। यदि हम आगे बात करते हैं, तो यह पहले से ही ब्लॉकचेन के बारे में है न कि इस कहानी का विषय।
और इसलिए, हम GO को निष्पादित करते हैं
हमने प्लगइन तंत्र का उपयोग करने का फैसला किया, जो न केवल तैयार और अच्छा है। वह निम्नलिखित करता है, हम संकलित करते हैं कि एक साझा पुस्तकालय में एक विशेष तरीके से एक प्लगइन क्या होगा, और फिर इसे लोड करें, इसमें प्रतीकों को ढूंढें और वहां निष्पादन पास करें। लेकिन पकड़ यह है कि GO के पास एक रनटाइम है, और यह लगभग मेगाबाइट कोड है, और डिफ़ॉल्ट रूप से यह रनटाइम भी इस लाइब्रेरी में जा रहा है, और हमारे पास हर जगह एक raznipipenny रनटाइम है। लेकिन अब हमने इसके लिए जाने का फैसला किया, यह सुनिश्चित करते हुए कि हम भविष्य में इसे हरा सकते हैं।
जब आप अपने पुस्तकालय का निर्माण करते हैं तो सब कुछ सरल होता है, आप इसे कुंजी - buildmode = प्लगइन के साथ बनाते हैं और .so फ़ाइल प्राप्त करते हैं, जिसे आप तब खोलते हैं।
p, err := plugin.Open(path)
आप जिस चरित्र में रुचि रखते हैं, उसकी तलाश में:
symbol, err := p.Lookup(Method)
और अब, इस पर निर्भर करता है कि चर एक फ़ंक्शन या फ़ंक्शन है, आप या तो इसे कॉल करते हैं या इसे एक चर के रूप में उपयोग करते हैं।
इस तंत्र के हुड के तहत एक सरल dlopen (3) है, हम पुस्तकालय को लोड करते हैं, जांचें कि यह एक प्लगइन है और इसके ऊपर आवरण दें, जब आवरण बनाते हैं, तो सभी निर्यात किए गए अक्षर इंटरफ़ेस {} में लिपटे होते हैं और संग्रहीत होते हैं। यदि यह एक फ़ंक्शन है, तो इसे सही प्रकार के फ़ंक्शन में घटाया जाना चाहिए और बस कहा जाता है, यदि चर - तो चर की तरह काम करते हैं।
याद रखने वाली मुख्य बात यह है कि यदि कोई प्रतीक एक चर है, तो यह पूरी प्रक्रिया के दौरान वैश्विक है और आप इसे बिना सोचे समझे उपयोग नहीं कर सकते।
यदि प्लगइन में एक प्रकार घोषित किया गया है, तो यह इस प्रकार को एक अलग पैकेज में रखने के लिए समझ में आता है ताकि मुख्य प्रक्रिया इसके साथ काम कर सके, उदाहरण के लिए, प्लगइन के कार्यों के लिए तर्क के रूप में। यह वैकल्पिक है, आप प्रतिबिंब को भाप और उपयोग नहीं कर सकते।
हमारे अनुबंध संबंधित "वर्ग" की वस्तुएं हैं, और शुरुआत में इस वस्तु का उदाहरण हमारे निर्यात चर में संग्रहीत किया गया था, इसलिए हम एक और समान चर बना सकते हैं:
export, err := p.Lookup("EXPORT") obj := reflect.New(reflect.ValueOf(export).Elem().Type()).Interface()
और पहले से ही सही प्रकार के इस स्थानीय चर के अंदर, वस्तु की स्थिति का deserialize। ऑब्जेक्ट को पुनर्स्थापित करने के बाद, हम उस पर तरीकों को कॉल कर सकते हैं। जिसके बाद ऑब्जेक्ट को सीरियल किया जाता है और स्टोर में वापस जोड़ा जाता है, चियर्स हमने अनुबंध पर विधि को बुलाया।
यदि आप इसमें रुचि रखते हैं, लेकिन प्रलेखन पढ़ने में बहुत आलसी हैं, तो:
method := reflect.ValueOf(obj).MethodByName(Method) res:= method.Call(in)
बीच में, आपको सही प्रकार के तर्क वाले खाली इंटरफेस के साथ सरणी को भरना होगा, यदि आप रुचि रखते हैं, तो अपने आप को देखें कि यह कैसे किया गया था, स्रोत खुले हैं, हालांकि
इतिहास में इस स्थान को ढूंढना मुश्किल होगा।
सामान्य तौर पर, हमारे लिए सब कुछ काम करता है, आप एक वर्ग की तरह कुछ के साथ कोड लिख सकते हैं, इसे ब्लॉकचेन पर रख सकते हैं, ब्लॉकचेन पर फिर से इस वर्ग का अनुबंध बना सकते हैं, इस पर एक विधि कॉल कर सकते हैं और अनुबंध की नई स्थिति को ब्लॉकचेन पर वापस लिखा जाता है। बहुत बढ़िया! हाथ पर कोड के साथ एक नया अनुबंध कैसे बनाएं? बहुत आसान है, हमारे पास निर्माण कार्य हैं जो एक नई बनाई गई वस्तु को लौटाते हैं, जो कि नया अनुबंध है। अब तक, सब कुछ प्रतिबिंब के माध्यम से काम करता है और उपयोगकर्ता को लिखना होगा:
var EXPORT ContractType
ताकि हम जान सकें कि प्रतीक किस अनुबंध का प्रतिनिधित्व है, और वास्तव में इसका उपयोग एक टेम्पलेट के रूप में किया जाता है।
हम वास्तव में इसे पसंद नहीं करते हैं और हमने कड़ी टक्कर दी।
पदच्छेद
सबसे पहले, उपयोगकर्ता को कुछ भी शानदार नहीं लिखना चाहिए, और दूसरी बात, हमारे पास यह विचार है कि अनुबंध के साथ अनुबंध की बातचीत सरल होनी चाहिए, और ब्लॉकचैन को ऊपर उठाए बिना परीक्षण किया गया, ब्लॉकचेन धीमा और कठिन है।
इसलिए, हमने अनुबंध को एक आवरण में लपेटने का फैसला किया, जो कि अनुबंध और आवरण टेम्पलेट के आधार पर, सिद्धांत रूप में, एक समझने योग्य समाधान है। सबसे पहले, आवरण हमारे लिए एक निर्यात वस्तु बनाता है, और दूसरी बात, यह उस पुस्तकालय को बदल देता है जिसके साथ अनुबंध किया जाता है जब उपयोगकर्ता अनुबंध लिखता है, नींव पुस्तकालय का उपयोग मोक के साथ किया जाता है, और जब अनुबंध प्रकाशित होता है, तो इसे एक मुकाबला से बदल दिया जाता है जो ब्लॉकचेन के साथ काम करता है। ।
आरंभ करने के लिए, आपको कोड को पार्स करने और समझने की आवश्यकता है कि हमारे पास आम तौर पर क्या है, इसके चारों ओर एक आवरण उत्पन्न करने के लिए बेसकॉन्ट्रैक्ट से विरासत में मिली संरचना को ढूंढें।
यह काफी सरलता से किया जाता है, हम फाइल को [] बाइट में कोड के साथ पढ़ते हैं, हालांकि पार्सर खुद ही फाइलों को पढ़ सकता है, यह पाठ कहीं अच्छा है कि सभी एएसटी तत्व संदर्भित करते हैं, वे फाइल में बाइट संख्या को संदर्भित करते हैं, और भविष्य में हम प्राप्त करना चाहते हैं। संरचना कोड के रूप में यह है, हम बस कुछ पसंद करते हैं।
func (pf *ParsedFile) codeOfNode(n ast.Node) string { return string(pf.code[n.Pos()-1 : n.End()-1]) }
हम वास्तव में फ़ाइल को पार्स करते हैं और सबसे ऊपर एएसटी नोड प्राप्त करते हैं जिससे हम फ़ाइल को क्रॉल करेंगे।
fileSet = token.NewFileSet() node, err := parser.ParseFile(fileSet, name, code, parser.ParseComments)
अगला, हम शीर्ष नोड से शुरू होने वाले कोड के चारों ओर जाते हैं, और एक अलग संरचना में सब कुछ दिलचस्प इकट्ठा करते हैं।
for _, decl := range node.Decls { switch d := decl.(type) { case *ast.GenDecl: … case *ast.FuncDecl: … } }
दशमलव, यह पहले से ही एक सरणी में दर्ज हो गया है, फ़ाइल में परिभाषित हर चीज की एक सूची है, लेकिन यह Decl इंटरफेस की एक सरणी है जो यह नहीं बताती है कि अंदर क्या है, इसलिए प्रत्येक तत्व को एक विशिष्ट प्रकार में डाला जाना चाहिए, यहां भाषा के लेखक इंटरफेस का उपयोग करने के अपने विचार से प्रस्थान कर गए हैं गो / अस्ट में इंटरफ़ेस बल्कि एक बेस क्लास है।
हम GenDecl और FuncDecl के प्रकार में रुचि रखते हैं। GenDecl एक चर या प्रकार की परिभाषा है, और आपको यह जांचने की आवश्यकता है कि वास्तव में अंदर का प्रकार क्या है, और एक बार फिर इसे TypeDecl प्रकार पर डालें जिसे आप पहले से काम कर सकते हैं। FuncDecl सरल है - यह एक फ़ंक्शन है, और यदि इसमें Recv फ़ील्ड भरा है, तो यह संबंधित संरचना का एक तरीका है। हम यह सब सामान एक सुविधाजनक भंडारण में इकट्ठा करते हैं, क्योंकि तब हम पाठ / टेम्पलेट का उपयोग करते हैं, और इसमें बहुत अधिक अभिव्यंजक शक्ति नहीं होती है।
केवल एक चीज जिसे हमें अलग से याद रखने की आवश्यकता है, वह उस प्रकार का नाम है जो बेसकॉन्ट्रैक्ट से विरासत में मिला है, और हम इसके साथ नृत्य करने जा रहे हैं।
कोड जनरेशन
और इसलिए, हम उन सभी प्रकारों और कार्यों को जानते हैं जो हमारे अनुबंध में हैं और हमें आने वाली विधि के नाम और तर्क के क्रमबद्ध सरणी से किसी ऑब्जेक्ट पर विधि कॉल करने में सक्षम होने की आवश्यकता है। लेकिन आखिरकार, कोड पीढ़ी के समय, हम अनुबंध के पूरे उपकरण को जानते हैं, इसलिए हम अपनी अनुबंध फ़ाइल के बगल में किसी अन्य फ़ाइल के पास रखते हैं, उसी पैकेज के नाम के साथ, जिसमें हम सभी आवश्यक आयात डालते हैं, प्रकार पहले से ही मुख्य फ़ाइल में परिभाषित होते हैं और अनावश्यक होते हैं।
और यहां मुख्य बात है, कार्यों पर आवरण। आवरण के नाम को किसी प्रकार के उपसर्ग द्वारा पूरक किया गया है और अब आवरण को खोजना आसान है।
symbol, err := p.Lookup("INSMETHOD_" + Method) wrapper, ok := symbol.(func(ph proxyctx.ProxyHelper, object []byte, data []byte) (object []byte, result []byte, err error))
प्रत्येक आवरण में एक ही हस्ताक्षर होते हैं, इसलिए जब हम इसे मुख्य कार्यक्रम से बुलाते हैं, तो हमें अतिरिक्त प्रतिबिंबों की आवश्यकता नहीं होती है, केवल एक चीज यह है कि फ़ंक्शन रैपर विधि के रैपर से भिन्न होते हैं, वे प्राप्त नहीं करते हैं और ऑब्जेक्ट की स्थिति वापस नहीं करते हैं।
रैपर के अंदर क्या है?
हम फ़ंक्शन के तर्कों के अनुरूप खाली चर का एक सरणी बनाते हैं, इसे एक प्रकार के इंटरफ़ेस के एक चर में डालते हैं, और इसमें तर्कों का वर्णन करते हैं, यदि हम एक विधि हैं, तो हमें ऑब्जेक्ट की स्थिति को क्रमबद्ध करना चाहिए, आम तौर पर ऐसा कुछ:
{{ range $method := .Methods }} func INSMETHOD_{{ $method.Name }}(ph proxyctx.ProxyHelper, object []byte, data []byte) ([]byte, []byte, error) { self := new({{ $.ContractType }}) err := ph.Deserialize(object, self) if err != nil { return nil, nil, err } {{ $method.ArgumentsZeroList }} err = ph.Deserialize(data, &args) if err != nil { return nil, nil, err } {{ if $method.Results }} {{ $method.Results }} := self.{{ $method.Name }}( {{ $method.Arguments }} ) {{ else }} self.{{ $method.Name }}( {{ $method.Arguments }} ) {{ end }} state := []byte{} err = ph.Serialize(self, &state) if err != nil { return nil, nil, err } {{ range $i := $method.ErrorInterfaceInRes }} ret{{ $i }} = ph.MakeErrorSerializable(ret{{ $i }}) {{ end }} ret := []byte{} err = ph.Serialize([]interface{} { {{ $method.Results }} }, &ret) return state, ret, err } {{ end }}
एक चौकस पाठक को इस बात में दिलचस्पी होगी कि प्रॉक्सी हेल्पर क्या है? - यह ऐसी कॉम्बिनेशन ऑब्जेक्ट है, जिसकी हमें अभी भी आवश्यकता है, लेकिन अब हम इसकी क्षमता का उपयोग क्रमबद्ध और डिसेरिएलाइज करने के लिए करते हैं।
खैर, कोई भी पढ़ेगा, पूछेगा, "लेकिन ये आपके तर्क हैं, वे कहाँ से हैं?" यहाँ एक समझने योग्य उत्तर भी है, हाँ पाठ / टेम्पलेट आकाश से पर्याप्त तारे नहीं हैं, इसीलिए हम इन पंक्तियों की गणना कोड में करते हैं, और टेम्पलेट में नहीं।
मेथड.आर्गुमेंट्सजेरो लिस्ट में कुछ ऐसा है
var arg0 int = 0 Var arg1 string = “” Var arg2 ackwardType = ackwardType{} Args := []interface{}{&arg0, &arg1, &arg2}
और तर्क के अनुसार "arg0, arg1, arg2" शामिल हैं।
इस प्रकार, हम जो कुछ भी चाहते हैं, उसे किसी भी हस्ताक्षर के साथ कह सकते हैं।
लेकिन हम किसी भी उत्तर को क्रमबद्ध नहीं कर सकते हैं, तथ्य यह है कि धारावाहिक प्रतिबिंब के साथ काम करते हैं, और यह संरचनाओं के अस्पष्टीकृत क्षेत्रों तक पहुंच नहीं देता है, यही कारण है कि हमारे पास एक विशेष प्रॉक्सी सहायक विधि है जो एक त्रुटि इंटरफ़ेस ऑब्जेक्ट लेती है और इसके लिए प्रकार नींव का एक ऑब्जेक्ट बनाती है। त्रुटि, जो सामान्य से भिन्न होती है कि त्रुटि पाठ निर्यात क्षेत्र में है, और हम इसे क्रमबद्ध कर सकते हैं, कुछ नुकसान के साथ।
लेकिन अगर हम एक कोड-जनरेटिंग स्टेरलाइज़र का उपयोग करते हैं, तो हमें इसकी आवश्यकता भी नहीं है, हम एक ही पैकेज में संकलित हैं, हमारे पास गैर-निर्यात वाले क्षेत्रों तक पहुंच है।
लेकिन क्या होगा अगर हम एक अनुबंध से एक अनुबंध को कॉल करना चाहते हैं?
आप समस्या की गहराई को नहीं समझते हैं यदि आपको लगता है कि अनुबंध से अनुबंध को कॉल करना आसान है। तथ्य यह है कि किसी अन्य अनुबंध की वैधता की पुष्टि सर्वसम्मति से होनी चाहिए और इस कॉल के तथ्य को ब्लॉकचैन पर हस्ताक्षर करना चाहिए, सामान्य रूप से, बस दूसरे अनुबंध के साथ संकलन करना और इसकी विधि को लागू करना काम नहीं करेगा, हालांकि मैं वास्तव में चाहता हूं। लेकिन हम प्रोग्रामर के दोस्त हैं, इसलिए हमें उन्हें सीधे सब कुछ करने का अवसर देना चाहिए, और सिस्टम के हुड के नीचे सभी चालें छिपानी चाहिए। इस प्रकार, अनुबंध का विकास ऐसा है जैसे कि प्रत्यक्ष कॉल के साथ, और अनुबंध एक-दूसरे को पारदर्शी रूप से खींचते हैं, लेकिन जब हम प्रकाशन के लिए अनुबंध एकत्र करते हैं, तो हम दूसरे अनुबंध के बजाय एक प्रॉक्सी को पर्ची करते हैं, जो केवल अनुबंध के बारे में अपना पता और हस्ताक्षर बताता है।
यह सब कैसे व्यवस्थित करें? - हमें एक विशेष निर्देशिका में अन्य अनुबंधों को संग्रहीत करना होगा जिसे हमारा जनरेटर आयात किए गए प्रत्येक अनुबंध के लिए प्रॉक्सी को पहचानने और बनाने में सक्षम होगा।
अगर हम मिले तो यह है:
import “ContractsDir/ContractAddress"
हम इसे आयातित अनुबंधों की सूची में लिखते हैं।
वैसे, इसके लिए आपको अनुबंध के स्रोत कोड को जानने की आवश्यकता नहीं है, आपको बस उस विवरण को जानना होगा जो हमने पहले ही एकत्र किया है, इसलिए यदि हम ऐसा विवरण कहीं प्रकाशित करते हैं, और सभी कॉल मुख्य प्रणाली से गुजरते हैं, तो हमें परवाह नहीं है कि क्या भाषा में एक और अनुबंध लिखा गया है, अगर हम इस पर तरीके कह सकते हैं, तो हम गो पर इसके लिए एक स्टब लिख सकते हैं, जो एक अनुबंध के साथ एक पैकेज की तरह दिखेगा जिसे सीधे कहा जा सकता है। नेपोलियन की योजना, चलो शुरू करें।
सिद्धांत रूप में, हमारे पास पहले से ही एक प्रॉक्सी हेल्पर विधि है, इस हस्ताक्षर के साथ:
RouteCall(ref Address, method string, args []byte) ([]byte, error)
इस विधि को सीधे अनुबंध से बुलाया जा सकता है, यह रिमोट कॉन्ट्रैक्ट को कॉल करता है, एक क्रमबद्ध प्रतिक्रिया देता है जिसे हमें पार्स करने और अपने अनुबंध पर लौटने की आवश्यकता होती है।
लेकिन यह देखने के लिए उपयोगकर्ता के लिए आवश्यक है:
ret := contractPackage.GetObject(Address).Method(arg1,arg2, …)
आइए शुरू करते हैं, सबसे पहले, प्रॉक्सी में, आपको उन सभी प्रकारों को सूचीबद्ध करने की आवश्यकता है जो अनुबंध के तरीकों के हस्ताक्षरों में उपयोग किए जाते हैं, लेकिन जैसा कि हमें याद है, प्रत्येक एएसटी नोड के लिए हम इसका पाठीय प्रतिनिधित्व कर सकते हैं, और अब इस तंत्र के लिए समय आ गया है।
अगला, हमें एक प्रकार का अनुबंध बनाने की आवश्यकता है, सिद्धांत रूप में, वह पहले से ही अपनी कक्षा को जानता है, केवल एक पते की आवश्यकता है।
type {{ .ContractType }} struct { Reference Address }
अगला, हमें किसी तरह गेटऑबजेक्ट फ़ंक्शन को लागू करने की आवश्यकता है, जो ब्लॉकचैन के पते पर एक प्रॉक्सी उदाहरण लौटाएगा जो जानता है कि इस अनुबंध के साथ कैसे काम किया जाए, और उपयोगकर्ता के लिए यह अनुबंध उदाहरण की तरह दिखता है।
func GetObject(ref Address) (r *{{ .ContractType }}) { return &{{ .ContractType }}{Reference: ref} }
दिलचस्प है, उपयोगकर्ता डिबगिंग मोड में गेटऑबजेक्ट विधि सीधे बेसकॉन्ट्रैक्ट संरचना विधि है, लेकिन कुछ भी नहीं है, कुछ भी हमें रोकता नहीं है, एसएलए को देखते हुए, वह करने के लिए जो हमारे लिए सुविधाजनक है। अब हम एक प्रॉक्सी अनुबंध बना सकते हैं, जिसके तरीके हम नियंत्रित करते हैं। यह वास्तव में तरीके बनाने के लिए बनी हुई है।
{{ range $method := .MethodsProxies }} func (r *{{ $.ContractType }}) {{ $method.Name }}( {{ $method.Arguments }} ) ( {{ $method.ResultsTypes }} ) { {{ $method.InitArgs }} var argsSerialized []byte err := proxyctx.Current.Serialize(args, &argsSerialized) if err != nil { panic(err) } res, err := proxyctx.Current.RouteCall(r.Reference, "{{ $method.Name }}", argsSerialized) if err != nil { panic(err) } {{ $method.ResultZeroList }} err = proxyctx.Current.Deserialize(res, &resList) if err != nil { panic(err) } return {{ $method.Results }} } {{ end }}
यहां तर्क सूची के निर्माण के साथ एक ही कहानी है, क्योंकि हम आलसी हैं और विधि का बिल्कुल सही संग्रह करते हैं। गणना के लिए, यह बहुत प्रकार के रूपांतरणों को लेता है जो टेम्पलेट नहीं जानते हैं, इसलिए सब कुछ पहले से तैयार है। फ़ंक्शंस के साथ, सब कुछ गंभीरता से अधिक जटिल है, और यह एक अन्य लेख का विषय है।
हमारे पास जो कार्य हैं वे ऑब्जेक्ट कंस्ट्रक्टर हैं और हमारे सिस्टम में वास्तव में ऑब्जेक्ट्स कैसे बनाए जाते हैं, इस पर बहुत जोर दिया गया है, निर्माण का तथ्य एक रिमोट एक्ज़ीक्यूटरर पर पंजीकृत है, ऑब्जेक्ट को किसी अन्य निष्पादक को हस्तांतरित किया जाता है, इसे चेक किया जाता है और वास्तव में वहां बचाया जाता है, और व्यर्थ में बचाने के कई तरीके हैं। ज्ञान के इस क्षेत्र को क्रिप्ट कहा जाता है। और विचार मूल रूप से सरल है, एक आवरण जिसके अंदर केवल पता संग्रहीत है, और विधियाँ जो कॉल को क्रमबद्ध करती हैं और हमारे सिंगलटन प्रोसेसर को खींचती हैं, जो बाकी काम करता है। हम प्रेषित प्रॉक्सी हेल्पर का उपयोग नहीं कर सकते हैं, क्योंकि उपयोगकर्ता ने इसे हमारे पास नहीं किया है, इसलिए हमें इसे एक एकल बनाना होगा।
एक और चाल - वास्तव में, हम अभी भी कॉल संदर्भ का उपयोग करते हैं, यह एक ऐसा ऑब्जेक्ट है जो इस बारे में जानकारी संग्रहीत करता है कि कब, क्यों, क्यों, हमारे स्मार्ट अनुबंध को क्यों कहा गया, इस जानकारी के आधार पर, उपयोगकर्ता यह निर्णय लेता है कि क्या निष्पादन बिल्कुल देना है, और यदि संभव हो तो फिर कैसे।
पहले, हमने केवल संदर्भ पारित किया, यह बेसकंट्रेक्ट प्रकार में एक सेटर और गेट्टर के साथ एक गैर-व्यक्त क्षेत्र था, और सेटर ने केवल एक बार क्षेत्र सेट करने की अनुमति दी, इसलिए अनुबंध निष्पादित होने से पहले संदर्भ सेट किया गया था, और उपयोगकर्ता केवल इसे पढ़ सकता है।
लेकिन यहां समस्या है, उपयोगकर्ता केवल इस संदर्भ को पढ़ता है, यदि वह किसी प्रकार के सिस्टम फ़ंक्शन के लिए कॉल करता है, उदाहरण के लिए, किसी अन्य अनुबंध के लिए एक प्रॉक्सी कॉल, तो इस प्रॉक्सी कॉल को कोई संदर्भ नहीं मिलता है, क्योंकि कोई भी उसे पास नहीं करता है। और फिर गोरोइन स्थानीय भंडारण दृश्य में प्रवेश करता है। हमने अपना खुद का लिखने का फैसला किया, लेकिन github.com/tylerb/gls का उपयोग करें।
यह आपको वर्तमान गोरोइन के लिए संदर्भ सेट करने और लेने की अनुमति देता है। इस प्रकार, यदि अनुबंध के अंदर कोई गोरोइन नहीं बनाया गया था, तो हम अनुबंध शुरू करने से पहले सिर्फ संदर्भ को सेट करते हैं, अब हम उपयोगकर्ता को एक विधि नहीं, बल्कि एक फ़ंक्शन देते हैं।
func GetContext() *core.LogicCallContext { return gls.Get("ctx").(*core.LogicCallContext) }
और वह खुशी से इसका उपयोग करता है, लेकिन हम इसका उपयोग रूटकाॅल () में करते हैं, उदाहरण के लिए, यह समझने के लिए कि वर्तमान में कौन सा अनुबंध किसी को आकर्षित कर रहा है।
सिद्धांत रूप में, उपयोगकर्ता गोरोइन बना सकता है, लेकिन अगर वह करता है, तो संदर्भ खो जाता है, इसलिए हमें इसके साथ कुछ करने की आवश्यकता है, उदाहरण के लिए, यदि उपयोगकर्ता गो कीवर्ड का उपयोग करता है, तो हमें अपने आवरण में ऐसी कॉल लपेटनी चाहिए, जिसे संदर्भ याद रखेगा और बनाएगा। गोरोइन और इसमें संदर्भ को पुनर्स्थापित करें, लेकिन यह एक अन्य लेख का विषय है।
सब एक साथ
हम मूल रूप से यह पसंद करते हैं कि जीओ भाषा टूलचैन कैसे काम करता है, वास्तव में यह विभिन्न कमांडों का एक समूह है जो एक काम करता है, जिसे एक साथ निष्पादित किया जाता है जब आप निर्माण करते हैं, उदाहरण के लिए। हमने ऐसा ही करने का फैसला किया, एक टीम एक अस्थायी निर्देशिका में एक अनुबंध फ़ाइल डालती है, दूसरा उसके लिए एक आवरण डालती है और तीसरी बार कॉल करती है, जो प्रत्येक आयातित अनुबंध के लिए एक प्रॉक्सी बनाती है, चौथा यह सब संकलित करता है, पांचवां इसे ब्लॉकचेन पर प्रकाशित करता है। और उन सभी को सही क्रम में चलाने के लिए एक आदेश है।
हुर्रे, अब हमारे पास GO से GO लॉन्च करने के लिए एक टूलकिन और रनटाइम है। अभी भी कई समस्याएं हैं, उदाहरण के लिए, आपको किसी तरह से अप्रयुक्त कोड को अनलोड करने की आवश्यकता है, आपको किसी तरह यह निर्धारित करने की आवश्यकता है कि यह लटका हुआ है और निलंबित प्रक्रिया को पुनरारंभ करें, लेकिन ये ऐसे कार्य हैं जो स्पष्ट हैं कि इसे कैसे हल किया जाए।
हां, निश्चित रूप से, हमने जो कोड लिखा है, वह पुस्तकालय होने का ढोंग नहीं करता है, इसे सीधे इस्तेमाल नहीं किया जा सकता है, लेकिन काम करने वाली कोड पीढ़ी का एक उदाहरण पढ़ना हमेशा बहुत अच्छा होता है, एक समय में मैंने इसे याद किया। तदनुसार, कोड पीढ़ी का हिस्सा
संकलक में देखा जा सकता है, लेकिन यह
निष्पादनकर्ता में कैसे शुरू होता है।