Notas de estudio del curso "HyperLedger Fabric 2.3 Alliance Chain Construction"

Plataforma del curso: 51CTO
Profesor del curso: Datouwa
Introducción al curso: La construcción de la cadena de alianzas y el funcionamiento del código de cadena se explican desde cero. Deje que todos dominen verdaderamente la construcción de la cadena de alianzas y el proceso de implementación e invocación del código de cadena.

1. Introducción a Hyperledger Fabric Alliance Chain

1.1 Escenarios de uso

Los datos de la cadena de bloques son públicos, pero en algunas industrias como la banca, la atención médica. Estos datos privados no se pueden divulgar por completo, o se puede establecer un mecanismo de autorización para la divulgación selectiva. De forma predeterminada, todos los nodos de la cadena de bloques deben mantener el libro mayor y revisar el progreso de las transacciones con otros miembros, lo cual es ineficiente y costoso. Hyperledger es diferente, utiliza supernodos para completar la función de contabilidad.

1.2 Generar fondo

公有链缺点:
1.数据完全公开
2.所有节点参与运算成本大
3.不支持私下机密交易

De acuerdo con las deficiencias anteriores, el diseño de Hyperledger se enfoca en aplicaciones industriales, resuelve las deficiencias existentes y amplía las funciones originales de la industria.
Hyperledger revoluciona el modelo tradicional de cadena de bloques, que incluye la gestión de los permisos de acceso de los participantes. En otras palabras, Hyperledger es un libro mayor compartido autorizado. Hyperledger proporciona un modelo seguro y saludable para la identificación, la auditoría, es decir, la privacidad. Por lo tanto, acorta el ciclo de operación. Expansión Hacer frente a diversos requisitos en la industria.

一个覆盖众多网络的世界
我们希望能够出现众多的区块链网络,使得每个网络账本都能执行不同的业务,基于这一期望值,而出现了Hyperledger,现在单一普遍的通用网络已经出现.
但是网络与网络之间的通信还尚未有实例.会员就是在个个网络中的通,Hyperledger,除了能够实现这种网络独立运行之外.
还有寻址系统.能够通过一个账本的交易.发现并利用另一个本中正当的交易与智能合约

inserte la descripción de la imagen aquí

1.3 Ventajas y desventajas de la cadena de alianzas

Ventajas :
(1) Fuerte capacidad de control
En comparación con la cadena pública, debido a que los nodos son generalmente masivos, una vez que se forma una cadena de bloques, los datos del bloque no se pueden manipular, por ejemplo, hay demasiados nodos de Bitcoin y desea manipularlos. los datos del bloque son casi imposibles; en la cadena del consorcio, siempre que la mayoría de todas las instituciones del consorcio lleguen a un consenso, los datos del bloque se pueden cambiar.
(2) La cadena de
consorcio semicentralizada solo es propiedad de los miembros dentro del consorcio hasta cierto punto. Debido a que el número de nodos es limitado, es fácil llegar a un consenso.
(3) Velocidad de transacción rápida
En esencia, la cadena de alianza sigue siendo una cadena privada, pero debido a su número limitado de nodos, es fácil llegar a un consenso, por lo que la velocidad de transacción también es muy rápida.
(4) Los datos no serán divulgados por defecto
A diferencia de la cadena pública, los datos de la cadena de alianza están limitados a la organización interna de la alianza y sus usuarios tienen permiso de acceso.
Desventajas :
(1) Debido a la estructura semicentralizada de la cadena de alianzas, es fácilmente atacada por jugadores maliciosos. Es posible que múltiples participantes se confabulen.
(2) Debido a la falta de estándares unificados de la industria, inevitablemente habrá varios obstáculos en la solución, pero en la actualidad toda la ecología aún está lejos del marco unificado de la cadena de alianzas.
(3) Para grandes empresas integrales, debido a su escasa flexibilidad en los escenarios de aplicación. Para iniciar una nueva alianza, debe ser aprobada por el acuerdo de todos los miembros.Sin embargo, las grandes empresas tienen muchos procesos y restricciones complicadas, por lo que es muy lento establecer una red general de este tipo entre muchas empresas grandes.

1.4 La estructura básica de la cadena de alianzas

inserte la descripción de la imagen aquí
El sistema Fabric está dividido por organizaciones. Cada organización contiene nodos Peer con diferentes funciones, y cada nodo Peer puede desempeñar múltiples roles. Todas las organizaciones comparten un clúster de pedidos unificado.

1.Endorser Peer(背书结点)︰每个链码在实例化的时候都会设置背书策略(Endorsement policy),指定哪些节点对交易背书才有效。
2.Leader Peer (主节点)∶主节点负责和Orderer排序服务节点通信,从排序服务节点处获取最新的区块并在组织内部同步。
3.Committer Peer(记账节点)︰负责验证从排序服务节点接收的区块里的交易,然后将块提交(写入/追加)到其通道账本的副本。
4.Anchor Peer(锚节点)︰在一个通道( channel )上可以被所有其他peer发现的peer5.Order Peer(排序节点)︰排序服务节点接收包含背书签名的交易,对未打包的交易进行排序生成区块,广播给Peer主节点。

1.5 Requisitos básicos

(1) Demanda de autoridad de red La autoridad
emisora ​​determina la autoridad de acceso apropiada de acuerdo con el propósito de la red, y esta autoridad requiere identificación antes de realizar transacciones en la red. La web opera públicamente y se integra fácilmente en las aplicaciones móviles. Puede ejecutarse de forma privada, y solo a través de la verificación de permisos puede familiarizarse con su situación. Y puede establecer diferentes niveles de permisos según sus necesidades.
(2) La importancia de la privacidad y la confidencialidad
Creemos que cualquier arquitectura de cadena de bloques tiene un requisito básico, es decir, los patrones de identidad y comportamiento de cualquier parte en la red no permiten que personas no autorizadas consulten la situación a través del libro mayor. También queremos poder garantizar la confidencialidad de la lógica comercial de los usuarios de blockchain y otros parámetros de transacción. Asegúrese de que nadie más que las partes interesadas involucradas en el contrato puedan acceder a los datos.

1.6 Introducción a la terminología de Hyperledger

chainCode-chaincode
es en realidad un contrato inteligente en la cadena pública Chaincode es un software que se ejecuta en el libro mayor, que puede codificar activos, y las instrucciones de transacción (o lógica comercial) también se pueden usar para modificar activos.
Los canales Channel-Channel
se crean en una cadena de bloques privada en la red Fabric. Se realiza el aislamiento y la confidencialidad de los datos. El libro mayor especificado por el canal se comparte con todos los nodos pares en el canal, y la parte de la transacción debe pasar la verificación correcta del canal para interactuar con el libro mayor. El canal se define mediante un " bloque de configuración".
Bloque Gengsis - Bloque Génesis Bloque
Génesis es un bloque de configuración que inicializa la red o el canal de la cadena de bloques. También es el primer bloque en la cadena de bloques.
Certificado
Fabric-ca Fabric-ca es el componente de gestión de certificados predeterminado, que informa a los miembros de la red y sus usuarios emiten certificados basados ​​en PKl.
Cliente (aplicación)
La aplicación cliente utiliza el SDK para tratar con la red Fabric. Primero, el cliente obtiene un certificado de identidad válido de la CA para unirse al canal de la aplicación en la red. Hyperledger Fabric ya tiene SDK en varios idiomas. Estos SDK encapsulan las llamadas a la interfaz de gRPC subyacente y pueden proporcionar un soporte de desarrollo y cliente más completo, incluidas múltiples implementaciones como Node.Js, Python, Java y Go.
Invocar: la llamada
se usa para llamar a la función en chainCode.ChainCode invocar es una transacción, propuesta, y luego ejecuta el proceso modular (respaldo, consenso, verificación, envío). La estructura de invocar es una función y una matriz de parámetros.
Ledger -ledger
Ledger es una cadena de canales y una base de datos mantenida por cada par en el canal.

2. Gramática básica del lenguaje Go

El maestro dijo que también puede consultar el tutorial de novatos .
Capítulo 2 02 Introducción al sistema Go Code

//定义了包名,此文件属于哪个包,每个o程序都包含一个名为main的包
package main
// go编译器当前程序需要引用那些包(系统包,第三方包,用户自定义)
import "fmt"
//main函数,go语言的入口必须在main包
func main(){
    
    
	/*Println:面数主要用于输出内容
	2:go基于面数式编程
	*/
	fmt.Println( "Hello, world! ")
	fmt.Print("Hello,world!")
	fmt.Printf("%s d","Hello,world", 2018)
}

Pregunta 1: Si hay un error " go: cannot find GOROOT directory: c: \go
", debe configurar el directorio raíz de GOROOT. Debe reiniciarse después de la configuración.
Capítulo 2 03 Matrices, bucles
Capítulo 2 04 Funciones
Capítulo 2 05 Punteros
Capítulo 2 06 Estructuras
Capítulo 2 07 Interfaces, nuevo uso
Capítulo 2 08 Paquetes personalizados

ir resumen de idioma

package mainimport(
	"fmt"
	"reflect"
)
func main(){
    
    
	fmt.Println( "main().. ........")
	// go根据赋的值判断变量类型
	var e,f = 123,"hello"
	fmt.Println("e: ", e," f:",f)
	//隐式声明(一旦赋值成功,则变量类型将确定)
	x,y := fn01()
	fmt.Print1n("x: ", x," y:", y)
	fmt.Println(fn02())
	a, b := fn03(1020)
	fmt.Println("a: ", a, "a type:", reflect.Typeof(a), " b:", b)
	fno4(123)
}

func fno1()(intint) {
    
    
	return 23
}

//定义号返回的变量(理解为函数局部变量)
func fn02()(sum int) {
    
    
	//var sum int
	//已经声明不需要重复声明
	sum = 100
	return
}


//支持返回指针类型
func fno3(×, y int)(*intint){
    
    
	sum :=x+y
	//返回变量地址,返回变量的值
	return &sum,sum
}

//可变参数
func fno4(nums ...int){
    
    
	total := 0
	//可变参数就是数组(返回元素的索引和索引对应的值)
	for _, num := range nums {
    
    
		//fmt.Println("i: ",i)
		total += num
	}
	fmt.Println(total)
}


resultado de la operación:
inserte la descripción de la imagen aquí

3. Inicio rápido de Shell

Busque en el tutorial para principiantes : tutorial de shell, siga el tutorial para aprender un poco

3.1 El primer script de shell

Estructura de directorio común de Linux:

/bin目录存放可执行的文件, root和一般账号都可用
/boot这个目录存放开机使用的文件,例如: linux内核和系统启动文件.. ldev存放所有设备文件.包括硬盘、分区、鼠标、键盘、USB等
/etc存放系统所有配置文件,
/home:用户主目录的,当你创建一个用户时,默认的用户文件夹就在该目录下
/mnt*如果需要挂载某些设备,一般建议存放到此目录中
/root: root用户的目录
/usr:注意usr并不是user的缩写,而是Unix Software Resource的缩写,"“Unix操作系统软件资源"放在该目录,而不是用户的数据

primer archivo de script

mkdir test
cd test
sudo vim demo01.sh
#首先在vim打开文件界面后,按一次字母“i”  vim模式转为 INSERT,处于可编辑状态
#写入文件demo01.sh的内容如下两行:其中#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell

#!/bin/bash
echo "Hello World !"
#写完之后,按“ESC”退出插入模式回到命令模式,然后按“:wq”保存退出

#给脚本文件添加执行权限
sudo chmod +x demo01.sh
#执行脚本文件
./demo01.sh 
#输出结果为:Hello World !

Análisis de permisos de archivos
inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

3.2 Paso de variables y parámetros

注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:
命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
中间不能有空格,可以使用下划线 _。
不能使用标点符号。
不能使用bash里的关键字(可用help命令查看保留关键字)。

El vi demo01.sharchivo de script se puede editar con los siguientes contenidos:

#!/bin/bash
echo "Hello World !"
url="http: // www.163.com"
echo ${
    
    url}
str="url is \n ${url}"
echo  $str
str='url is \n ${url}'
echo $str
echo 'shell传递参数案例'
echo "file name is $0"
echo"第二参数为:$1"
echo "总共传递 $# 个参数"

#执行结果说明,双引号内支持变量和转义符,而单引号不支持

inserte la descripción de la imagen aquí

3.3 Matrices, para bucles

Se pueden almacenar múltiples valores en una matriz. Bash Shell solo admite matrices unidimensionales (las matrices multidimensionales no son compatibles) y no es necesario definir el tamaño de la matriz durante la inicialización (similar a PHP).
Similar a la mayoría de los lenguajes de programación, los subíndices de los elementos de la matriz comienzan en 0.
Los arreglos de shell están representados por corchetes y los elementos están separados por símbolos de "espacio". El formato de sintaxis es: nombre_arreglo=(valor1 valor2 … valorn)

#!/bin/bash
arr=('A' 'Bug' 3.14 100)
#显示数组
echo "第1个元素为${arr[0]}"
echo "第2个元素为${arr[1]} ,长度为${#arr[1]}"
echo "数组成员为: ${arr[*]},数组的长度为${#arr[*]}"
for loop in ${
    
    arr[*]}
do
    echo "this value is ${loop}"
done

if [ $1 == $2 ]
then
  echo "a==b"
else
  echo "a!=b"
fi

inserte la descripción de la imagen aquí

3.4 El archivo de función contiene

(1) Función

#!/bin/bash

funWithReturn(){
    
    
    echo "这个函数会对输入的两个数字进行相加运算..."
    echo "输入第一个数字: "
    read aNum
    echo "输入第二个数字: "
    read anotherNum
    echo "两个数字分别为 $aNum$anotherNum !"
    #注意,加法外面是两层小括号
    return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"

inserte la descripción de la imagen aquí
(2) Inclusión de archivos
Al igual que otros lenguajes, Shell también puede incluir scripts externos. Esto facilita encapsular algún código común como un archivo separado.

Shell 文件包含的语法格式如下:

. filename   # 注意点号(.)和文件名中间有一空格
或
source filename

Nota: los archivos incluidos no necesitan tener permiso de ejecución

#文件fn.sh,这个即为被包含文件,不用为其赋予执行权限
#!/bin/bash
funcDemo(){
    
    
  echo "参数1为:$1"
  echo "参数2为:$2"
  echo "请输入一个数值:"
  read num
  return $(($1 +$2 + ${
    
    num}))
}


#文件callfn.sh
!/bin/bash
#引入同目录的sh文件
source ./fn.sh
funcDemo 1 2
echo "result: $?"


#终端执行
gan@gan-virtual-machine:~/Gantest$ vi fn.sh
gan@gan-virtual-machine:~/Gantest$ vi callfn.sh
gan@gan-virtual-machine:~/Gantest$ chmod u+x callfn.sh 
gan@gan-virtual-machine:~/Gantest$ ./callfn.sh 
参数1为:1
参数2为:2
请输入一个数值:
3
result: 6

3,5 caso

var='yes'
case $var in
"yes")
	echo 'your choose is yes'
	;;
