参考http://www.blockchainbrother.com/article/1339

configtxgen是Hyperledger Fabric提供的用于通道配置的实用程序,主要生成以下3种文件:

  • 排序服务节点使用的创世区块;
  • 创建通道使用的通道配置交易;
  • 更新通道用的锚节点交易。

目前,该工具主要侧重于生成排序服务节点的创世区块,但是将来预计增加生成新通道的配置以及重新配置已有的通道。

1.编译生成该configtxgen工具的方法与cryptogen相似,有两种办法:

该工具的源码在github.com/hyperledger/fabric/common/tools/configtxgen
1)在github.com/hyperledger/fabric目录下运行:

vagrant@ubuntu-xenial:/opt/gopath/src/github.com/hyperledger/fabric$ configtxgen --help
configtxgen: command not found
vagrant@ubuntu-xenial:/opt/gopath/src/github.com/hyperledger/fabric$ make configtxgen
.build/bin/configtxgen
CGO_CFLAGS=" " GOBIN=/opt/gopath/src/github.com/hyperledger/fabric/.build/bin go install -tags "" -ldflags "-X github.com/hyperledger/fabric/common/tools/configtxgen/metadata.CommitSHA=325999f" github.com/hyperledger/fabric/common/tools/configtxgen
Binary available as .build/bin/configtxgen

然后会生成./build/bin/configtxgen可执行文件

2)在github.com/hyperledger/fabric/common/tools/configtxgen目录下运行go build

2.编译成功后查看其参数信息:

vagrant@ubuntu-xenial:/opt/gopath/src/github.com/hyperledger/fabric$ configtxgen --help
Usage of configtxgen:
-asOrg string
作为特定的组织(按名称string)执行配置生成,只包括org(可能)有权设置的写集中的值。如用来指明生成的锚节点所在的组织
-channelCreateTxBaseProfile string
指定一个概要文件作为orderer系统通道当前状态,以允许在通道创建tx生成期间修改非应用程序参数。仅在与“outputCreateChannelTx”组合时有效。
-channelID string
在configtx中使用的通道ID,即通道名称,默认是"testchainid"
-configPath string
包含要使用的配置的路径(如果设置的话)
-inspectBlock string
按指定路径打印块中包含的配置,用于检查和输出通道中创世区块的内容,锚节点在configtx.yaml中的AnchorPeers中指定
-inspectChannelCreateTx string
按指定路径打印交易中包含的配置,用来检查通道的配置交易信息
-outputAnchorPeersUpdate string
创建一个配置更新来更新锚节点(仅在默认通道创建时工作,并且仅在第一次更新时工作)
-outputBlock string
将genesis块写入(如果设置)的路径。configtx.yaml文件中的Profiles要指定Consortiums,否则启动排序服务节点会失败
-outputCreateChannelTx string
将通道配置交易文件写入(如果设置)的路径。configtx.yaml文件中的Profiles必须包含Application,否则创建通道会失败
-printOrg string
将组织的定义打印为JSON。(对于手动向通道添加组织非常有用)
-profile string
指定使用的是configtx.yaml中某个用于生成的Profiles配置项。(默认为“SampleInsecureSolo”)
-version
显示版本信息

3.使用代码分析命令:

在github.com/hyperledger/fabric-samples/first-network/byfn.sh文件中使用该工具来生成orderer genesis块、通道配置交易和锚节点更新交易,代码如下:

function generateChannelArtifacts() {
which configtxgen
if [ "$?" -ne ]; then
echo "configtxgen tool not found. exiting"
exit
fi echo "##########################################################"
echo "######### Generating Orderer Genesis block ##############"
echo "##########################################################"
# Note: For some unknown reason (at least for now) the block file can't be
# named orderer.genesis.block or the orderer will fail to launch!
echo "CONSENSUS_TYPE="$CONSENSUS_TYPE #查看共识类型
set -x
if [ "$CONSENSUS_TYPE" == "solo" ]; then
configtxgen -profile TwoOrgsOrdererGenesis -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block
elif [ "$CONSENSUS_TYPE" == "kafka" ]; then
configtxgen -profile SampleDevModeKafka -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block
else
set +x
echo "unrecognized CONSESUS_TYPE='$CONSENSUS_TYPE'. exiting"
exit
fi
res=$?
set +x
if [ $res -ne ]; then
echo "Failed to generate orderer genesis block..."
exit
fi
echo
echo "#################################################################"
echo "### Generating channel configuration transaction 'channel.tx' ###"
echo "#################################################################"
set -x
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
res=$?
set +x
if [ $res -ne ]; then
echo "Failed to generate channel configuration transaction..."
exit
fi echo
echo "#################################################################"
echo "####### Generating anchor peer update for Org1MSP ##########"
echo "#################################################################"
set -x
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
res=$?
set +x
if [ $res -ne ]; then
echo "Failed to generate anchor peer update for Org1MSP..."
exit
fi echo
echo "#################################################################"
echo "####### Generating anchor peer update for Org2MSP ##########"
echo "#################################################################"
set -x
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate \
./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
res=$?
set +x
if [ $res -ne ]; then
echo "Failed to generate anchor peer update for Org2MSP..."
exit
fi
echo
}

