نموذج برنامج جديد لسلسلة Hyperledger Fabric



منذ وقت ليس ببعيد ، تم إصدار الإصدار الأول من عقد النسيج- 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.TransactionContextInterface

type CustomTransactionContextInterface interface {
	contractapi.TransactionContextInterface
	GetData() []byte
	SetData([]byte)
}

2. الهيكل الذي قمنا فيه بتضمين العقد

type CustomTransactionContext struct {
	contractapi.TransactionContext
	data []byte
}

3. ننفذ الطرق المعلنة

// GetData return set data
func (ctc *CustomTransactionContext) GetData() []byte {
	return ctc.data
}

// SetData provide a value for data
func (ctc *CustomTransactionContext) SetData(data []byte) {
	ctc.data = data
}

الآن ، عند تهيئة العقد ، نقوم ببساطة بتمرير هذه البنية كمعالج:

simpleContract := new(SimpleContract)

simpleContract.TransactionContextHandler = new(CustomTransactionContext)

وجميع طرق عقدنا الآن بدلاً من ctx contractapi.TransactionContextInterface تقبل ctx CustomTransactionContextInterface .

هناك حاجة إلى سياق مخصص لدفع الدولة من خلال عمليات ربط المعاملات . خطافات المعاملات هي اسم جميل للوسيطة التي تنطلق قبل أو بعد استدعاء طريقة العقد.

مثال على خطاف ، قبل استدعاء طريقة ، يستخرج من دفتر الأستاذ قيمة المفتاح الذي تم تمريره بواسطة المعلمة الأولى في المعاملة:

SimpleContract.go
func 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.go
simpleContract.BeforeTransaction = GetWorldState

الآن يمكننا الحصول على قيمة المفتاح المطلوب في الطرق بشكل أكثر اختصارًا:

SimpleContract.go
func (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.go
func 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.go
func UnknownTransactionHandler(ctx CustomTransactionContextInterface) error {
	fcn, args := ctx.GetStub().GetFunctionAndParameters()
	return fmt.Errorf("Invalid function %s passed with args %v", fcn, args)
}

main.go
simpleContract.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.go
cc, err := contractapi.NewChaincode(simpleContract, yetAnotherContract)

	if err != nil {
		panic(err.Error())
	}

	if err := cc.Start(); err != nil {
		panic(err.Error())
	}

مجموع


حل النموذج الجديد من كود الترميز مشكلة التوجيه ، الوسيطة ، تسلسل قيم الإرجاع ، إلغاء تسلسل وسيطات السلسلة (يمكن استخدام أي أنواع باستثناء الواجهة {} ). الآن يبقى انتظار تنفيذ النموذج الجديد لـ Go SDK.

شكرا للانتباه.

الجزء الثاني

All Articles