"no")	
	echo'your choose is no'
	;;
*)
	echo 'your choose is error'
	;;
esac
#短路模式
[ -d $1 ] && rm -r $1
#动态获取当前参数数量
echo $#
#吧所有参数左移动1位
shift 1
echo $#

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Cuarto, instalación de tela.

4.1 Instalación de Ubuntu

VMWare版本:VMware® Workstation 16 Pro 16.2.3 build-19376536
Ubuntu版本:Ubuntu 22.04 LTS【好像是发行版,我就是想装最新的,哈哈哈。大头娃老师用的是20.04版本】

Por supuesto, primero debe instalar VMWare y luego crear una nueva máquina virtual =="Siga las instrucciones e instálela paso a paso. La versión 22.04 de Ubuntu se ve bien y VMtools se instala solo.
Descarga e instala Baidu tú mismo~

4.2 Configuraciones y comandos básicos de Ubuntu

1. Cambiar la fuente de descarga

Haga clic en los nueve puntos en la esquina inferior izquierda para ingresar a la interfaz de búsqueda -> ingrese "software" en el cuadro de búsqueda -> haga clic en "Software y actualizaciones" que aparece, ingrese a la interfaz para cambiar la fuente de descarga -> haga clic en el cuadro correspondiente para descargar -> encontrar Alibaba Cloud Source (el profesor dijo que esto es relativamente estable)-> elegir servidor-> ingresar autenticación de contraseña-> cerrar-> recargar.

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
Una vez cargado, puede ejecutar el comando para verificar si la fuente de descarga se reemplazó correctamente:

cat /etc/apt/sources.list

inserte la descripción de la imagen aquí

2. Comandos básicos de Linux

创建文件 touch 文件名.文件后缀
删除文件 rm 文件名.文件后缀
创建目录 mkdir 目录名
删除目录 rmdir 目录名
删除带文件的目录 rm -r 目录名/
移动文件 mv 文件当前绝对路径  想要移动到的目录的绝对路径
		mv /home/hong/abc.txt  ../    #将abc.txt移动到上一级目录
移动文件并重命名 mv 当前文件名 目标文件名
root@ubuntu:/home# ls
abc.txt hong
root@ubuntu:/home# mv abc.txt aaa.txt
root@ubuntu:/home# ls
aaa.txt hong

#解压zip、tar压缩文件
root@ubuntu:/home/hong/Desktop# ls
fabric-2.1.1.zip fabric-2.3.0.tar.gz
root@ubuntu:/home/hong/Desktop# tar xzf fabric-2.3.0.tar.gz -C .
root@ubuntu:/home/hong/Desktop# tar xzf fabric-2.3.0.tar.gz -C ../
root@ubuntu: /home/ hong/Desktop# cd ..
root@ubuntu:/ home/hong# ls
Desktop Downloads fabric-2.3.0 Pictures  emplates Documents examples.desktop Music Public Videos
root@ubuntu :/home/hong#rm -r fabric-2.3.0/
root@ubuntu:/home/ hong# ls
Desktop Downloads Music Public Videos Documents examples.desktop Pictures Templates
root@ubuntu :/home/hong# cd Desktop/
root@ubuntu :/home/hong/Desktop# ls
fabric-2.1.1.zip fabric-2.3.0 fabric-2.3.0.tar.gz

root@ubuntu: /home/hong/Desktop# unzip fabric-2.1.1.zip -d .
#这种-d方式会覆盖原来有的文件,如果这个目录下有重名文件,解压出来直接覆盖
root@ubuntu: / home/ hong/Desktop# ls
fabric-2.1.1 fabric-2.1.1.zip fabric-2.3.0 fabric-2.3.0.tar.gz

4.3 Introducción e instalación de Docker

1. Una breve introducción a docker

Fabric no está directamente en Linux, está construido en base a contenedores Docker.
Almacén Docker : hay muchos componentes y entornos en este almacén, y los componentes del tejido que usaremos están en él.
Hay una breve introducción en el tutorial para principiantes: Docker es un motor contenedor de aplicaciones de código abierto, basado en el lenguaje Go y de código abierto de conformidad con el protocolo Apache 2.0. Docker permite a los desarrolladores empaquetar sus aplicaciones y dependencias en un contenedor portátil y liviano, que luego se puede distribuir a cualquier máquina Linux popular y también se puede virtualizar. El contenedor utiliza completamente el mecanismo de la caja de arena y no habrá ninguna interfaz entre ellos (similar a la aplicación para iPhone) y, lo que es más importante, la sobrecarga de rendimiento del contenedor es extremadamente baja.
inserte la descripción de la imagen aquí
Podemos realizar pruebas y depuraciones arbitrarias en la caja de arena dentro de la ballena sin afectar otros contenidos del océano. Una vez completada la depuración, podemos trasplantar la copia completa de la caja de arena de ballenas a diferentes sistemas operativos sin reconfigurar la prueba. Los contenedores Docker son el equivalente a las ballenas.
La configuración de los diferentes supernodos de la cadena de la alianza es en realidad la misma, por lo que se puede hacer con la ayuda del contenedor de la ventana acoplable. La prueba se completa primero en la ventana acoplable, y los otros nodos se pueden copiar y trasplantar ~

La diferencia entre docker y VM:
VM
inserte la descripción de la imagen aquí
presenta docker
inserte la descripción de la imagen aquí

综上:
docker是软件层面的虚拟化,比较轻量级,用于软件环境的复制移植和测试。
VM是操作系统层面的虚拟化,比较笨重,用于操作系统层面的测试。

2. instalación de la ventana acoplable

(1) Instale la ventana acoplable y vea la versión de la ventana acoplable

sudo apt install docker.io

Comandos disponibles dockerpara ver, el éxito de la instalación de la ventana acoplable tendrá una introducción de subcomando.
inserte la descripción de la imagen aquí
Ver la versión de la ventana acoplable

gan@gan-virtual-machine:~$ sudo docker version
Client:
 Version:           20.10.12
 API version:       1.41
 Go version:        go1.17.3
 Git commit:        20.10.12-0ubuntu4
 Built:             Mon Mar  7 17:10:06 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.12
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.17.3
  Git commit:       20.10.12-0ubuntu4
  Built:            Mon Mar  7 15:57:50 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.5.9-0ubuntu3
  GitCommit:        
 runc:
  Version:          1.1.0-0ubuntu1
  GitCommit:        
 docker-init:
  Version:          0.19.0
  GitCommit:        

(2) Configuración
A de Docker, demostración de extracción y eliminación de imágenes: tome hello-world como ejemplo

#拉取hello-world镜像,这样子是默认拉取最新版,如果想选特定版本就在后面加上版本号即可
sudo docker pull hello-world
#用docker images命令可以看到,我们拉取镜像成功啦,执行结果如下
sudo docker images
#REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
#hello-world   latest    feb5d9fea6a5   8 months ago   13.3kB
#删除镜像  后面跟的是镜像ID,因为同一个名字的镜像有不同版本,你可以装多个版本,所以会有多个同名的镜像,只有镜像ID才能唯一标识镜像
sudo docker rmi feb5d9fea6a5
#Untagged: hello-world:latest
#Untagged: hello-world@sha256:13e367d31ae85359f42d637adf6da428f76d75dc9afeb3c21faea0d976f5c651
#Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412
#Deleted: sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359
#再次查看,发现镜像删除成功啦
sudo docker images
#REPOSITORY   TAG       IMAGE ID   CREATED   SIZE

B. Configurar el acelerador de la ventana acoplable

Ingrese " https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors" en la página web, escanee con Alipay o Taobao para abrir Alibaba Cloud y confirme el inicio de sesión. Luego está el sitio web del acelerador, así como la guía de configuración del acelerador.
inserte la descripción de la imagen aquí

gan@gan-virtual-machine:~$ cd /etc/docker
gan@gan-virtual-machine:/etc/docker$ ls
key.json
#发现etc/docker目录下没有daemon.json这个文件,所以我们需要新建一个

Está bien, seguimos el comando de instalación de Ubuntu según lo solicite la interfaz de Alibaba Cloud, y copiamos y pegamos directamente ese párrafo en el terminal de comandos de Ubuntu.