1)首先是生成orderer创世区块

 if [ "$CONSENSUS_TYPE" == "solo" ]; then
configtxgen -profile TwoOrgsOrdererGenesis -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block
elif [ "$CONSENSUS_TYPE" == "kafka" ]; then
configtxgen -profile SampleDevModeKafka -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block
else

-profile TwoOrgsOrdererGenesis : 指定使用的是configtx.yaml中Profiles配置项中的TwoOrgsOrdererGenesis配置,即:

Profiles:

    TwoOrgsOrdererGenesis:
<<: *ChannelDefaults
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2

-profile SampleDevModeKafka :指定使用的是configtx.yaml中Profiles配置项中的SampleDevModeKafka配置,即:

    SampleDevModeKafka:
<<: *ChannelDefaults
Capabilities:
<<: *ChannelCapabilities
Orderer:
<<: *OrdererDefaults
OrdererType: kafka
Kafka:
Brokers:
- kafka.example.com: Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Application:
<<: *ApplicationDefaults
Organizations:
- <<: *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2

-channelID byfn-sys-channel :将通道名称命名为byfn-sys-channel

-outputBlock ./channel-artifacts/genesis.block :为生成的创世区块文件名及保存路径

2)生成通道配置交易

configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

-profile TwoOrgsChannel : 指定使用的是configtx.yaml中Profiles配置项中的TwoOrgsChannel配置,即:

    TwoOrgsChannel:
Consortium: SampleConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
- *Org2
Capabilities:
<<: *ApplicationCapabilities

-outputCreateChannelTx ./channel-artifacts/channel.tx :指明生成的通道配置交易存储的路径及文件名

-channelID $CHANNEL_NAME :通道名为自己设置的$CHANNEL_NAME值

3)生成组织的锚节点

configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
...
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP

-outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx : 为生成锚节点的更新交易文件及保存路径-asOrg Org1MSP : 指明该锚节点所在的组织

除了上面的函数能够实现的这三个功能外,configtxgen还能够:

4)查看创世区块信息

configtxgen -profile TwoOrgsOrdererGenesis -inspectBlock ./channel-artifacts/genesis.block

-inspectBlock ./channel-artifacts/genesis.block :指定要查看的创世区块文件

5)查看通道配置交易信息

configtxgen -profile TwoOrgsChannel -inspectChannelCreateTx ./channel-artifacts/channel.tx

-inspectChannelCreateTx ./channel-artifacts/channel.tx : 指定要查看的通道配置交易信息

4.configtx.yaml文件

此配置文件主要分为3部分。

1)Profiles部分。它是默认的,这部分包含一些用于开发或测试场景的示例配置,这些配置涉及fabric目录中的加密部分。configtxgen工具允许通过-profile标签来指定配置文件。Profiles部分可以显式声明所有配置,但是通常都是从默认配置中继承。

2)Organizations部分。它是默认的,这部分包含示例配置MSP定义的单一引用。对于生产部署,应该删除这部分配置,并以新网络成员的MSP定义来替代它。组织中每一个元素都必须带有锚标签,如&orgName,这些标签可以在Profiles中部分引用。

3)默认部分。此部分是Orderer和Application的配置,包括一些属性配置,如BatchTimeout和一般用作继承的基础值。

github.com/hyperledger/fabric-samples/first-network/configtx.yaml:

如果没学过yaml,可见yaml的简单学习

# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
# ---
################################################################################
#
# Section: Organizations
#
# - 本节定义了不同的组织标识,这些标识将在稍后的配置中引用。
#
################################################################################
Organizations: # SampleOrg使用sampleconfig定义了一个MSP。它不应该在生产中使用,但可以用作其他定义的模板
- &OrdererOrg
# DefaultOrg定义了使用在fabric.git开发环境的sampleconfig中的组织
Name: OrdererOrg # ID 下载MSP的ID
ID: OrdererMSP # MSPDir是包含MSP配置的文件系统路径,由cryptogen工具生成的加密材料路径
MSPDir: crypto-config/ordererOrganizations/example.com/msp # Policies定义了在这个配置树级别的策略集
# 对于组织策略,它们的规范路径通常是/Channel/<Application|Orderer>/<OrgName>/<PolicyName>
Policies:
Readers:
Type: Signature
Rule: "OR('OrdererMSP.member')"
Writers:
Type: Signature
Rule: "OR('OrdererMSP.member')"
Admins:
Type: Signature
Rule: "OR('OrdererMSP.admin')" - &Org1
# DefaultOrg定义了使用在fabric.git开发环境的sampleconfig中的组织
Name: Org1MSP # ID 下载MSP的ID
ID: Org1MSP MSPDir: crypto-config/peerOrganizations/org1.example.com/msp # Policies定义了在这个配置树级别的策略集
# 对于组织策略,它们的规范路径通常是/Channel/<Application|Orderer>/<OrgName>/<PolicyName>
Policies:
Readers:
Type: Signature
Rule: "OR('Org1MSP.admin', 'Org1MSP.peer', 'Org1MSP.client')"
Writers:
Type: Signature
Rule: "OR('Org1MSP.admin', 'Org1MSP.client')"
Admins:
Type: Signature
Rule: "OR('Org1MSP.admin')" # 将此标志设置为true.
AnchorPeers: #指明org1中使用peer0作为锚节点
# AnchorPeers定义了可以用于跨组织gossip通信的节点的位置
# 注意,这个值只在应用程序部分上下文的genesis块中编码
- Host: peer0.org1.example.com
Port: - &Org2
# DefaultOrg定义了使用在fabric.git开发环境的sampleconfig中的组织
Name: Org2MSP # ID 来指定下载的MSP定义
ID: Org2MSP MSPDir: crypto-config/peerOrganizations/org2.example.com/msp # Policies定义了在这个配置树级别的策略集
# 对于组织策略,它们的规范路径通常是/Channel/<Application|Orderer>/<OrgName>/<PolicyName>
Policies:
Readers:
Type: Signature
Rule: "OR('Org2MSP.admin', 'Org2MSP.peer', 'Org2MSP.client')"
Writers:
Type: Signature
Rule: "OR('Org2MSP.admin', 'Org2MSP.client')"
Admins:
Type: Signature
Rule: "OR('Org2MSP.admin')" AnchorPeers: #指明org2中使用peer0作为锚节点
# AnchorPeers定义了可以用于跨组织gossip通信的节点的位置
# 注意,这个值只在应用程序部分上下文的genesis块中编码
- Host: peer0.org2.example.com
Port: ################################################################################
#
# SECTION: Capabilities
#
# - 本节定义fabric network的功能。这是v1..0的一个新概念,不应该在带有v1..x版本orderers和peers的混合网络中使用。
# 功能定义了fabric二进制文件中必须提供的特性,以便该二进制文件安全地参与fabric网络。
# 例如,如果添加了新的MSP类型,较新的二进制文件可能会识别并验证来自该类型的签名,而没有此支持的较老的二进制文件将无法验证这些交易。
# 这可能导致不同版本的fabric二进制文件具有不同的世界状态。相反,为通道定义一个功能会通知那些没有这个功能的二进制文件,它们必须停止处理交易,直到它们被升级。
# 对于v1..x 版本,如果定义了任何功能(包括关闭所有功能的映射),v1..x版本的节点可能会故意崩溃。
#
################################################################################
Capabilities:
# 通道功能同时适用于orderers和peers,并且必须得到双方的支持。
# 将功能的值设置为true以满足需要。
Channel: &ChannelCapabilities
#Channel的V1.3是一个用来表示运行在V1..x版本上的所有orderers和peers都需要的行为的catchall标志,但这将与以前版本中的orderers和peers不兼容。
# 在启用V1.3通道功能之前,请确保通道上的所有orderers和peers都位于v1..0或更高版本。
V1_3: true # Orderer功能只适用于Orderer,并且可以安全地与以前版本的节点一起使用。
# 将功能的值设置为true以满足需要
Orderer: &OrdererCapabilities
# Orderer的V1.1是一个用来表示确定被在v1..x中运行的所有orderers所需要的行为的catchall标志,但这与以前版本中的orderers不兼容。
# 在启用V1. orderer功能之前,请确保通道上的所有orderer都处于v1..0或更高版本。
V1_1: true # Application功能只应用于peer网络,并且可以安全地与以前的版本的orderers一起使用。
# 将功能的值设置为true以满足需要
Application: &ApplicationCapabilities
# Application的V1.3用来启用新的非向后兼容特性fabric V1.3的补丁
V1_3: true
# Application的V1.2用来启用新的非向后兼容特性和fabric V1.2的补丁(注意,如果设置了之后的版本的功能,则不需要设置此功能)
V1_2: false
# Application的V1.1启用新的非向后兼容特性和fabric V1.1的补丁(注意,如果设置了之后的版本的功能,则不需要设置此特性)。
# 所以这里设置是V1_3为true,另外两个为false
V1_1: false ################################################################################
#
# SECTION: Application
#
# - 本节为Application相关参数定义要编码到配置交易或创世区块中的值
# 注意,只有在创建channel tx时才会用Application,否则是创建区块
#
################################################################################
Application: &ApplicationDefaults # Organizations是定义为网络应用程序端参与者的组织列表
Organizations: # Policies定义了在这个配置树级别的策略集
# 对于组织策略,它们的规范路径通常是/Channel/<Application|Orderer>/<OrgName>/<PolicyName>
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins" Capabilities:
<<: *ApplicationCapabilities
################################################################################
#
# SECTION: Orderer
#
# - 本节为orderer相关参数定义要编码到配置交易或创世区块中的值
#
################################################################################
Orderer: &OrdererDefaults # Orderer 类型: 要启动的Orderer实现类型
# 可用的类型有“solo”和“kafka”
OrdererType: solo Addresses:
- orderer.example.com: # Batch Timeout: 创建批处理之前要等待的时间
BatchTimeout: 2s # Batch Size: 控制成块的消息数量
BatchSize: # Max Message Count: 批处理中允许的最大消息数
MaxMessageCount: # Absolute Max Bytes: 批处理中允许序列化消息的绝对最大字节数。
AbsoluteMaxBytes: MB # Preferred Max Bytes: 批处理中允许序列化消息的首选最大字节数。
# 大于首选最大字节的消息将导致批处理大于改值。
PreferredMaxBytes: KB Kafka:
# Brokers: orderer连接到的Kafka代理的列表
# NOTE: Use IP:port notation
Brokers:
- 127.0.0.1: # Organizations是组织的列表,组织被定义为网络的orderer方的参与者
Organizations: # Policies定义了在这个配置树级别的策略集
# 对于Orderer策略,它们的规范路径通常是/Channel/Orderer/<PolicyName>
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
# BlockValidation指定必须包含来自orderer的哪些签名,以便peer对其进行验证。
BlockValidation:
Type: ImplicitMeta
Rule: "ANY Writers" ################################################################################
#
# CHANNEL
#
# 本节为channel相关参数定义要编码到配置交易或创世区块中的值
#
################################################################################
Channel: &ChannelDefaults # Policies定义了在这个配置树级别的策略集
# 对于Channel策略,它们的规范路径通常是/Channel/<PolicyName>
Policies:
# 谁可能调用'Deliver' API
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
# 谁可能调用'Broadcast' API
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
# 默认,谁可能调用在这个配置级别的元素
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins" # Capabilities描述通道级功能,有关详细描述,请参阅此文件中其他部分的专用功能部分
Capabilities:
<<: *ChannelCapabilities ################################################################################
#
# Profile
#
# - 这里可以编码不同的配置概要文件,将其指定为configtxgen工具的参数
#
################################################################################
Profiles: TwoOrgsOrdererGenesis:
<<: *ChannelDefaults
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2
TwoOrgsChannel:
Consortium: SampleConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
- *Org2
Capabilities:
<<: *ApplicationCapabilities SampleDevModeKafka:
<<: *ChannelDefaults
Capabilities:
<<: *ChannelCapabilities
Orderer:
<<: *OrdererDefaults
OrdererType: kafka
Kafka:
Brokers:
- kafka.example.com: Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Application:
<<: *ApplicationDefaults
Organizations:
- <<: *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2

