منذ وقت ليس ببعيد ، تم إصدار الإصدار الأول من عقد النسيج- api-go - تنفيذ نموذج البرمجيات الجديد لسلاسل RFC 0001 . دعونا نرى ما هو وكيفية استخدامه.هنا أعددت مستودعًا بشبكة نسيج بسيطة ، حيث يعمل الزملاء في وضع ديف. اتبع التعليمات من المستودع لبدء الشبكة والعودة (لن يستغرق الأمر أكثر من 5 دقائق).الآن بعد أن أصبحت الشبكة قيد التشغيل وتم تثبيت السلسلة ، دعنا نلقي نظرة على الأجزاء الداخلية من السلسلة التي تعمل في النموذج الجديد.في SimpleContract.go ، نستورد الوحدة باستخدام واجهة برمجة التطبيقات الجديدة:github.com/hyperledger/fabric-contract-api-go/contractapi
بعد ذلك ، نصف عقدنا باستخدام هيكل SimpleContract الذي يتم تضمين بنية العقد فيه :type SimpleContract struct {
contractapi.Contract
}
من الضروري إنشاء العقد حتى يفي عقدنا بواجهة ContractInterface . هنا يجب عليك أن تحجز وتقول أن العقد! = Chaincode. السلاسل هي حاوية لعدد غير محدد من العقود. تقوم تشينكود بتخزين عقودها في الخريطة ، كما هو موضح في هذه القائمة:
type ContractChaincode struct {
DefaultContract string
contracts map[string]contractChaincodeContract
metadata metadata.ContractChaincodeMetadata
Info metadata.InfoMetadata
TransactionSerializer serializer.TransactionSerializer
}
يتم استخدام عقود
الخرائط داخليًا بواسطة Invoke لتوجيه الطلبات:func (cc *ContractChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
nsFcn, params := stub.GetFunctionAndParameters()
li := strings.LastIndex(nsFcn, ":")
var ns string
var fn string
if li == -1 {
ns = cc.DefaultContract
fn = nsFcn
} else {
ns = nsFcn[:li]
fn = nsFcn[li+1:]
}
...
nsContract := cc.contracts[ns]
...
successReturn, successIFace, errorReturn = nsContract.functions[fn].Call(ctx, transactionSchema, &cc.metadata.Components, serializer, params...)
...
return shim.Success([]byte(successReturn))
}
لذا عد إلى SimpleContract. يجب أن تحتوي جميع الطرق على معلمة ctx تفي بواجهة TransactionContextInterface . بشكل افتراضي ، تحصل جميع الطرق على TransactionContext قياسي ، وهو ما يكفي في معظم الحالات.يسمح لنا هذا السياق بالعمل مع ClientIdentity ، على سبيل المثال ، مثل:func (sc *SimpleContract) Whois(ctx contractapi.TransactionContextInterface) (string, error) {
return ctx.GetClientIdentity().GetID()
}
أو احصل على كعب مألوف (shim.ChaincodeStubInterface) لتنفيذ جميع الإجراءات المعتادة للتفاعل مع دفتر الأستاذ:func (sc *SimpleContract) Write(ctx contractapi.TransactionContextInterface, key string, value []byte) error {
return ctx.GetStub().PutState(key, value)
}
لكن! في كود المستودع التجريبي الخاص بنا ، يمكنك رؤية سياق مختلف تمامًا في الأساليب:func (sc *SimpleContract) Create(ctx CustomTransactionContextInterface, key string, value string) error {
existing := ctx.GetData()
if existing != nil {
return fmt.Errorf("Cannot create world state pair with key %s. Already exists", key)
}
err := ctx.GetStub().PutState(key, []byte(value))
if err != nil {
return errors.New("Unable to interact with world state")
}
return nil
}
هذا سياق مخصص. يتم إنشاؤه ببساطة شديدة. انتبه إلى context.go من مستودعنا :1. نعلن واجهة متوافقة مع Contractapi.TransactionContextInterfacetype CustomTransactionContextInterface interface {
contractapi.TransactionContextInterface
GetData() []byte
SetData([]byte)
}
2. الهيكل الذي قمنا فيه بتضمين العقدtype CustomTransactionContext struct {
contractapi.TransactionContext
data []byte
}
3. ننفذ الطرق المعلنة
func (ctc *CustomTransactionContext) GetData() []byte {
return ctc.data
}
func (ctc *CustomTransactionContext) SetData(data []byte) {
ctc.data = data
}
الآن ، عند تهيئة العقد ، نقوم ببساطة بتمرير هذه البنية كمعالج:simpleContract := new(SimpleContract)
simpleContract.TransactionContextHandler = new(CustomTransactionContext)
وجميع طرق عقدنا الآن بدلاً من ctx contractapi.TransactionContextInterface تقبل ctx CustomTransactionContextInterface .هناك حاجة إلى سياق مخصص لدفع الدولة من خلال عمليات ربط المعاملات . خطافات المعاملات هي اسم جميل للوسيطة التي تنطلق قبل أو بعد استدعاء طريقة العقد.مثال على خطاف ، قبل استدعاء طريقة ، يستخرج من دفتر الأستاذ قيمة المفتاح الذي تم تمريره بواسطة المعلمة الأولى في المعاملة:SimpleContract.gofunc GetWorldState(ctx CustomTransactionContextInterface) error {
_, params := ctx.GetStub().GetFunctionAndParameters()
if len(params) < 1 {
return errors.New("Missing key for world state")
}
existing, err := ctx.GetStub().GetState(params[0])
if err != nil {
return errors.New("Unable to interact with world state")
}
ctx.SetData(existing)
return nil
}
main.gosimpleContract.BeforeTransaction = GetWorldState
الآن يمكننا الحصول على قيمة المفتاح المطلوب في الطرق بشكل أكثر اختصارًا:SimpleContract.gofunc (sc *SimpleContract) Read(ctx CustomTransactionContextInterface, key string) (string, error) {
existing := ctx.GetData()
if existing == nil {
return "", fmt.Errorf("Cannot read world state pair with key %s. Does not exist", key)
}
return string(existing), nil
}
الخطاف بعد استدعاء الأسلوب متطابق تقريبًا ، باستثناء أنه بالإضافة إلى السياق ، فإنه يقبل واجهة فارغة (لماذا نحتاجها ، سنكتشفها لاحقًا):YetAnotherContract.gofunc After(ctx contractapi.TransactionContextInterface, beforeValue interface{}) error {
fmt.Println(ctx.GetStub().GetTxID())
fmt.Println("beforeValue", beforeValue)
return nil
}
يعرض هذا الخطاف معرف المعاملة والقيمة التي أرجعتها الطريقة قبل الخطاف . للتحقق من هذا الخط الخلفي ، يمكنك الذهاب إلى حاوية CLI واستدعاء طريقة العقد: قم بالتبديل إلى الوحدة الطرفية حيث يتم تشغيل الرمز ، سيبدو الإخراج على النحو التالي:docker exec -it cli sh
peer chaincode query -n mycc -c '{"Args":["YetAnotherContract:SayHi"]}' -C myc
e503e98e4c71285722f244a481fbcbf0ff4120adcd2f9067089104e5c3ed0efe # txid
beforeValue مرحبًا # القيمة من الطريقة السابقة
ماذا لو أردنا معالجة الطلبات باسم وظيفة غير موجود؟ لهذا، أي عقد له UnknownTransaction المجال :unknown_handler.gofunc UnknownTransactionHandler(ctx CustomTransactionContextInterface) error {
fcn, args := ctx.GetStub().GetFunctionAndParameters()
return fmt.Errorf("Invalid function %s passed with args %v", fcn, args)
}
main.gosimpleContract.UnknownTransaction = UnknownTransactionHandler
يمكن التحقق من ذلك أيضًا من خلال CLI: الخلاصة:docker exec -it cli sh
peer chaincode query -n mycc -c '{"Args":["BadRequest", "BadKey"]}' -C myc
خطأ: فشل المصادقة أثناء الاستعلام. الاستجابة: الحالة: 500 رسالة: "تم تمرير وظيفة غير صالحة BadRequest مع args [BadKey]"
لكي يبدأ الكود على الأقران ، يجب أن نستدعي طريقة Start () كما كان من قبل ، قبل تحويل جميع عقودنا إلى الكود :main.gocc, err := contractapi.NewChaincode(simpleContract, yetAnotherContract)
if err != nil {
panic(err.Error())
}
if err := cc.Start(); err != nil {
panic(err.Error())
}
مجموع
حل النموذج الجديد من كود الترميز مشكلة التوجيه ، الوسيطة ، تسلسل قيم الإرجاع ، إلغاء تسلسل وسيطات السلسلة (يمكن استخدام أي أنواع باستثناء الواجهة {} ). الآن يبقى انتظار تنفيذ النموذج الجديد لـ Go SDK.شكرا للانتباه.الجزء الثاني