gan@gan-virtual-machine:/etc/docker$ sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
    
    
  "registry-mirrors": ["https://m6juudzz.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
[sudo] password for gan: 
{
    
    
  "registry-mirrors": ["https://m6juudzz.mirror.aliyuncs.com"]
}
gan@gan-virtual-machine:/etc/docker$ ls
daemon.json  key.json
gan@gan-virtual-machine:/etc/docker$ cat daemon.json 
{
    
    
  "registry-mirrors": ["https://m6juudzz.mirror.aliyuncs.com"]
}

Después de configurar el acelerador, debe reiniciar la ventana acoplable y, a continuación, volvemos a extraer la imagen para comprobar si el acelerador está configurado correctamente.

#先查看docker当前状态:active (running)  运行中
gan@gan-virtual-machine:/etc/docker$ service docker status
#关闭docker
gan@gan-virtual-machine:/etc/docker$ service docker stop
#再次查看docker当前状态:inactive (dead) 关闭
gan@gan-virtual-machine:/etc/docker$ service docker status
#此时再重新启动
gan@gan-virtual-machine:/etc/docker$ service docker start
#再次查看docker状态就又变成运行中啦


#然后重新拉取hello-world镜像,如果成功,说明加速器配置应该没问题
gan@gan-virtual-machine:/etc/docker$ sudo docker pull hello-world
#Using default tag: latest
#latest: Pulling from library/hello-world
#2db29710123e: Pull complete 
#Digest: sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
#Status: Downloaded newer image for hello-world:latest
#docker.io/library/hello-world:latest

4.4 Comandos comunes de docker

1:查看版本: docker version
2:查看系统信息: docker info显示 Docker系统信息,包括镜像和容器数。
3: docker images //显示镜像列表
4: docker run hello-world(图文讲解)
//1∶客户端首先要连接Docker服务器。2:从Docker服务器中pull镜像。3:从lmage中创建容器,一个镜像可以创建多个容器。4: Docker守护进程将输出流式传输到Docker客户端,后者将其发送到您的终端。
5: docker rmi hello-world ===> docker images
6: docker pull nginx:1.18.0 //通过官网下载指定版本
7: docker run [-d] nginx //后台运行镜像
8: docker ps //l查看已运行镜像列表-a列表所有的容器,-f过滤,-q只列表容器的id。
9: docker run -d -p 8080:80 nginx //启动时候配置端口映射
10: docker stop containerID //只要能识别ID一位数也行-r尽量先删除容器在删除镜像2.1 docker rmi nginx:1.18.0 #删除已下载的镜像rm是删除容器(镜像运行的称为容器)
11:启动、关闭服务service docker status|start|stop

(1) Imagen y contenedor
inserte la descripción de la imagen aquí

#拉取镜像
root@gan-virtual-machine:~# docker pull hello-world
#运行容器
root@gan-virtual-machine:~# docker run hello-world
#查看当前正在运行的容器,发现没有,因为hello-world就是下图中所示,打印几个步骤出来,执行完成后自动关闭容器。
root@gan-virtual-machine:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

inserte la descripción de la imagen aquí

#删除docker镜像失败
root@gan-virtual-machine:~# docker rmi feb
Error response from daemon: conflict: unable to delete feb5d9fea6a5 (must be forced) - image is being used by stopped container 2d9ba30d106c

#查看所有镜像,包括未处于运行状态的容器
root@gan-virtual-machine:~# docker ps -a
CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES
2d9ba30d106c   hello-world   "/hello"   37 minutes ago   Exited (0) 37 minutes ago             condescending_tu
#想要删除镜像需要先删除其对应的容器,和删除类之前要删掉对应的对象一个道理。
#删除容器2d9ba30d106c。后面接的容器ID,只要能够区分就行,不用写完整的ID,如果第一位就能区分,打一个字母也是可以的
root@gan-virtual-machine:~# docker rm 2d9
#查看容器
root@gan-virtual-machine:~# docker ps -a
#CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
#容器已经删掉了,此时删除镜像
root@gan-virtual-machine:~# docker rmi feb
#查看镜像,确保删除成功
root@gan-virtual-machine:~# docker images

inserte la descripción de la imagen aquí

(2) Implementación de Nginx
Puede ver la versión en el almacén acoplable
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

#找到1.19.6版本【因为老师用的这个版本啦~】,复制镜像拉取命令
#不加版本号的话,会默认拉取最新的版本,其实装最新版也是可以哒~
docker pull nginx:1.19.6

inserte la descripción de la imagen aquí
Puede localhost:端口号acceder a Nginx, que en realidad es similar a la interfaz del servidor de Tomcat.
inserte la descripción de la imagen aquí

Pero el mío es así: [El colapso de Xiaobai fue en un momento diferente al del maestro]
inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

(3) Pequeño episodio
Debido a que no podía acceder a la página de Nginx, busqué el problema y descubrí que todavía no podía acceder, así que miré hacia atrás, solo que este paso era diferente del maestro. Cambie de administrador
con el comando No tengo éxito, v_v. Entonces yo Baidu,su
inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

ubuntu初始root密码
安装完Ubuntu后可能没有设置root密码,不知道密码自然就无法进入根用户下。
到网上搜了一下,原来是这么回事:
Ubuntu的默认root密码是随机的,即每次开机都有一个新的root密码。
解决方法:
我们可以在终端输入命令“sudo passwd”,然后输入当前用户的密码后"Enter"。
终端会提示我们输入新的密码并确认,此时的密码就是root新密码。
修改成功后,输入命令 su root,再输入新的密码就ok了。

De todas formas, finalmente se comprobó que no era por problema del administrador. Reinicie el cliente docker y reinicie el contenedor ~
inserte la descripción de la imagen aquí

4.5 Descarga del componente Fabric

1. Versión resumida: descarga de componentes de Fabric y comando de extracción

Cambie primero al administrador y luego ejecute el siguiente comando ~

//在GitHub下载fabric2.3.0的tar.gz文件后,拖到Ubuntu中,并且解压到/opt目录下,进入解压后的目录
root@ubuntu:/home/hong/Desktop# tar xzf fabric-2.3.0.tar.gz -C /opt
root@ubuntu:/opt/fabric-2.3.0/scripts# apt install curl
//如果有报错“E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)”的话,可以不用移除/var/lib/dpkg/lock这个目录,然后重新安装apt install curl,如果没有报错,跳过下面这条语句
root@ubuntu:/opt/fabric-2.3.0/scripts# rm /var/lib/dpkg/lock
//安装,拉取下载组件,获得bin、config、test-等目录
root@ubuntu:/opt/fabric-2.3.0/scripts# ./bootstrap.sh 
//安装docker-compose
root@ubuntu/opt/fabric-2.3.0/scripts# :apt install docker-compose
//启动Fabric服务
root@ubuntu:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh up
//可以看下,现在在运行的docker容器
root@ubuntu:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# docker ps -a
//然后试一下终止网络命令——>本质就是销毁之前启动的docker容器的过程
root@ubuntu:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh down
//组件下载这部分就完成啦

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Agrega un pequeño truco
para desactivar la pantalla de bloqueo automático, para que al descargar recursos, la pantalla no se bloquee por estar mucho tiempo fuera de la interfaz de Ubuntu, lo que hará que se suspenda la descarga
. versión de Ubuntu, que debe desactivarse.
inserte la descripción de la imagen aquí

La nueva versión que instalé no parece tener esta opción, si la hay, la ubicación de la configuración puede haber cambiado.
inserte la descripción de la imagen aquí

2. Captura de pantalla del proceso de descarga del componente [registro de captura de pantalla de los pasos del profesor, puede omitirlo sin verlo]

El profesor dijo que en la próxima lección hablaremos sobre la descarga del paquete de código fuente de fabric
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
Después de la instalación, inicie la red e intente:
inserte la descripción de la imagen aquí

Se encontró una dependencia de ventana acoplable faltante en este componente. Docker-compose es en realidad una herramienta de línea de comandos de Docker Antes de que varias imágenes de Docker necesiten llamarse entre sí y dependan unas de otras, se requiere docker-compose para completarlo. Simplemente instálelo ~
inserte la descripción de la imagen aquí
Después de la instalación, puede encontrar que no hay muestras al principio y
inserte la descripción de la imagen aquí
después de que se complete la operación,
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
luego inicie la red y vea:
inserte la descripción de la imagen aquí

3. El problema de la falla de descarga del componente ca

Problema 1 La descarga del componente CA falló: "------> 1.4.9 El binario fabric-ca-client no está disponible para descargar (Disponible desde 1.1.0-rc1) <----
" No me importa, Pensé que no tendría ningún efecto. Aparecerá un aviso cuando inicie la red más tarde. Lo pensé y debería volver y verificarlo, o es mejor completarlo.

===> Downloading version 1.4.9 platform specific fabric-ca-client binary
===> Downloading:  https://github.com/hyperledger/fabric-ca/releases/download/v1.4.9/hyperledger-fabric-ca-linux-amd64-1.4.9.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:21 --:--:--     0
curl: (7) Failed to connect to github.com port 443 after 21048 ms: Connection refused

gzip: stdin: unexpected end of file
tar: Child returned status 1
tar: Error is not recoverable: exiting now
==> There was an error downloading the binary file.

------> 1.4.9 fabric-ca-client binary is not available to download  (Available from 1.1.0-rc1) <----

Luego lo descargué en el host y copié el archivo tar.gz descargado en él. Después de descomprimirlo, descubrí que eran los dos archivos en el contenedor.
inserte la descripción de la imagen aquí
Después de dos días, lo agregué. Cuando reinstalé, descubrí que puede completar la descarga cuando la red es buena.
inserte la descripción de la imagen aquí

**Pregunta 2 La imagen de la ventana acoplable no se extrajo correctamente: **Cuando el servicio se inicie más tarde, aparecerá el mensaje "Los archivos binarios de la estructura local y las imágenes de la ventana acoplable no están sincronizados. Esto puede causar problemas".
inserte la descripción de la imagen aquí

Al instalar por primera vez, debido a que falló la descarga del componente de CA, se detuvo en el lugar donde falló la descarga del componente de CA y se cerró, por lo que no se extrajo la imagen de la ventana acoplable.
inserte la descripción de la imagen aquí
Cuando se instaló por segunda vez más tarde, todo salió bien y tenía una imagen acoplable ~
inserte la descripción de la imagen aquí

4.6 Análisis de procesos de componentes de descarga de fabric

1. Descarga del código fuente de Fabric

Enlace de recursos
Abra el sitio web de GitHub arriba, luego despliegue y preste atención a la parte derecha “Releases 45 v2.4.3 Latest”, haga clic Releasespara ingresar a la nueva interfaz, haga clic en Tags, puede ver diferentes versiones, encontramos 2.3.0, seleccione tar.gz. ¡Entonces la descarga es exitosa!
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Después de descargar el código fuente, quiero copiarlo a Ubuntu. No puedo arrastrarlo directamente. Aunque instalé VMtools, simplemente no puedo arrastrar el archivo del código fuente. Luego, lo copio con un disco U. , jajaja, soy realmente inteligente ~
[Id. de problema 202206181158] Me encontré con un problema extraño: puedo copiar un solo archivo del host a ubuntu, pero no comprimir archivos y carpetas a Ubuntu. El error es el siguiente:
inserte la descripción de la imagen aquí

2. Análisis de instalación de componentes

El maestro construyó manualmente el servicio de tela de acuerdo con el catálogo. Él dijo: "De hecho, si la red no es muy buena, puede descargar el paquete de instalación requerido y luego construir manualmente el servicio de estructura". Quería darnos una imagen más clara de los componentes importantes y su estructura al hacerlo manualmente.

//目录结构
fabric-2.3.0【项目主目录】
	|—— scripts【加载组件第一级子目录】
		|—— bootstrap.sh【启动文件,可以用这个文件拉取并下载后面几个需要的重要文件】
		|—— fabric-samples【官方实例目录】
			|—— bin【这个bin目录下有十个文件:configtxgen,cryptogen ,fabric-ca-client,idemixgen,osnadmincon,figtxlator,discover,fabric-ca-server,orderer,peer。都是很重要的组件,包括节点、证书服务端、证书客户端等】
			|—— config【配置文件目录,内含三个配置文件:configtx.yaml, core.yaml,orderer.yaml】
			|—— test-network【网络测试目录,其下有一个控制网络服务启动、关闭等相关命令的文件】
				|—— network.sh【控制网络启动、关闭的文件】
							

inserte la descripción de la imagen aquí

Cinco, análisis de código fuente

5.1 Análisis de código fuente Bootstrap

#!/bin/bash
#
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

# if version not passed in, default to latest released version
#设置fabric版本
VERSION=2.3.0
# if ca version not passed in, default to latest released version
#很多时候fabric有最新版,但是组件没有对应更新位相应版本,这个ca版本是1.4.9
CA_VERSION=1.4.9
ARCH=$(echo "$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')")
MARCH=$(uname -m)
#接下来是6个函数的定义,我们可以先跳过这些函数内容,先看后面的具体调用部分
printHelp() {
    
    #打印输出,描述性的
    echo "Usage: bootstrap.sh [version [ca_version]] [options]"
    echo
    echo "options:"
    echo "-h : this help"
    echo "-d : bypass docker image download"
    echo "-s : bypass fabric-samples repo clone"
    echo "-b : bypass download of platform-specific binaries"
    echo
    echo "e.g. bootstrap.sh 2.3.0 1.4.9 -s"
    echo "will download docker images and binaries for Fabric v2.3.0 and Fabric CA v1.4.9"
}

# dockerPull() pulls docker images from fabric and chaincode repositories
# note, if a docker image doesn't exist for a requested release, it will simply
# be skipped, since this script doesn't terminate upon errors.

dockerPull() {
    
    
    #three_digit_image_tag is passed in, e.g. "1.4.7"
    #传过来的第一个参数,FABRIC_TAG
    three_digit_image_tag=$1
    #所有参数向前移动一位,相当与FABRIC_TAG删掉啦,移动之后,第一个参数$1就变成了数组的第一个元素啦
    shift
    #two_digit_image_tag is derived, e.g. "1.4", especially useful as a local tag for two digit references to most recent baseos, ccenv, javaenv, nodeenv patch releases
    #切割版本号,把最后一位切掉
    two_digit_image_tag=$(echo "$three_digit_image_tag" | cut -d'.' -f1,2)
    #“$#”是动态获取当前参数数量,gt是“>”符号,即当前参数数量大于0
    while [[ $# -gt 0 ]]
    do
    	#获取第一个要拉取的镜像名称
        image_name="$1"
        #“$three_digit_image_tag”即版本号
        echo "====> hyperledger/fabric-$image_name:$three_digit_image_tag"
        #拉取镜像,通过镜像名称和版本号拉取
        docker pull "hyperledger/fabric-$image_name:$three_digit_image_tag"
        docker tag "hyperledger/fabric-$image_name:$three_digit_image_tag" "hyperledger/fabric-$image_name"
        docker tag "hyperledger/fabric-$image_name:$three_digit_image_tag" "hyperledger/fabric-$image_name:$two_digit_image_tag"
        #所有参数向前移动一位,下载完一个就往左移动一个,即当前$1变成第二个要获取的镜像名字啦
        shift
    #直到当前参数个数为0,即拉取下载完所有镜像之后,推出循环,函数执行结束
    done
}

cloneSamplesRepo() {
    
    
    # clone (if needed) hyperledger/fabric-samples and checkout corresponding
    # version to the binaries and docker images to be downloaded
    #if [ -d first-network ]用于判断当前这个目录是否存在
    if [ -d first-network ]; then #first-network在1.4好像就改了,把这个目录改成fabric-samples了,所以这条语句不重要
        # if we are in the fabric-samples repo, checkout corresponding version
        echo "==> Already in fabric-samples repo"
    elif [ -d fabric-samples ]; then #这个语句,如果fabric-samples目录存在,说明已经有东西啦,不用下载,直接进目录就行。所以这个条件下的操作为:打印“Changing directory to fabric-samples”并进入fabric-samples
        # if fabric-samples repo already cloned and in current directory,
        # cd fabric-samples
        echo "===> Changing directory to fabric-samples"
        cd fabric-samples
    else  #这时候,说明fabric-samples目录不存在,所以我们要下载。所以执行的操作为:打印“Cloning hyperledger/fabric-samples repo”,并通过git获取fabric-samples目录及其对应文件并且进入那个目录。
        echo "===> Cloning hyperledger/fabric-samples repo"
        git clone -b master https://github.com/hyperledger/fabric-samples.git && cd fabric-samples
         #这里值得多说一点,&& 与运算,两个都为真才执行,其实隐含了前一个命令执行成功才会执行后一个命令。所以在Linux里面,&&可以连接任何命令,不一定是条件表达式。这就是“短路操作”。与或非运算都有短路操作。
    	#command1 && command2 。&&左边的命令(命令1)返回真(即返回0,成功被执行)后,&&右边的命令(命令2)才能够被执行;换句话说,“如果这个命令执行成功&&那么执行这个命令”语法格式:commandl && command2 && command3 ...
		#1.命令之间使用&&连接,实现逻辑与的功能。2.只有在&&左边的命令返回真(命令返回值$? == 0),&&右边的命令才会被执行。3.只要有一个命令返回假(命令返回值 $?== 1),后面的命令就不会被执行。4.示例1[root@open ~]# cp anaconda-ks.cfg/data/ && rm -f anaconda-ks.cfg && echo "SUCCESS"中,cp命令首先从root的y家目录复制文件文件anaconda-ks.cfgi到 /data目录下;执行成功后,使用rm 命令删除源文件;如果删除成功则输出提示信息"SUCCESS"。
    fi
	
	#判断版本
    if GIT_DIR=.git git rev-parse v${
    
    VERSION} >/dev/null 2>&1; then
        echo "===> Checking out v${VERSION} of hyperledger/fabric-samples"
        git checkout -q v${
    
    VERSION}
    else
        echo "fabric-samples v${VERSION} does not exist, defaulting master"
        git checkout -q master
    fi
}

# This will download the .tar.gz
download() {
    
    
    local BINARY_FILE=$1
    local URL=$2
    echo "===> Downloading: " "${URL}"
    #设置重连的次数(5),每次重连延迟3秒
    #文件下载成功之后,直接调用tar解压操作
    #rc=$?表示获取函数执行的结果,即解压是否成功的结果
    curl -L --retry 5 --retry-delay 3 "${URL}" | tar xz || rc=$?
    #补充
    #|运算符:管道符号,是unix—个很强大的功能,符号为一条竖线:"|"。用法:command 1 | command 2,他的功能是把第一个命令command 1执行的结果作为command2的输入传给command 2。
    if [ -n "$rc" ]; then #rc不为空,说明解压出现问题。返回22,并打印“There was an error downloading the binary file”
        echo "==> There was an error downloading the binary file."
        return 22
    else #如果解压成功,打印"==> Done."
        echo "==> Done."
    fi
}

pullBinaries() {
    
    #通过两个下载,实现bin和config目录的生成
	#打印,并且通过传入两个参数(文件名和GitHub下载地址)调用download函数。
    echo "===> Downloading version ${FABRIC_TAG} platform specific fabric binaries"
    download "${BINARY_FILE}" "https://github.com/hyperledger/fabric/releases/download/v${VERSION}/${BINARY_FILE}"
    if [ $? -eq 22 ]; then #获取download调用执行结果,如果为22说明download执行失败,所以打印"------> ${FABRIC_TAG} platform specific fabric binary is not available to download <----"。然后退出执行。后面下载ca也是同理的
        echo
        echo "------> ${FABRIC_TAG} platform specific fabric binary is not available to download <----"
        echo
        exit
    fi
	#同理,下载fabric-ca。打印,并且通过传入两个参数(文件名和GitHub下载地址)调用download函数。
    echo "===> Downloading version ${CA_TAG} platform specific fabric-ca-client binary"
    download "${CA_BINARY_FILE}" "https://github.com/hyperledger/fabric-ca/releases/download/v${CA_VERSION}/${CA_BINARY_FILE}"
    if [ $? -eq 22 ]; then 
        echo
        echo "------> ${CA_TAG} fabric-ca-client binary is not available to download  (Available from 1.1.0-rc1) <----"
        echo
        exit
    fi
}

pullDockerImages() {
    
    
	#command命令:无条件调用系统提供的函数。这个是为了确保即使用户自己定义了一个叫docker的函数,执行当前函数时调用的也是系统提供的docker函数。
    command -v docker >& /dev/null
    #用于接收上一条命令的执行结果,如果为0,则说明系统docker函数存在
    NODOCKER=$?
    if [ "${NODOCKER}" == 0 ]; then
    	#系统docker存在,则创建一个数组,数组里面存储待下载镜像名称
        FABRIC_IMAGES=(peer orderer ccenv tools)
        case "$VERSION" in
        #如果当前是fabric2.x版本,则组件添加baseos
        2.*)
            FABRIC_IMAGES+=(baseos)
            shift
            ;;
        esac
        echo "FABRIC_IMAGES:" "${FABRIC_IMAGES[@]}"
        echo "===> Pulling fabric Images"
        #调用函数dockerPull ,且传入两个参数:第一个参数是下载Fabric版本FABRIC_TAG,第二个是数组FABRIC_IMAGES的所有元素
        dockerPull "${FABRIC_TAG}" "${FABRIC_IMAGES[@]}"
        echo "===> Pulling fabric ca Image"
        #创建数组,其中只有一个元素:ca
        CA_IMAGE=(ca)
        #调用函数dockerPull ,且传入两个参数:第一个参数是下载ca版本CA_TAG,第二个是数组CA_IMAGE的所有元素
        dockerPull "${CA_TAG}" "${CA_IMAGE[@]}"
        echo "===> List out hyperledger docker images"
        #如果下载成功,则用docker images查看镜像。grep是正则表达式,也就是在所以docker images中选择和hyperledger相关的镜像打印输出。
        docker images | grep hyperledger
    else
        echo "========================================================="
        echo "Docker not installed, bypassing download of Fabric images"
        echo "========================================================="
    fi
}

#从这里开始~首先定义了三个变量,将它们都赋值为true
DOCKER=true
SAMPLES=true
BINARIES=true

# Parse commandline args pull out
# version and/or ca-version strings first
if [ -n "$1" ] && [ "${1:0:1}" != "-" ]; then
    VERSION=$1;shift  #一会儿下载时,读取输入的第一个参数,如果其不为“-”,则赋值给VERSION;否则前面采用默认的VERSION=2.3.0。一般是不会执行这几句的,但是加上的话逻辑更完整。
    if [ -n "$1" ]  && [ "${1:0:1}" != "-" ]; then
        CA_VERSION=$1;shift  #同理,,读取输入的第一个参数,如果不为“-”,则赋值给CA_VERSION;否则前面的采用CA_VERSION=1.4.9
        if [ -n  "$1" ] && [ "${1:0:1}" != "-" ]; then
            THIRDPARTY_IMAGE_VERSION=$1;shift #同理,读取第一个参数,如果版本号不为“-”,则赋值给THIRDPARTY_IMAGE_VERSION
        fi
    fi
fi

# prior to 1.2.0 architecture was determined by uname -m
#拼接要下载的文件下载链接,此处代码不重要
#后面打印输出的时候要用“=> Downloading version 2.3.0 platform specific fabric binaries=> Downloadinq: https://github.com/hyperledger/fabric/releases/download/v2.3.0/hyperledger-fabric-linux-amd64-2.3.0.tar.gz”以及“=> Downloading version 1.4.9 platform specific fabric-ca-client binary =>Downloading: https://github.com/hyperledger/fabric-ca/releases/download/vl.4.9/hyperledger-fabric-ca-linux-amd64-1.4.9.tar.gz”
if [[ $VERSION =~ ^1\.[0-1]\.* ]]; then
    export FABRIC_TAG=${
    
    MARCH}-${
    
    VERSION}
    export CA_TAG=${
    
    MARCH}-${
    
    CA_VERSION}
    export THIRDPARTY_TAG=${
    
    MARCH}-${
    
    THIRDPARTY_IMAGE_VERSION}
else
    # starting with 1.2.0, multi-arch images will be default
    : "${CA_TAG:="$CA_VERSION"}"
    : "${FABRIC_TAG:="$VERSION"}"
    : "${THIRDPARTY_TAG:="$THIRDPARTY_IMAGE_VERSION"}"
fi

BINARY_FILE=hyperledger-fabric-${
    
    ARCH}-${
    
    VERSION}.tar.gz
CA_BINARY_FILE=hyperledger-fabric-ca-${
    
    ARCH}-${
    
    CA_VERSION}.tar.gz

#这部分才是核心逻辑,指明了其实boostrap.sh总共就完成了三件事情
#如果SAMPLES变量为True,则调用cloneSamplesRepo,下载hyperledger/fabric-samples
#如果BINARIES变量为True,则调用pullBinaries,拉取Hyperledger Fabric binaries
#如果DOCKER变量为True,则调用pullDockerImages,拉取Hyperledger Fabric docker images
# then parse opts
while getopts "h?dsb" opt; do
    case "$opt" in
        h|\?)
            printHelp
            exit 0
            ;;
        d)  DOCKER=false
            ;;
        s)  SAMPLES=false
            ;;
        b)  BINARIES=false
            ;;
    esac