hyperledge工具-configtxgen的更多相关文章

  1. hyperledge工具-configtxlator

    参考:http://www.blockchainbrother.com/article/1337 1.作用: 因为无论配置交易文件 .tx和初始区块文件 .block都是二进制格式,用户无法直接编辑. ...

  2. hyperledge工具-cryptogen

    参考:http://baijiahao.baidu.com/s?id=1596614770784685300&wfr=spider&for=pc cryptogen是Hyperledg ...

  3. hyperledger中文文档学习-4-构建第一个fabric网络

    接下来的操作都将在hyperledge环境安装构建的虚拟机的环境下进行 参考https://hyperledgercn.github.io/hyperledgerDocs/build_network_ ...

  4. Bring up a Kafka-based Ordering Service

    Bring up a Kafka-based Ordering Service 这篇文章假设读者对怎样设置Kafka集群和ZooKeeper集合已经初步了解.这篇文章的目的是讲解部署一个基于Kafka ...

  5. HyperLedger Fabric部署与链码解读

    1.Fabric简介 Fabric是超级账本中的一个项目,用以推进区块链技术.和其他区块链类似,它也有一个账本,使用智能合约,且是一个参与者可以分别管理自身交易的系统.它是一个联盟链.Fabric与其 ...

  6. 区块链学习——HyperLedger-Fabric v1.0 启动过程分析

    本章我们从fabric v1.0的e2e_cli示例开始分析整个启动过程以及在过程中的一些配置文件 首先呢,还是确保你的基本环境已经搭建完成,v1.0源码和镜像也都下载完毕 fabric启动过程中的相 ...

  7. 搭建Fabric网络(三)artifacts是怎么生成的:cryptogen和configtxgen

    在first-network里,./byfn.sh generate可以生成artifacts文件. generate参数其实是使用了cryptogen和configtxgen这两个工具,这两个工具分 ...

  8. 超级账本Hyperledge的关键部件说明

    帐本(Ledger) Fabric帐本(Ledger)是一系列有序和防篡改的状态转换的记录,结构由一个区块链构成,并将不可变的.有序的记录存放在区块中:同时包含一个状态数据库来记录当前的状态,账本的当 ...

  9. Unity3d入门 - 关于unity工具的熟悉

    上周由于工作内容较多,花在unity上学习的时间不多,但总归还是学习了一些东西,内容如下: .1 根据相关的教程在mac上安装了unity. .2 学习了unity的主要的工具分布和对应工具的相关的功 ...