done

if [ "$SAMPLES" == "true" ]; then
    echo
    echo "Clone hyperledger/fabric-samples repo"
    echo
    cloneSamplesRepo
fi
if [ "$BINARIES" == "true" ]; then
    echo
    echo "Pull Hyperledger Fabric binaries"
    echo
    pullBinaries
fi
if [ "$DOCKER" == "true" ]; then
    echo
    echo "Pull Hyperledger Fabric docker images"
    echo
    pullDockerImages
fi

5.2 Introducción a los conceptos de canal, organización y certificado

1. Código de cadena

En tejido, un contrato inteligente se llama código de cadena y tiene 6 estados:

. install (代码上链)
. instantate(初始化)
. invocable (调用链码)
. upgrade(由于合约有多个副本,因此每个合约都有版本号,版本升级时就是upgrade)
. Deinstantiate (合约销毁)
. Uninstall(卸载链码)

2. Canal

canal: una subred privada, de hecho, para aislar diferentes aplicaciones, un canal puede contener un lote de códigos de cadena.
Solo las organizaciones (empresas) en el mismo canal pueden compartir los mismos datos de bloque y realizar operaciones de código de cadena relacionadas.

3. Certificados y Cifrado

El módulo CA implementado anteriormente es para distribuir claves públicas y privadas a los nodos, y el cifrado y las firmas se pueden lograr con las claves públicas y privadas.
Cifrado asimétrico: los mensajes cifrados con clave pública solo se pueden descifrar con clave privada; de manera similar, los mensajes firmados con clave privada solo se pueden verificar con clave pública.

数字证书:联盟链采用的非对称加密技术来事项身份验证和数据加密,本质就是SSl技术
CA(Certificate Authority):提供数字证书包括公钥(服务使用者)和私钥(服务者)
还提供一个CRL:证书吊销列表

por ejemplo:
inserte la descripción de la imagen aquí

El número de buzón de Xiao Ming es 11A y el número de buzón de Xiaohong es 11B. Estos son conocidos por todos, similares a 11A, 11B se llaman claves públicas. Si Xiaoming quiere enviar una carta a Xiaohong, solo necesita poner la carta escrita en el buzón de Xiaohong. Las cartas protegidas por buzones son como mensajes encriptados. Solo Xiaohong puede abrirlo con su llave. Y la clave de Xiaohong es su clave privada.
(1) Esta es la clave pública, todos tienen un par de claves, una es pública y la otra es secreta
(2) A quiere enviar información a B, cifra la información con la clave pública de B y se la envía a B
(3) ) B obtiene información cifrada, descifra la información cifrada con su propia clave y obtiene la información de texto sin formato

4. Arquitectura de cadena de consorcio

fabric联盟链的开发人员主要分为三类:
底层是系统运维,负责系统的部署与维护;
其次是组织管理人员,负责证书、MSP权限管理、共识机制等;
最后是业务开发人员,他们负责编写chaincode、创建维护channel、执行transaction交易等

inserte la descripción de la imagen aquí

5. Proceso de ejecución de la tela

El proceso de tejido se mencionó en la "Arquitectura básica 1.4 de la cadena de alianzas" anterior, aquí hay una breve introducción al consenso y la interacción de los nodos. Esta parte del contenido proviene del libro breve: Análisis en profundidad del modo de consenso de tejido

El modo Endorse+Kafka+Commit adoptado por el modo de consenso de fabric incluye los siguientes pasos:

(1)请求背书:客户端用自己的私钥对交易进行签名后,按照指定格式将交易和签名信息进行打包,然后将打包后的数据发给背书节点请求背书。

(2)验证背书:背书节点收到背书请求后,验证交易的签名是否正确并调用智能合约验证交易内容是否合法。验证通过的话,背书节点用自己的私钥对背书结果进行签名并按照指定格式打包,然后将打包后的数据发给客户端。

(3)提交交易:客户端收到背书结果后,验证背书结果的签名是否正确。验证通过后,对交易请求和背书结果签名并打包。然后,把打包后的数据发送给orderer节点提交交易。

(4)排序广播:orderer节点收到交易后,验证数据的客户端签名[bJ1] 是否正确。验证通过后,将交易发给kafka集群对应的topic。由于orderer中的对于每个通道都在kafka上监听对应的消息,因此,kafka将消息存放到对应topic上之后,会将消息广播给通道上的所有orderer。因为各个orderer的消息都是由kafka按照相同顺序发送的,因此,这个过程也实现了消息的排序。

(5)打包出块:orderer节点接收到从kafka推送的消息(kafka节点见同步消息不需要验证),当满足出块策略[bJ2] :缓存交易个数达到区块最大交易数或者时间达到出快时间,则将交易进行打包、对数据签名,然后出块,并将区块分发给peer节点。

(6)验证记账:peer节点接收到区块后,验证交易是否有效即验证区块的交易是否满足背书策略以及区块中交易的读写集版本是否正确[bJ3] 。验证通过的话,执行此交易的内容更改状态数据库。验证失败的话,对此条交易不做任何处理。当区块中的交易全部处理完成后,将区块记录在本地数据库。

Diagrama de flujo de interacción de comunicación:
inserte la descripción de la imagen aquí

Diagrama de flujo de flujo de datos:
inserte la descripción de la imagen aquí

5.3 análisis de inicio de red de prueba

chaincode:智能合约,上文已提到。每个chaincode可提供多个不同的调用命令。 transaction:交易,每条指令都是一次交易。
world state: 对同一个key的多次交易形成的最终value,就是世界状态。
endorse:背书。金融上的意义为:指持票人为将票据权利转让给他人或者将一定的票据权利授予他人行使,而在票据背面或者粘单上记载有关事项并签章的行为。通常我们引申为对某个事情负责。在我们的共识机制的投票环节里,背书意味着参与投票。
endorsement policy:背书策略。由智能合约chaincode选择哪些peer节点参与到背书环节来。 peer:存放区块链数据的结点,同时还有endorse和commit功能。
channel:私有的子网络,事实上是为了隔离不同的应用,一个channel可含有一批chaincode,
PKI: Public Key Infrastructure,一种遵循标准的利用公钥加密技术为电子商务的开展提供一套安全基础平台的技术和规范。
MSP: Membership Service Provider,联盟链成员的证书管理,它定义了哪些RCA以及ICA在链里是可信任的,包括定义了cHlannel上的合作者。
org: orginazation,管理一系列合作企业的组织。
root@gan-virtual-machine:/# cd /opt/fabric-2.3.0/scripts/fabric-samples/test-network/
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh up
#fabric采用了leveldb数据库,key-value形式,且支持海量存储。用于存储交易状态
Starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb' with crypto from 'cryptogen'
LOCAL_VERSION=2.3.0
DOCKER_IMAGE_VERSION=1.4.8
Local fabric binaries and docker images are out of  sync. This may cause problems.#不知道为啥会报这个错,老师的没有报这个错诶
#这个是很重要的,用于创建证书的脚本
/opt/fabric-2.3.0/scripts/fabric-samples/test-network/../bin/cryptogen
#用上述那个文件的工具创建证书
Generating certificates using cryptogen tool
#创建组织1,其配置文件为./organizations/cryptogen/crypto-config-org1.yaml,可采用yaml验证工具验证,发现其实是json格式的内容。
#在这个配置文件中,Name: Org1 表示组织名称;Domain: org1.example.com  表示组织的域名;EnableNodeOUs: true 表示会在msp目录会生成config.yaml配置文件;另外,Users:  换行   Count: 1。修改这个Count的值,可以改变启动网络时创建的节点数,其实Count=1的话,会有一个管理员admin和一个普通用户,相当于两个节点。同理,Count=2相当于有三个节点。
Creating Org1 Identities
+ cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output=organizations
org1.example.com
+ res=0
+ #创建组织1
Creating Org2 Identities
+ cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output=organizations
org2.example.com
+ res=0
+ #创建order节点
Creating Orderer Org Identities
+ cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output=organizations
+ res=0
Generating CCP files for Org1 and Org2
WARNING: The COMPOSE_PROJECT_NAME variable is not set. Defaulting to a blank string.
Creating network "docker_test" with the default driver
Creating volume "docker_orderer.example.com" with default driver
Creating volume "docker_peer0.org1.example.com" with default driver
Creating volume "docker_peer0.org2.example.com" with default driver
Creating peer0.org2.example.com ... done
Creating peer0.org1.example.com ... done
Creating orderer.example.com    ... done
Creating cli                    ... done
CONTAINER ID   IMAGE                               COMMAND             CREATED         STATUS                  PORTS                                                                                  NAMES
ddde72be9f06   hyperledger/fabric-tools:latest     "/bin/bash"         1 second ago    Up Less than a second                                                                                          cli
af8a3cbd19a4   hyperledger/fabric-orderer:latest   "orderer"           2 seconds ago   Up 1 second             0.0.0.0:7050->7050/tcp, :::7050->7050/tcp, 0.0.0.0:7053->7053/tcp, :::7053->7053/tcp   orderer.example.com
27cb1ca1c1b2   hyperledger/fabric-peer:latest      "peer node start"   2 seconds ago   Up Less than a second   0.0.0.0:7051->7051/tcp, :::7051->7051/tcp                                              peer0.org1.example.com
6fcc31ed3930   hyperledger/fabric-peer:latest      "peer node start"   2 seconds ago   Up 1 second             0.0.0.0:9051->9051/tcp, :::9051->9051/tcp                                              peer0.org2.example.com


Complemento --- herramienta de verificación de archivos yaml: puede ver la información del certificado a http://www.bejson.com/validators/yaml_editor
inserte la descripción de la imagen aquí
través de . El maestro cambió el in a , re- networking, encontrará que hay dos usuarios más en la organización 1“vim connection-org1.yaml”
inserte la descripción de la imagen aquí
connection-org1.yamlCount=1Count=3./network.sh up
inserte la descripción de la imagen aquí

5.4 Creación de canales y despliegue de Chaincode

创建通道
	创建区块信息
	组织	加入通道