随机推荐

  1. 带你使用JS-SDK自定义微信分享效果

    前言 想必各位在写wap端时都遇到过这样的场景吧 ----自定义分享标题.图片.描述 接下来小编给大家讲解下分享相关操作 预期效果 原始的分享效果: 使用微信JS-SDK的分享效果: 可以看出缩略图, ...

  2. js控制随机数生成概率代码实例

    基本思路:把Math.random()js随机数生成的数看着百分比,然后定义每个整数值取值范围. 具体内容如下,供大家参考 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...

  3. angular 用拦截器统一处理http请求和响应 比如加token

    想使用angularjs里的htpp向后台发送请求,现在有个用户唯一识别的token想要放到headers里面去,也就是{headres:{'token':1}} index.html里引入以下js: ...

  4. react学习笔记2

    1.build文件介绍 (1)react.js  是react的核心库 (2)react-dom.js  提供与DOM相关功能 (3)browser.js  是将JSX语法转为javascript语法 ...

  5. window.print()小知识

    window.print()  实际上,是浏览器打印功能菜单的一种程序调用.与点击打印功能菜单一样,不能精确分页,不能设置纸型,套打的问题更加无从谈起,只不过,可以让用户不用去点菜单,直接点击网页中的 ...

  6. DOCTYPE声明作用?标准模式与兼容模式?

    <!DOCTYPE>文档声明是用来告诉浏览器使用哪种DTD,一般放在(X)HTML文档开头声明,用以告诉其他人这个文档的类型风格:DTD(文档类型定义)是一组机器可读的规则,它们指示(X) ...

  7. CSS中默认被继承的属性

    在CSS中,所有属性都可以被继承,只需要显式的设置属性值为inherit即可.如果不设置该属性,CSS大部分属性默认不会从父元素继承而是设置初始值(initial value),但是有一部分属性,默认 ...

  8. 【机器学习】Google机器学习工程的43条最佳实践

    https://blog.csdn.net/ChenVast/article/details/81449509 本文档旨在帮助那些掌握机器学习基础知识的人从Google机器学习的最佳实践中获益.它提供 ...

  9. Python 关于类函数设计的一点总结

    关于类函数设计的一点总结 by:授客 QQ:1033553122 代码1 #!/usr/bin/env python #-*-encoding:utf-8-*- __author__ = 'shouk ...

  10. 2014/08/31 Zushi

    今天是逗子森户海滨浴场开放的最后一天,趁着最后的光景来这里透透气. 在学皮划艇准备下海的人们,貌似还挺有趣. 来自云端的上帝之手. 谁愿意和我一起向着夕阳弄桨. 夕阳西下,那里是家乡的方向. 灯塔和神 ...