部署链码
	部署deploycc
	查看各种版本链码
	部署go版本链码
	apt、wget、curl对比
	Linux程序存储结构介绍
	wget下载的配置Go
	配置GOProxy代理

1. Crea un canal

//通过别名定义命令,这是“临时性”的,下一次打开Ubuntu就需要重新定义了
root@gan-virtual-machine:~#  alias ss='cd /opt/fabric-2.3.0/scripts/fabric-samples/test-network/'
root@gan-virtual-machine:~#  ss
//进入目录,启动网络
root@gan-virtual-machine :/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh up
//创建通道,可以后接-C指定通道别名,不指定通道名字的话,默认通道名为“mychannel”
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh createChannel
//创建通道时在终端显示的日志信息:主要包括两大部分内容——**创建区块信息和****组织加入通道**

2. Implementar el código de cadena

(1) Análisis de comandos de implementación de Chaincode

./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
1.-ccn 代表要部署目录简称
2.-ccp代表部署的路径
3.-ccl代表链码语言版本

(2) Ver varios códigos de cadena de versión
inserte la descripción de la imagen aquí

(3) Implementar el código de cadena de la versión go

./network.sh deployCC-ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
执行链码部署命令之后,会有报错提示
#通过executable file not found in $PATH异常可以发现缺少go命令,但是如果通过apt安装默认go的版本比较低,而Fabric2.3需要配套的go版本为>go1.15,因此我们直接采用wget或者官网下载新版本

(4) Comparación de apt, wget y curl

1. apt用于Debian系列(社区组织维护的发行版本)的linux系统,代表是Ubuntu,之前低版本是apt-get,下载与安装一气呵成!
2. wget:(World Wide Web get): 通过FTP或HTTP协议抓取的文件,默认下载到当前目录,可以通过**wget -O savePath** 要下载文件的url路径,来指定保存路径
3. curl:对应 TP, FTPS,HTTP, HTTPS, GOPHER,TELNET等协议的lib

(5) Introducción a la estructura de almacenamiento de programas de Linux

1./usr:系统级的目录,可以理解为C:/Windowsl。/usr/lib理解为c:/Windows/System32。
2./usr/local:用户级的程序目录,可以理解为C:/Progrem Filesl。用户自己编译的软件默认会安装到这个目录下。
3. /opt:用户级的程序目录,可以理解为D:.Software,opt有可选的意思,这里可以用于放置第三方大型软件(或游戏),当你不需要时,直接rm -rf掉即可。
源码放哪里
1./usr/src:系统级的源码目录。
2./usr/local/src:用户级的源码目录

(6) Instalar go
Debido a que queremos ejecutar la versión go de la demostración, necesitamos instalar el entorno de idioma go.
Método 1

#可以用apt,但是不推荐。因为如果部署新版本fabric的时候,很有可能阿里云源这边没有更新对应的go语言高版本
#但其实我试了一下,因为今年已经2022年啦~所以apt安装的已经是1.18版本了,够用于fabric2.3.0的了。但是我还是用了老师说的另一种方法
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# apt install golang-go
#如果go版本太低,部署链码会报错“go: unknown subcommand "mod"
#要卸载掉低版本的go,apt方式安装之后的卸载命令
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# apt remove golang-go

Método 2

#下载go源码。我看现在go版本已经到1.18.3了,所以我把连接替换了https://studygolang.com/dl/golang/go1.18.3.linux-amd64.tar.gz
# 如果前面那个连接不上的话,用这个网址 https://golang.google.cn/dl/go1.18.3.linux-amd64.tar.gz
wget https://studygolang.com/dl/golang/go1.15.4.linux-amd64.tar.gz
#解压文件到/usr/local中
tar xfz go1.15.4.linux-amd64.tar.gz -C /usr/local
#修改~/.bashrc
vim ~/.bashrc
#配置GOROOT,添加Gopath路径。如果不配GOROOT的话之后运行go version时会提示warning warning: GOPATH set to GOROOT (/usr/local/go) has no effectgo version go1.13.4 linux / amd64

export GOROOT=/usr/local/go
export GOPATH=/home/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH


#激活配置bachrc相当于当前用户环境变量配置  root@gan-virtual-machine:~# source ~/.bashrc  注意所在目录
source ~/.bashrc
go version #验证go是否安装成功,go version go1.18.3 linux/amd64

(7) Configure el proxy GOProxy
Cuando la versión especificada de GO se haya descargado anteriormente y las variables de entorno GOPATH y GOROOT estén configuradas, puede descargar los paquetes de dependencia relacionados con el código de cadena de github de acuerdo con el proceso de búsqueda, pero la última excepción es aún lanzado. , se puede ver a partir de la información anormal que el acceso a github.com ha expirado. En este momento, se debe configurar un agente para descargar los recursos de Github.

#可以直接利用命令添加
go env -w GOPROXY=https://goproxy.cn/
#也可以像之前配置GOROOT那样去/.barshrc文件中添加
export GOPROXY=https://goproxy.cn/

(8) Vuelva a implementar el código de cadena de la versión go

root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go

5.5 Instrucciones complementarias

1. Introducción al ecosistema de la cadena Fabric Consortium

El objetivo del proyecto Fabric es realizar el marco básico subyacente de una cadena de bloques autorizada general (Cadena autorizada). Para que sea adecuado para diferentes ocasiones, se utiliza una arquitectura modular para proporcionar componentes intercambiables y escalables, incluidos algoritmos de consenso, seguridad de cifrado , servicios digitales como activos, contratos inteligentes e identificación.
inserte la descripción de la imagen aquí

1.Fabric CALIPER:性能测试组件
2.Fabric Explorer:查看区块链信息组件
3. Fabric Composer:包含了开发构架,和工具的开源项目
4.Fabric Cello:支持定制(例如,大小,一致〉区块链请求、在裸机,虚拟云(例如,虚拟机,云数据云),容器集群方便部署自己联盟链

2. Revisión de pedidos importantes

  1. cryptogen: Genera estructura organizacional y archivos de identidad (certificados)
  2. configtxgen: generar bloques de configuración y transacciones de configuración
  3. configtxlator: leer información de configuración
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí

3. Complemento: cree un registro de solicitud de canal

La información de registro que se muestra en el terminal cuando se crea el canal: incluye principalmente tres partes principales: crear información de bloque y organizar el canal de unión y sincronizar la información del nodo .

另外补充Fabric命令手册:http://cw.hubwiz.com/card/c/fabric-command-manual/1/1/10/
Protobuf详细说明: https://www.jianshu.com/p/a24c88c0526a
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh createChannel Creating channel 'mychannel'.

If network is not up, starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb'
#创建了一个区块mychannel.block,存储目录为“./channel-artifacts/mychannel.block ”
Generating channel genesis block 'mychannel.block'
/opt/fabric-2.3.0/scripts/fabric-samples/test-network/../bin/configtxgen
#以“+”开头的语句是fabric命令,可以到Fabric命令手册网址去看对应命令的含义
+ configtxgen -profile TwoOrgsApplicationGenesis -outputBlock ./channel-artifacts/mychannel.block -channelID mychannel
2022-06-19 10:44:45.834 CST [common.tools.configtxgen] main -> INFO 001 Loading configuration
2022-06-19 10:44:45.844 CST [common.tools.configtxgen.localconfig] completeInitialization -> INFO 002 orderer type: etcdraft
2022-06-19 10:44:45.844 CST [common.tools.configtxgen.localconfig] completeInitialization -> INFO 003 Orderer.EtcdRaft.Options unset, setting to tick_interval:"500ms" election_tick:10 heartbeat_tick:1 max_inflight_blocks:5 snapshot_interval_size:16777216 
2022-06-19 10:44:45.845 CST [common.tools.configtxgen.localconfig] Load -> INFO 004 Loaded configuration: /opt/fabric-2.3.0/scripts/fabric-samples/test-network/configtx/configtx.yaml
2022-06-19 10:44:45.847 CST [common.tools.configtxgen] doOutputBlock -> INFO 005 Generating genesis block
2022-06-19 10:44:45.847 CST [common.tools.configtxgen] doOutputBlock -> INFO 006 Creating application channel genesis block
2022-06-19 10:44:45.848 CST [common.tools.configtxgen] doOutputBlock -> INFO 007 Writing genesis block
+ res=0
Creating channel mychannel
#区块创建完成之后,就调用组织,将组织1和组织2添加到通道中去。联盟链中,不通的通道是隔离开的,不通的组织必须在同一个通道里面才能相互访问并执行通道中的链码。
#下面是其加入的一些信息,包括组织的基本信息、验证、状态等。
Using organization 1
+ osnadmin channel join --channel-id mychannel --config-block ./channel-artifacts/mychannel.block -o localhost:7053 --ca-file /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --client-cert /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt --client-key /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.key
+ res=0
#20开头的状态表示成功
Status: 201
{
    
    
	"name": "mychannel",
	"url": "/participation/v1/channels/mychannel",
	"consensusRelation": "consenter",
	"status": "active",
	"height": 1
}

Channel 'mychannel' created
#将组织1加入通道
Joining org1 peer to the channel...
Using organization 1
+ peer channel join -b ./channel-artifacts/mychannel.block
+ res=0
2022-06-19 10:44:52.033 CST [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2022-06-19 10:44:52.071 CST [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
#将组织2加入通道
Joining org2 peer to the channel...
Using organization 2
+ peer channel join -b ./channel-artifacts/mychannel.block
+ res=0
2022-06-19 10:44:55.142 CST [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2022-06-19 10:44:55.180 CST [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
#添加锚节点到通道,通过锚节点可以实现节点同步
Setting anchor peer for org1...
Using organization 1
Fetching channel config for channel mychannel
Using organization 1
Fetching the most recent configuration block for the channel
+ peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
2022-06-19 02:44:55.425 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2022-06-19 02:44:55.429 UTC [cli.common] readBlock -> INFO 002 Received block: 0
2022-06-19 02:44:55.430 UTC [channelCmd] fetch -> INFO 003 Retrieving last config block: 0
2022-06-19 02:44:55.433 UTC [cli.common] readBlock -> INFO 004 Received block: 0
Decoding config block to JSON and isolating config to Org1MSPconfig.json
+ configtxlator proto_decode --input config_block.pb --type common.Block
+ jq '.data.data[0].payload.data.config'
+ jq '.channel_group.groups.Application.groups.Org1MSP.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "peer0.org1.example.com","port": 7051}]},"version": "0"}}' Org1MSPconfig.json
Generating anchor peer update transaction for Org1 on channel mychannel
+ configtxlator proto_encode --input Org1MSPconfig.json --type common.Config
+ configtxlator proto_encode --input Org1MSPmodified_config.json --type common.Config
+ configtxlator compute_update --channel_id mychannel --original original_config.pb --updated modified_config.pb
+ configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate
+ jq .
++ cat config_update.json
+ echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":{' '"channel_id":' '"mychannel",' '"isolated_data":' '{},' '"read_set":' '{' '"groups":' '{' '"Application":' '{' '"groups":' '{' '"Org1MSP":' '{' '"groups":' '{},' '"mod_policy":' '"",' '"policies":' '{' '"Admins":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Endorsement":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Readers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Writers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '}' '},' '"values":' '{' '"MSP":' '{' '"mod_policy":' '"",' '"value":' null, '"version":' '"0"' '}' '},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '},' '"write_set":' '{' '"groups":' '{' '"Application":' '{' '"groups":' '{' '"Org1MSP":' '{' '"groups":' '{},' '"mod_policy":' '"Admins",' '"policies":' '{' '"Admins":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Endorsement":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Readers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Writers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '}' '},' '"values":' '{' '"AnchorPeers":' '{' '"mod_policy":' '"Admins",' '"value":' '{' '"anchor_peers":' '[' '{' '"host":' '"peer0.org1.example.com",' '"port":' 7051 '}' ']' '},' '"version":' '"0"' '},' '"MSP":' '{' '"mod_policy":' '"",' '"value":' null, '"version":' '"0"' '}' '},' '"version":' '"1"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '}}}}'
+ configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope
2022-06-19 02:44:55.758 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2022-06-19 02:44:55.781 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
Anchor peer set for org 'Org1MSP' on channel 'mychannel'

Setting anchor peer for org2...
Using organization 2
Fetching channel config for channel mychannel
Using organization 2
Fetching the most recent configuration block for the channel
+ peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
2022-06-19 02:44:56.166 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2022-06-19 02:44:56.172 UTC [cli.common] readBlock -> INFO 002 Received block: 1
2022-06-19 02:44:56.173 UTC [channelCmd] fetch -> INFO 003 Retrieving last config block: 1
2022-06-19 02:44:56.175 UTC [cli.common] readBlock -> INFO 004 Received block: 1
Decoding config block to JSON and isolating config to Org2MSPconfig.json
+ configtxlator proto_decode --input config_block.pb --type common.Block
+ jq '.data.data[0].payload.data.config'
Generating anchor peer update transaction for Org2 on channel mychannel
+ jq '.channel_group.groups.Application.groups.Org2MSP.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "peer0.org2.example.com","port": 9051}]},"version": "0"}}' Org2MSPconfig.json
#configtxlator命令用来将fabric的数据结构在protobuf和JSON 之间进行转换,也可以用来创建配置更新。该命令可以启动一个REST 服务来通过HTTP暴露服务接口,也可以直接在命令行使用。
+ configtxlator proto_encode --input Org2MSPconfig.json --type common.Config
+ configtxlator proto_encode --input Org2MSPmodified_config.json --type common.Config
+ configtxlator compute_update --channel_id mychannel --original original_config.pb --updated modified_config.pb
+ configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate
+ jq .
++ cat config_update.json
+ echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":{' '"channel_id":' '"mychannel",' '"isolated_data":' '{},' '"read_set":' '{' '"groups":' '{' '"Application":' '{' '"groups":' '{' '"Org2MSP":' '{' '"groups":' '{},' '"mod_policy":' '"",' '"policies":' '{' '"Admins":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Endorsement":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Readers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Writers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '}' '},' '"values":' '{' '"MSP":' '{' '"mod_policy":' '"",' '"value":' null, '"version":' '"0"' '}' '},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '},' '"write_set":' '{' '"groups":' '{' '"Application":' '{' '"groups":' '{' '"Org2MSP":' '{' '"groups":' '{},' '"mod_policy":' '"Admins",' '"policies":' '{' '"Admins":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Endorsement":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Readers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '},' '"Writers":' '{' '"mod_policy":' '"",' '"policy":' null, '"version":' '"0"' '}' '},' '"values":' '{' '"AnchorPeers":' '{' '"mod_policy":' '"Admins",' '"value":' '{' '"anchor_peers":' '[' '{' '"host":' '"peer0.org2.example.com",' '"port":' 9051 '}' ']' '},' '"version":' '"0"' '},' '"MSP":' '{' '"mod_policy":' '"",' '"value":' null, '"version":' '"0"' '}' '},' '"version":' '"1"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '},' '"mod_policy":' '"",' '"policies":' '{},' '"values":' '{},' '"version":' '"0"' '}' '}}}}'
+ configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope
2022-06-19 02:44:56.504 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2022-06-19 02:44:56.531 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
Anchor peer set for org 'Org2MSP' on channel 'mychannel'
#提示:通道'mychannel'创建成功
Channel 'mychannel' joined

4. Suplemento: Implementar registro de solicitud de código de cadena

La información de registro que se muestra en el terminal al implementar el código de cadena: incluye principalmente descargar el código de cadena , descomprimir el código de cadena , implementar e instalar el código de cadena en el canal , enviar el código de cadena al nodo de aprobación y enviarlo al pedido nodo solo después de que la aprobación sea exitosa .

root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
deploying chaincode on channel 'mychannel'
executing with the following
- CHANNEL_NAME: mychannel
- CC_NAME: basic
- CC_SRC_PATH: ../asset-transfer-basic/chaincode-go
- CC_SRC_LANGUAGE: go
- CC_VERSION: 1.0
- CC_SEQUENCE: 1
- CC_END_POLICY: NA
- CC_COLL_CONFIG: NA
- CC_INIT_FCN: NA
- DELAY: 3
- MAX_RETRY: 5
- VERBOSE: false
Vendoring Go dependencies at ../asset-transfer-basic/chaincode-go
/opt/fabric-2.3.0/scripts/fabric-samples/asset-transfer-basic/chaincode-go /opt/fabric-2.3.0/scripts/fabric-samples/test-network
go: downloading github.com/hyperledger/fabric-contract-api-go v1.1.0
go: downloading github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212
go: downloading github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e
go: downloading github.com/stretchr/testify v1.5.1
go: downloading github.com/golang/protobuf v1.3.2
go: downloading google.golang.org/grpc v1.23.0
go: downloading github.com/xeipuuv/gojsonschema v1.2.0
go: downloading github.com/go-openapi/spec v0.19.4
go: downloading github.com/gobuffalo/packr v1.30.1
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading gopkg.in/yaml.v2 v2.2.8
go: downloading google.golang.org/genproto v0.0.0-20180831171423-11092d34479b
go: downloading golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
go: downloading github.com/go-openapi/jsonpointer v0.19.3
go: downloading github.com/go-openapi/jsonreference v0.19.2
go: downloading github.com/go-openapi/swag v0.19.5
go: downloading github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415
go: downloading github.com/gobuffalo/envy v1.7.0
go: downloading github.com/gobuffalo/packd v0.3.0
go: downloading golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542
go: downloading github.com/PuerkitoBio/purell v1.1.1
go: downloading github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e
go: downloading github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f
go: downloading github.com/joho/godotenv v1.3.0
go: downloading github.com/rogpeppe/go-internal v1.3.0
go: downloading github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
go: downloading golang.org/x/text v0.3.2
/opt/fabric-2.3.0/scripts/fabric-samples/test-network
#链码下载已完成
Finished vendoring Go dependencies
#打包链码并生成打包文件
+ peer lifecycle chaincode package basic.tar.gz --path ../asset-transfer-basic/chaincode-go --lang golang --label basic_1.0
+ res=0
Chaincode is packaged
#给组织1,2安装链码
Installing chaincode on peer0.org1...
Using organization 1
#在指定peer节点上安装链码
+ peer lifecycle chaincode install basic.tar.gz
+ res=0
2022-06-19 13:57:09.511 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nJbasic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad\022\tbasic_1.0" > 
2022-06-19 13:57:09.511 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: basic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad
Chaincode is installed on peer0.org1

Install chaincode on peer0.org2...
Using organization 2
+ peer lifecycle chaincode install basic.tar.gz
+ res=0
2022-06-19 13:57:24.197 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nJbasic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad\022\tbasic_1.0" > 
2022-06-19 13:57:24.197 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: basic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad
Chaincode is installed on peer0.org2


Using organization 1
#查询指定peer节点上已经安装的链码
+ peer lifecycle chaincode queryinstalled
+ res=0
Installed chaincodes on peer:
Package ID: basic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad, Label: basic_1.0
Query installed successful on peer0.org1 on channel
Using organization 1
#机构审批链码,即提交链码到背书节点,背书机构进行审批。其中“--tls --cafile”参数后面跟着证书,指明审批机构要具有相应资质,即指明证书文件。
+ peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name basic --version 1.0 --package-id basic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad --sequence 1
+ res=0
2022-06-19 13:57:26.409 CST [chaincodeCmd] ClientWait -> INFO 001 txid [790e1b59b00f895df8b49474d836a5636ddfe1138aff24923b5509869bd8eb26] committed with status (VALID) at localhost:7051
#提示:链码在组织1节点1审批通过
Chaincode definition approved on peer0.org1 on channel 'mychannel'
Using organization 1
Checking the commit readiness of the chaincode definition on peer0.org1 on channel 'mychannel'...
Attempting to check the commit readiness of the chaincode definition on peer0.org1, Retry after 3 seconds.
#检查指定的链码是否可以向通道提交。
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 1.0 --sequence 1 --output json
+ res=0
{
    
    
	"approvals": {
    
    
		"Org1MSP": true,#表示链码审批在组织1通过
		"Org2MSP": false#表示链码审批在组织2还没通过
	}
}
Checking the commit readiness of the chaincode definition successful on peer0.org1 on channel 'mychannel'
Using organization 2
Checking the commit readiness of the chaincode definition on peer0.org2 on channel 'mychannel'...
Attempting to check the commit readiness of the chaincode definition on peer0.org2, Retry after 3 seconds.
#检查指定的链码是否可以向通道提交。
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 1.0 --sequence 1 --output json
+ res=0
{
    
    
	"approvals": {
    
    #这时候组织2还没执行审批操作,所以和签名的结果一样,组织1通过链码审批,而组织2还没有
		"Org1MSP": true,
		"Org2MSP": false
	}
}
Checking the commit readiness of the chaincode definition successful on peer0.org2 on channel 'mychannel'
Using organization 2
#向组织2提交链码的审核操作,和向组织1提交的步骤是类似的
+ peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name basic --version 1.0 --package-id basic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad --sequence 1
+ res=0
2022-06-19 13:57:34.732 CST [chaincodeCmd] ClientWait -> INFO 001 txid [a3959f8b65cdf8dff719c9e5c12a0a713d116ddb4f4d5f56d295af2b62478fb2] committed with status (VALID) at localhost:9051
#链码在组织2审批通过
Chaincode definition approved on peer0.org2 on channel 'mychannel'
Using organization 1
Checking the commit readiness of the chaincode definition on peer0.org1 on channel 'mychannel'...
Attempting to check the commit readiness of the chaincode definition on peer0.org1, Retry after 3 seconds.
#这时候再检查一下指定的链码是否可以向通道提交。
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 1.0 --sequence 1 --output json
+ res=0
{
    
    
	"approvals": {
    
    #这时候组织1和组织2都通过了链码审批
		"Org1MSP": true,
		"Org2MSP": true
	}
}
Checking the commit readiness of the chaincode definition successful on peer0.org1 on channel 'mychannel'
Using organization 2
Checking the commit readiness of the chaincode definition on peer0.org2 on channel 'mychannel'...
Attempting to check the commit readiness of the chaincode definition on peer0.org2, Retry after 3 seconds.
#同样,在组织2也再检查一下指定的链码是否可以向通道提交。
+ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 1.0 --sequence 1 --output json
+ res=0
{
    
    
	"approvals": {
    
    #结果也是:组织1和组织2都通过了链码审批
		"Org1MSP": true,
		"Org2MSP": true
	}
}
Checking the commit readiness of the chaincode definition successful on peer0.org2 on channel 'mychannel'

#在上面,组织1和2都完成了对链码的审批操作并且通过之后

Using organization 1
Using organization 2
# 向指定通道提交链码,提交给order(排序节点)
+ peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name basic --peerAddresses localhost:7051 --tlsRootCertFiles /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1.0 --sequence 1
+ res=0
2022-06-19 13:57:43.304 CST [chaincodeCmd] ClientWait -> INFO 001 txid [8f5eafd1b019ffc861a890af9944f5ce74b6ac13795301f5d531a610fdc21c13] committed with status (VALID) at localhost:9051
2022-06-19 13:57:43.330 CST [chaincodeCmd] ClientWait -> INFO 002 txid [8f5eafd1b019ffc861a890af9944f5ce74b6ac13795301f5d531a610fdc21c13] committed with status (VALID) at localhost:7051
#这时候提示,提交到排序节点的操作已经成功啦
Chaincode definition committed on channel 'mychannel'



Using organization 1
Querying chaincode definition on peer0.org1 on channel 'mychannel'...
Attempting to Query committed status on peer0.org1, Retry after 3 seconds.
#在组织1这边查询指定通道上已经提交的链码
+ peer lifecycle chaincode querycommitted --channelID mychannel --name basic
+ res=0
Committed chaincode definition for chaincode 'basic' on channel 'mychannel':
Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true]
#查询结果是,链码已经在组织1上定义成功
Query chaincode definition successful on peer0.org1 on channel 'mychannel'
Using organization 2
Querying chaincode definition on peer0.org2 on channel 'mychannel'...
Attempting to Query committed status on peer0.org2, Retry after 3 seconds.
#同理,在组织2这边查询指定通道上已经提交的链码
+ peer lifecycle chaincode querycommitted --channelID mychannel --name basic
+ res=0
Committed chaincode definition for chaincode 'basic' on channel 'mychannel':
Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true]
#查询结果是,链码已经在组织2上也定义成功
Query chaincode definition successful on peer0.org2 on channel 'mychannel'
Chaincode initialization is not required

5. Cree manualmente el entorno de pares

Ejecutar el comando peer directamente en la línea de comando arrojará una excepción. La razón es que es fácil de entender que todas las operaciones deben especificar qué nodo de organización se usa en qué canal, y estos nodos deben tener permisos legales.
inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí
//Primero asegurate que estas en el directorio test-network, y configura las siguientes variables de entorno (esta vez para facilitar la configuracion de variables temporales)
//Configuracion de variables temporales: Las variables de entorno configuradas de esta forma son temporales, es desaparecerá en el siguiente arranque. Esta vez es solo para la conveniencia de que los estudiantes entiendan el proceso de configuración.

PWD='/opt/fabric-2.3.0/scripts/fabric-samples/test-network'
export PATH=${
    
    PWD}/../bin:$PATH    #这时候,在终端输入peer,已经有用法提示了,但是现在还不能正常使用,继续配置。还有配置组织、证书等相关信息配置文件
export FABRIC_CFG_PATH=${
    
    PWD}/../config/
export CORE_PEER_TLS_ENABLED=true
#启用对服务端的TLS身份验证
export CORE_PEER_LOCALMSPID="Org1MSP"
#两行指明了org1的加密所用的信息
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
 open /opt/fabric-2.3.0/scripts/fabric-samples/config/tls/ca.crt: no such file or directory

export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
#当前组织节点IP与端口
export CORE_PEER_ADDRESS=localhost:7051
#注意:这里的端口配置也要区分节点的哟——排序节点7050,第一个组织节点7051,第二个组织节点9051

#补充————fabric环境变量手册:http://cw.hubwiz.com/card/c/fabric-env-manual/1/6/1/


PWD='/opt/fabric-2.3.0/scripts/fabric-samples/test-network'
export PATH=${
    
    PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${
    
    PWD}/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051

Las variables de entorno del nodo de la organización 1 se configuran arriba. A continuación, puede usar la identidad del nodo de la organización 1 para realizar la verificación de comandos de pares:

peer channel list
peer lifecycle chaincode queryinstalled
peer channel getinfo -c mychannel

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

6. Implementación de implementación de casos oficiales de Fabcar

6.1 adquisición e instalación del proyecto fabcar

contrato de instalación y adquisición de código de cadena fabcar análisis de la estructura del proyecto
fabcar fabcar es un código de cadena descentralizado para transacciones de automóviles (automóviles).El proyecto admite consultas de un solo automóvil, adición, adición de consulta, adición de actualización, eliminación y otras operaciones. Realiza las funciones de agregar, borrar, verificar y modificar el código general de la cadena.

inserte la descripción de la imagen aquí
análisis de dependencia go.mod
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

(1) Obtenga el paquete de dependencias de Fabcar

Una vez completada la ejecución, habrá más proveedores en la carpeta actual, que es la carpeta donde se ubican las dependencias de contratos inteligentes.

#配置go代理。然后已经配置可以跳过
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# go env -w GOPROXY=https://goproxy.cn
#刚开始执行的时候,遇到报错“missing go.sum entry; to add it:”。通过执行命令:“go mod tidy”后再执行“go mod vendor”
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# go mod vendor

(2) Agregar registro de host

/etc/hosts, nombre de host y archivo de configuración de ip, en la red de la ciudad de oficina o INTERNET, cada host tiene una dirección P, que distingue a cada host y puede negar ip para la comunicación. Pero es inconveniente recordar 100 millones en su mundo, y todos tienen un nombre de dominio. En una red de área local, cada cámara tiene un nombre de host, que se utiliza para distinguir los hosts y facilitar el acceso mutuo.

#我们当前配置都是在本地的,如果以后大项目部署之后,不同的节点对应不同的ip地址,道理都是一样的,在/etc/hosts中配置其映射关系即可。
#这个项目,我们就把下面三条语句加入/etc/hosts文件中即可
127.0.0.1 orderer.example.com
127.0.0.1 peer0.org1.example.com
127.0.0.1 peer0.org2.example.com

root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# vim /etc/hosts

inserte la descripción de la imagen aquí

Operaciones de envío y revisión de código de cadena ORG1

(3) Configurar las variables de entorno y la información de autenticación de la organización

Este es el punto 5 en la Sección 5.5: Construya manualmente las variables de entorno configuradas en el entorno de pares. El contenido es el mismo ~

#后续很多地方会用到test-network目录,因此把它赋值到PwD变量中
PWD='/opt/fabric-2.3.0/scripts/fabric-samples/test-network'
#把${PwD}/ ../bin临时注册到环境变量中, peer命令可以执行
export PATH=${
    
    PWD}/../bin:$PATH
#还需要设置FABRIc_CF6_PATH路径指向fabric-samples中的core.yaml文件路径
export FABRIC_CFG_PATH=${
    
    PWD}/../config/
#下载是配置组织的证书和相关信息
# http:/ / cw.hgbwiz.com/card/c/fabric-env-manual/1/3/12/
#启用对服务端的TLs身份验证(TLS 由ssL发展而来)是一种身份验证机制
export CORE_PEER_TLS_ENABLED=true
#组织1的MSP的标识ID
export CORE_PEER_LOCALMSPID="Org1MSP"
#获取组织节点证书的位置
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
#获取访问账户的msp证书
export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
#同一机构中其他Peer节点要连接此节点需指定的P2P连接地址
export CORE_PEER_ADDRESS=localhost:7051
#注意:这里的端口配置也要区分节点的哟——排序节点7050,第一个组织节点7051,第二个组织节点9051


#下面是方便复制粘贴到终端命令行直接执行的版本
PWD='/opt/fabric-2.3.0/scripts/fabric-samples/test-network'
export PATH=${
    
    PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${
    
    PWD}/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051

(4) Contrato de código de cadena de paquetes

#前提:fabric网络启动且通道创建完成  可以只使用一个命令./network.sh up createChannel
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh up
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# ./network.sh createChannel

root@ubuntu:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# 
#参数介绍:
#(1)package outputfile #打包输出文件名
#(2)path string #链码源代码路径
#(3)lang string # 链码开发语言,默认:golang
#(4)label string #链码标识,相当于链码的名字


root@ubuntu:/opt/fabric-2.3. /scripts /fabric-samples /fabcar/go# peer lifecycle chaincode package fabcar.tar.gz --path /opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go --label fabcar_1
#1s查看当前目录,发现有了一个fabcar.tar.gz文件
root@ubuntu:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# ls
#上一条语句执行结果:fabcar.go fabcar.tar.gz go.mod go.sum runfabcar.sh vendor


(5) Instalar el código de cadena

#安装智能合约(时间会比较久,耐心稍等)
peer lifecycle chaincode install fabcar.tar.gz
#2022-06-19 18:44:00.603 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nIfabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b\022\010fabcar_1" > 
#2022-06-19 18:44:00.603 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: fabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b
#这个hash值很重要,下面的步骤要用===》   fabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b

inserte la descripción de la imagen aquí
En este momento, puede consultar el código de cadena instalado: la organización 1 ha completado la operación de instalación del código de cadena (la organización 2 aún no lo ha hecho) y se generará un valor hash único para identificar este código de cadena después de que se complete la instalación. En este punto, también podemos usar el siguiente comando para ver los archivos de código de cadena que se han instalado en el nodo actual.

root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# peer lifecycle chaincode queryinstalled
Installed chaincodes on peer:
Package ID: fabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b, Label: fabcar_1
#peer lifecycle chaincode queryinstalled:如果出现两个链码名字,其中第一个basic_1.0是上一章节官方自动安装的deployCC案例,而fabcar_1是本章节手动安装的案例。我把上一章节自动安装的删掉了,所以我查询结果只有一个~

Si no se elimina el código de cadena instalado anteriormente basic, el resultado de la consulta debería ser el siguiente:
inserte la descripción de la imagen aquí

(6) Operaciones de revisión y envío de código de cadena ORG1

Una vez que el paquete de código de cadena está instalado en el nodo par, el nodo de respaldo puede aprobar la definición de código de cadena. Solo después de pasar todos los nodos de respaldo o la mayoría de los nodos de respaldo (diferentes políticas de respaldo tienen requisitos para la cantidad de pases), las aprobaciones posteriores son El código de cadena se puede instanciar e invocar.

root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name fabcar --version 1.0 --package-id fabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b --sequence 1

#1.-o localhost:7050#排序节点地址
#2.--tls #排序节点启动时是否启动tls
#3. --ordererTLSHostnameOverride orderer.example.com #验证TLS连接时使用的排序节点主机名
#4. --cafile PEM编码的CA证书文件路径
#5.--channellD mychannel# 通道ID
#6. --name fabcar#链码名称
#7. --version 1.0#链码版本
#8.--package-id fabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b  #链码安装包标识ID
#9. --sequence 1#通道链码定义序列号,默认值:1


#运行结果
#2022-06-19 19:08:44.426 CST [chaincodeCmd] ClientWait -> INFO 001 txid [5a9ae2ad8e9dcc9d03201df1cad3dd11664eb2ec3eb041b19b98fcb78ba6148c] committed with status (VALID) at localhost:7051
#解释:链码审核的过程本身也是一次交易


#如果已经安装上basic链码demo,再装这个fabcar的话,记得修改链码ID为“fabcar_1:1146b4b491871bf18b23dd67dd8cc058655b36cc0e2274f165ed06b796a8f276 ”。反正注意--package-id 后面跟的内容要替换成自己安装成功之后日志显示中出现的hash~
#peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name fabcar --version 1.0 --package-id fabcar_1:1146b4b491871bf18b23dd67dd8cc058655b36cc0e2274f165ed06b796a8f276  --sequence 1

ORG1 Consulta para ver el estado de revisión del código de la cadena e intenta enviar el código de la cadena al canal
[Este paso no es necesario, el maestro necesita el consentimiento de todas las organizaciones para enviarlo con éxito, por lo que intentar enviarlo ahora es solo para hacernos más veo claramente que no es posible ahora Enviado con éxito. ]
Dado que la organización 2 aún no ha superado la auditoría de fabcar, el envío de la organización 1 fallará y finalizará más tarde. También puede utilizar el siguiente comando para ver las organizaciones cuyos códigos de cadena se han auditado.

 root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name fabcar --version 1.0 --sequence 1 --output json

#1. --channellID通道ID
#2.--name链码名称
#3. --version链码版本
#4. --output结果输出格式

#执行结果:组织1已经对链码审核且通过。组织2这边还没通过。
{
    
    
	"approvals": {
    
    
		"Org1MSP": true,
		"Org2MSP": false
	}
}

No hay ningún código de cadena que haya sido aprobado por la organización 2, enviémoslo para probarlo

peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name fabcar --peerAddresses localhost:7051 --tlsRootCertFiles /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1.0 --sequence 1

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
Más tarde descubrí por qué, porque pegué la ubicación del certificado incorrecta para la organización 2 y la ubicación del certificado para la organización 1 ~ Lo he estado mirando durante mucho tiempo, Dios mío, no es fácil encontrar un error.

Operaciones de revisión y envío de código de cadena ORG2

Anteriormente, la información de configuración de ORG1 se configuraba temporalmente en la línea de comando restante del shell y desaparecía después de salir de la línea de comando. Por lo tanto, la información de ORG2 se puede configurar en el archivo ~/.bashrc. Este archivo se cargará automáticamente cada vez que el usuario actual inicia la línea de comando. .
De hecho, las operaciones en el lado de la organización 2 son las mismas que los comandos para la organización 1 excepto por la información de configuración diferente.

(7) Configurar la información de la organización ORG2

Ingrese al modo de edición a través de vim ~/.bashrc, y agregue la información de ORG2 al final, guarde y salga, cierre la línea de comando y reiníciela.Si aún se puede reconocer la variable, la configuración es exitosa.

PWD='/opt/fabric-2.3.0/scripts/fabric-samples/test-network'
#把$(PMD}/ ../bin临时注册到环境变量中, peer命令可以执行
export PATH=${
    
    PWD}/../bin:$PATH
#还需要设置FABRIc_CF6_PATH路径指向fabric-samples中的core.yaml文件路径
export FABRIC_CFG_PATH=${
    
    PWD}/../config/
#下载是配置组织的证书和相关信息
# http://cw.hubwiz.com/card/c/fabric-env-manual/1/3/12/
#启用对服务端的TLS身份验证(TLS 由 ssL发展而来)是一种身份验证机制
export CORE_PEER_TLS_ENABLED=true
#组织1的NSP的标识ID
export CORE_PEER_LOCALMSPID="Org2MSP"
#获取组织节点证书的位置
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 
#同一机构中其他Peer节点要连接此节点需指定的P2P连接地址
export CORE_PEER_ADDRESS=localhost:9051


#下面是方便复制粘贴到终端命令行直接执行的版本
PWD='/opt/fabric-2.3.0/scripts/fabric-samples/test-network'
export PATH=${
    
    PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${
    
    PWD}/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051

(8) código de cadena de instalación ORG2

#进入........fabcar/go,发现有了一个fabcar.tar.gz文件,表明链码已经打包好了
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# ls
fabcar.go  fabcar.tar.gz  go.mod  go.sum  runfabcar.sh  vendor
#安装链码,结果中链码名字及其hash值与组织1安装之后的结果一样。
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# peer lifecycle chaincode install fabcar.tar.gz
#运行结果
#2022-06-19 21:07:18.548 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nIfabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b\022\010fabcar_1" > 
#2022-06-19 21:07:18.548 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: fabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b

(9) Revisión del código de la cadena

Una vez que el paquete de código de cadena está instalado en el nodo del mismo nivel, el nodo de respaldo puede aprobar la definición de código de cadena. Solo después de pasar todos los nodos de respaldo o la mayoría de los nodos de respaldo con diferentes políticas de respaldo, existe un requisito para la cantidad de pases para pasar el revisión.El código de cadena puede ser instanciado e invocado.Dado que el núcleo de declaración de código de cadena configura la información relevante del nodo de pedido, este código es exactamente el mismo que ORG1.

root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name fabcar --version 1.0 --package-id fabcar_1:4e7996bf4058869807f239b859a97dc609ed19e2fcfd10f1bf86540f6dd3cf5b,Label: fabcar_1 --sequence 1

#运行结果
#2022-06-19 21:09:43.517 CST [chaincodeCmd] ClientWait -> INFO 001 txid [39a98e0f41600cb2dce82fc05b70e50ea3aa28025c2d134860d725864865eacb] committed with status (VALID) at localhost:9051

(10) Ver el estado de revisión del código de cadena y enviar el código de cadena

ORG2 verifica el estado de revisión del código de cadena y envía el código de cadena. En este momento, ORG1 y ORG2 han pasado la revisión al mismo tiempo, y esta vez finalmente se puede enviar el código de cadena

#查看链码审核状态
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/fabcar/go# peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name fabcar --version 1.0 --sequence 1 --output json
#运行结果
{
    
    
	"approvals": {
    
    
		"Org1MSP": true,
		"Org2MSP": true
	}
}


El código de la cadena finalmente se envía al nodo de la organización
. El código es el mismo que ORG1, pero ORG2 no pudo pasar la revisión anterior, lo que resultó en la falla del primer envío del código de la cadena.

peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name fabcar --peerAddresses localhost:7051 --tlsRootCertFiles /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles /opt/fabric-2.3.0/scripts/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1.0 --sequence 1


#返回的结果如下,如图可以看出链码已经提交成功,由于已经同时通过了ORG1,ORG2的审核,因此链码可以提交给排序节点
# 2022-06-19 21:57:01.524 CST [chaincodeCmd] ClientWait -> INFO 001 txid [26be9ccdddf6de2c5994406ac2411a5e478112d6f8d005a17be293939be11c89] committed with status (VALID) at localhost:7051
#2022-06-19 21:57:01.527 CST [chaincodeCmd] ClientWait -> INFO 002 txid [26be9ccdddf6de2c5994406ac2411a5e478112d6f8d005a17be293939be11c89] committed with status (VALID) at localhost:9051

inserte la descripción de la imagen aquí

#这样子是没起来的,要这些容器的状态都变成Up才行~
#可以看到后面的invoke我就执行不了,本质原因就是链码容器没启起来~
root@gan-virtual-machine:/opt/fabric-2.3.0/scripts/fabric-samples/test-network# docker ps -a
CONTAINER ID   IMAGE                                                                                                                                                                    COMMAND                  CREATED         STATUS         PORTS                                                                                  NAMES
7af1a01a5b75   dev-peer0.org2.example.com-basic_1.0-4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad-6c0d5b0755cb92ed5555bd2e8a8765a6f425d1ed5ed9a90e625e01939e2113be   "chaincode -peer.add…"   6 minutes ago   Created                                                                                               dev-peer0.org2.example.com-basic_1.0-4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad
dc3cc5f8e9ad   dev-peer0.org1.example.com-basic_1.0-4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad-42f57faac8360472e47cbbbf3940e81bba83439702d085878d148089a1b213ca   "chaincode -peer.add…"   6 minutes ago   Created                                                                                               dev-peer0.org1.example.com-basic_1.0-4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad
9d7a9f992eae   hyperledger/fabric-tools:latest                                                                                                                                          "/bin/bash"              9 minutes ago   Up 9 minutes                                                                                          cli
b474ffa6a73e   hyperledger/fabric-peer:latest                                                                                                                                           "peer node start"        9 minutes ago   Up 9 minutes   7051/tcp, 0.0.0.0:9051->9051/tcp, :::9051->9051/tcp                                    peer0.org2.example.com
e6a08f02b506   hyperledger/fabric-peer:latest                                                                                                                                           "peer node start"        9 minutes ago   Up 9 minutes   0.0.0.0:7051->7051/tcp, :::7051->7051/tcp                                              peer0.org1.example.com
8adfb840488c   hyperledger/fabric-orderer:latest                                                                                                                                        "orderer"                9 minutes ago   Up 9 minutes   0.0.0.0:7050->7050/tcp, :::7050->7050/tcp, 0.0.0.0:7053->7053/tcp, :::7053->7053/tcp   orderer.example.com

Adiciones, eliminaciones y cambios de código de cadena

Requisito previo: Los pasos para el envío exitoso del capítulo anterior todavía están allí, o puede reinstalar el contenido del capítulo anterior y hacerlo de nuevo ~ configure las variables de entorno y la información de certificación de la organización, empaquete el código de la cadena, organice el código de la cadena de auditoría del nodo y todos los nodos de la organización pasan la auditoría Después de enviar el código de cadena, el código de cadena se respalda correctamente.
A continuación, tratamos de llamar a métodos como agregar, eliminar, modificar y verificar:
(11) Inicialización

#采用invoke,调用链码的InitLedger方法初始化数据
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $cafile -C mychannel -n fabcar --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $org1_CertFiles --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $org2_CertFiles -c '{"Args":["InitLedger"]}'
#参数解释
#-o 排序服务器节点
#--cafile排序节点的PEM证书文件路径
#-C通道名称
#-n链码名称
#-peerAddresses锚节点的地址
#--tlsRootCertFiles前面peerAddresses的TLS证书路径
#-c链码的方法和调用方法的参数


#备注,其中
$cafile = /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
$org1_CertFiles=/opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
$org2_CertFiles=/opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt


Reportó un error ~ Siento que he revisado cada letra, ¡no debería ser un problema! Pero ¿por qué se informa el error? Lo
inserte la descripción de la imagen aquí
verifiqué y el canal existe.
inserte la descripción de la imagen aquí
Esta solución no funciona y no resuelve el problema ~

#搜到一个解决方案,试一下不知道能不能成功
docker stop $(docker ps -aq)
docker rm $(docker ps -aq)
docker rmi -f $(docker image|grep "dev-" |awk '{print $3}')
然后重新执行一下

[ID de pregunta: 202206202151] docker ps -aEl estado del contenedor que consulté con " " es Created. Más tarde, mi amigo sugirió que usara la versión 2.2.5 para permitir el contenedor de código de cadena Up, pero todavía no sé qué lo causó ~
inserte la descripción de la imagen aquí

(12) Consulta

#采用query调用链码中的QueryCar方法查询单部汽车
peer chaincode query -C mychannel -n fabcar -c '{"Args":["QueryCar","CAR0"]}'
#果然,上一步初始化没有成功是查询不了的,但如果上一步成功是可以查询的

(13) Nuevo

#采用invoke调用链码的CreateCar新增一辆汽车
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $cafile -C mychannel -n fabcar --peerAddresses peer0O.org1.example.com:7051 --tlsRootCertFiles $org1_CertFiles --peerAddresses peer0.org2.example.com:9051--tlsRootCertFiles $org2_CertFiles -c '{"Args":["CreateCar","CAR10","GEELY","Borui","Blue","Yujialing"]}'

(14) Nueva consulta

#采用query调用链码中的QueryCar方法查询新增的汽车
peer chaincode query -C mychannel -n fabcar -c '{
    
    " Args":["QueryCar" , "CAR10"]}

(15) Modificación

#采用invoke调用链码的ChangeCarOwner 更新汽车所有者
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $cafile -C mychannel
-n fabcar --peerAddresses
peer0.org1.example.com:7051 --tlsRootCertFiles $org1_CertFiles --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $org2_CertFiles -c '{
    
    "Args":["ChangeCarOwner","CAR10","小岳岳"]}

6.2 Escriba su propio código de cadena

#编写链码,存为atcc


package main

import (
"encoding/json"
"fmt"
"log"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

//SmartContract provides functions for managing an Asset
type SmartContract struct {
    
    
	contractapi.Contract
}


type Asset struct {
    
    
	ID string `json: "ID"`
	Owner string `json : "owner"`
	Value int`json : "Value"`
}


func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
    
    
	assets := []Asset{
    
    
		{
    
    ID: "asset1", Owner: "ZhangSan", Value: 300},
		{
    
    ID: "asset2",Owner: "Lisi", Value: 400},
		{
    
    ID: "asset3",Owner: "Klay", Value: 500},
	}

	for _, asset := range assets {
    
    
		assetjSON, err := json.Marshal(asset)
		if err != nil {
    
    
			return err
		}

		err = ctx.GetStub().PutState(asset.ID,assetjSON)
		if err != nil {
    
    
			return fmt.Errorf( "failed to put to world state. %v"  , err)
		}
	}
	return nil
}


func(s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error){
    
    
	assetJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
    
    
		return nil, fmt.Errorf("failed to read from world state: %v" , err)
	}
	if assetJSON == nil {
    
    
		return nil, fmt.Errorf( "the asset %s does not exist", id)
	}

	var asset Asset
	err = json. Unmarshal(assetJSON, &asset)
	if err != nil {
    
    
		return nil, err
	}
	return &asset, nil
}


func(s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error){
    
    
// range query with empty string for startKey and endKey does an
// open-ended query of all assets in the chaincode namespace
	resultsIterator,err := ctx.GetStub().GetStateByRange("", "")
	if err != nil {
    
    
		return nil,err
	}
	defer resultsIterator.Close()
	
	var assets []*Asset
	for resultsIterator.HasNext() {
    
    
		queryResponse, err := resultsIterator.Next()
		if err != nil {
    
    
			return nil, err
		}
		var asset Asset
		err = json.Unmarshal(queryResponse.Value, &asset)
		if err != nil {
    
     
			return nil, err
		}
		assets = append(assets, &asset)
	}
	return assets, nil
}


func main() {
    
    
	assetChaincode, err := contractapi.NewChaincode(&SmartContract{
    
    })
	if err != nil {
    
    
		log.Panicf("Error creating asset-transfer-basic chaincode: %v" , err)
	}
	if err := assetChaincode.Start(); err != nil {
    
    
		log. Panicf("Error starting asset-transfer-basic chaincode:%v" , err)
	}
}

Esta parte se aprendió después de que probé con éxito fabcar con la versión 2.2.5, por lo que la ejecuté directamente en función de la implementación anterior de demostraciones de chaincode básicas y fabcar. Por lo tanto, se omiten los pasos de cambiar el archivo de hosts y organizar la configuración del entorno de información.

//在配置了组织1信息的终端上执行:
//创建链码文件存放目录atcc,并新建atcc.go,写入上述的go语言链码代码段
//我选择在chaincode目录下创建的,这个创建在哪里没关系,自己喜欢就好~【在配置了组织1信息的终端上执行或者配置了组织1信息的终端上执行都可以】
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode# mkdir atcc
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode# cd atcc
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# vim atcc.go
//获取链码依赖
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# go mod init atcc.go 
//go: creating new go.mod: module atcc.go
//go: to add module requirements and sums:
//	go mod tidy             这里它提示我要用go mod tidy,如果执行之后是正常发现了可拉取的依赖包的话,没有这个提醒可以不用执行这句命令          
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# go mod tidy
//go: finding module for package github.com/hyperledger/fabric-contract-api-go/contractapi
//go: found github.com/hyperledger/fabric-contract-api-go/contractapi in github.com/hyperledger/fabric-contract-api-go v1.1.1
//拉取依赖并下载到vendor目录下存储
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# go mod vendor
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# ls
//atcc.go  go.mod  go.sum  vendor   这时候可以看到,依赖下载成功啦
//通过组织1打包链码
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer lifecycle chaincode package atcc.tar.gz --path /opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc --label atcc_1
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# ls
//atcc.go  atcc.tar.gz  go.mod  go.sum  vendor   可以看到打包完成后,目录下增加了atcc.tar.gz文件
//然后我们将链码包atcc.tar.gz安装到组织1节点上,下面所示:安装成功后,返回status 200且会输出链码包标识ID——“atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48”
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer lifecycle chaincode install atcc.tar.gz
//2022-06-21 19:38:06.193 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGatcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48\022\006atcc_1" > 
//2022-06-21 19:38:06.193 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48
//然后组织1节点要审核链码,通过审核后会输出交易ID——“txid [117c564ffca5a75299a84a613a4c33380c6ed1e6744f2ca85f20efa9823252fd]”
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name atcc --version 1.0 --package-id atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48 --sequence 1
//2022-06-21 19:39:19.540 CST [chaincodeCmd] ClientWait -> INFO 001 txid [117c564ffca5a75299a84a613a4c33380c6ed1e6744f2ca85f20efa9823252fd] committed with status (VALID) at localhost:7051
//可以通过命令查看当前链码的审核状态,因为只有所有组织节点都审核通过之后,才能提交链码。但这个时候只有Org1MSP通过了审核,组织2还没有通过审核
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name atcc --version 1.0 --sequence 1 --output json
{
    
    
	"approvals": {
    
    
		"Org1MSP": true,
		"Org2MSP": false
	}
}
//也可以通过命令查看当前节点上安装的链码
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer lifecycle chaincode queryinstalled
//Installed chaincodes on peer:
//Package ID: basic_1.0:3cfcf67978d6b3f7c5e0375660c995b21db19c4330946079afc3925ad7306881, Label: basic_1.0
//Package ID: fabcar_1:1146b4b491871bf18b23dd67dd8cc058655b36cc0e2274f165ed06b796a8f276, Label: fabcar_1
//Package ID: atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48, Label: atcc_1


//在配置了组织2信息的终端上执行:
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# ls
atcc.go  atcc.tar.gz  go.mod  go.sum  vendor
//同之前一样,将链码包atcc.tar.gz安装到组织2节点上,安装成功后也会返回status 200且会输出链码包标识ID——“atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48”
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer lifecycle chaincode install atcc.tar.gz
//2022-06-21 19:41:36.634 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGatcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48\022\006atcc_1" > 
//2022-06-21 19:41:36.634 CST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48
//然后组织2节点也要审核链码,通过审核后也会输出交易ID——“txid [117c564ffca5a75299a84a613a4c33380c6ed1e6744f2ca85f20efa9823252fd]”
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name atcc --version 1.0 --package-id atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48 --sequence 1
//2022-06-21 19:41:53.240 CST [chaincodeCmd] ClientWait -> INFO 001 txid [50999fd3617bd19d9dc26d9826abdfac3a468628872550574cdd5887184250c8] committed with status (VALID) at localhost:9051
//可以通过命令查看当前链码的审核状态,这时候组织1和组织2都审核通过啦,可以提交链码了
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name atcc --version 1.0 --sequence 1 --output json
{
    
    
	"approvals": {
    
    
		"Org1MSP": true,
		"Org2MSP": true
	}
}
//通过组织2提交链码【通过组织1提交也是可以的,提交一次就行啦~都是同步的。】
//提交成功后会输出两条日志信息,说明提交的交易来自两个节点【真实情况下是多个,有多少个节点审核并通过了链码,这里就会有多少条日志信息,且都是对应于同一条交易ID】
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc#  peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name atcc --peerAddresses localhost:7051 --tlsRootCertFiles /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1.0 --sequence 1
//2022-06-21 19:49:24.598 CST [chaincodeCmd] ClientWait -> INFO 001 txid [b01da77332d3b3525f03a854b5ba4a442010626491a70637aeffd65b622738e0] committed with status (VALID) at localhost:9051
//2022-06-21 19:49:24.624 CST [chaincodeCmd] ClientWait -> INFO 002 txid [b01da77332d3b3525f03a854b5ba4a442010626491a70637aeffd65b622738e0] committed with status (VALID) at localhost:7051
//提交成功输出上面的日志之后,可以用docker ps -a 命令看到docker容器中又多了三个节点对应的atcc链码容器,且均处于Up状态。这时候可以进行链码调用啦~
//首先进行链码初始化,如果成功会返回status200,否则会提示错误原因
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n atcc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{
    
    "Args":["InitLedger"]}'
//2022-06-21 19:55:41.739 CST [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 
//初始化成功之后可以采用query调用链码中的ReadAsset方法查询某条asset记录。-c后面接的是链码中的方法及其所需要的参数,这些方法是自己定义的,如InitLedger、ReadAsset、GetAllAssets,自己编写这个链码中只有这三个方法~
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer chaincode query -C mychannel -n atcc -c '{
    
    "Args":["ReadAsset","asset1"]}'
//{"ID":"asset1","Owner":"ZhangSan","Value":300}
//也可以采用query调用链码的GetAllAssets查询所有asset记录
root@gan-virtual-machine:/opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc# peer chaincode query -C mychannel -n atcc -c '{
    
    "Args":["GetAllAssets"]}'
//[{"ID":"asset1","Owner":"ZhangSan","Value":300},{"ID":"asset2","Owner":"Lisi","Value":400},{"ID":"asset3","Owner":"Klay","Value":500}]
//补充第14步:Java调用链码扩展: https://www.cnblogs.com/aarond/p/12173797.html   感兴趣的话可以自己下去看~

El siguiente es un comando puro, no hay un indicador de directorio de terminal, es más conveniente copiar y pegar ~

//组织1执行
PWD='/opt/fabric-2.2.5/scripts/fabric-samples/test-network'
export PATH=${
    
    PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${
    
    PWD}/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051

peer lifecycle chaincode package atcc.tar.gz --path /opt/fabric-2.2.5/scripts/fabric-samples/chaincode/atcc --label atcc_1
peer lifecycle chaincode install atcc.tar.gz 
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name atcc --version 1.0 --package-id atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48 --sequence 1
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name atcc --version 1.0 --sequence 1 --output json



//组织2执行
PWD='/opt/fabric-2.2.5/scripts/fabric-samples/test-network'
export PATH=${
    
    PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${
    
    PWD}/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051

peer lifecycle chaincode install atcc.tar.gz 
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name atcc --version 1.0 --package-id atcc_1:a682cc7b1295ef65592650b9bcd0936ebc7812fb842eee436b8e1ca9dbc1da48 --sequence 1
 peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name atcc --peerAddresses localhost:7051 --tlsRootCertFiles /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1.0 --sequence 1
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n atcc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/fabric-2.2.5/scripts/fabric-samples/test-network/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{
    
    "Args":["InitLedger"]}'
peer chaincode query -C mychannel -n atcc -c '{
    
    "Args":["ReadAsset","asset1"]}'
peer chaincode query -C mychannel -n atcc -c '{
    
    "Args":["GetAllAssets"]}'




Agregue una pequeña pregunta ~ puede ignorarse

Prefacio:
inserte la descripción de la imagen aquí

Los dos archivos de código de cadena empaquetados son diferentes, esto no es importante ~ simplemente sáltelo

fabcar.tar.gz está empaquetado en el directorio chaincode, que se puede agregar, borrar, modificar y verificar
fabcar_.tar.gz está empaquetado en el directorio fabcar. Se obtuvo en el capítulo anterior y no se puede agregar, borrar, modificar y comprobado.

inserte la descripción de la imagen aquí
Uno de los profesores es 3. Más que M, el otro es 2. Más que M. Sospecho que puedo agregar, eliminar, modificar y verificar normalmente, y no hay diferencia, porque observé la estructura del directorio y el directorio 7.55M empaquetado en el directorio chaincode es equivalente a dos directorios fabcar. El contenido debe ser el mismo, hay demasiados, no puedo comparar el contenido de los archivos uno por uno~
inserte la descripción de la imagen aquí

resumen de aprendizaje

Aunque el contenido de este curso todavía es muy básico, todavía hay muchas cosas que necesitan ser aprendidas en profundidad. Sin embargo, a través de este curso, aprendí la sintaxis simple de shell and go, que es útil para leer el código más adelante. Además, el conocimiento relacionado con blockchain se revisa a través de casos prácticos, lo cual es útil para comprender blockchain y dónde puede ser necesario más adelante.
Muchas gracias, Profesor Datouwa~ ¡Muy buena charla! muy beneficioso
El camino es un largo camino por recorrer, y subiré y bajaré para buscarlo. peleando~

Supongo que te gusta

Origin blog.csdn.net/weixin_49422491/article/details/125309519
Recomendado
Clasificación