如何开始编码

首发于我的博客网站(prajna.top) 欢迎大家前去交流,有pdf版本。 本文主要是从应用的角度出发,分别阐述操作系统接口,计算机语言,文件系统等背后的一些知识,规范,原理,设计思想,应用法门,让初学者对编码有一个整体的,全局的认识,有一个物理的视角,找到自己的起点。 前言写这篇文章主要是基于自己大学的经历,当时抱着一腔热血去学计算机编程,可是当把c/c++语言,数据结构,操作系统,计算机组成原理等课程都学完后,却发现自己似乎什么也不会,只会printf打印一些字符串。那段时间真的好苦恼,特别想做软件,却不知从何开始,也不知道该如何去使力,蹉跎了好久,浪费了大量的时间。 造成这种现象的主要原因,一是自己缺少那种天赋,二是教学过于侧重基础和理论,每门课程只涉及到一个局部,没有一门课程把这些串起来。我不了解语言的基础库,除了printf后,其它API都不会用;也不了解具体的操作系统平台的API;虽然学了TCP/IP,socket的具体使用却又不清楚; 至于像fat,ext等磁盘文件系统的格式,那就更遥远了;我甚至还不清楚计算机语言和编译工具的关系;更要命的是,我还不知道自己不知道这些。说白了就是理论同应用脱节,虽然大学也安排了课程设计,实验和实习,但都只是走了过场,也没有人来指点一下,该看些什么书籍。大学读完,就知道拖拉几个控件做一个窗口,连接一下数据库。 windows + VC屏蔽了太多的技术细节,可惜大学期间接触的偏偏就是它,对用户这个是好事越傻瓜越好,可是对计算机的学生就要了命了。自从我转投到linux开源世界后,终于才发现了什么是自由,什么是编程。当阅读linux源码碰到不理解的地方,可以直接修改源码加上打印,分析kernel流程。对比着minix的源码来学习操作系统结构原理,那些概念就变成实实在在的数据结构和算法,动手写一个minix的驱动,微内核和宏内核区别就一目了然了。强烈建议,想学习编程的同学都去拥抱开源世界,然后,再回到自己感兴趣的或工作相关的领域。 计算机语言说白了就是工具,关键还是你要做什么,这样就涉及到了应用,以及专业背景知识。如想做驱动编程,离不开对操作系统驱动架构的了解;想做一个磁盘分区合并,那需要了解文件系统的格式;做个播放器吧,那对视频文件格式,编码格式,编解码API的了解必不可少。 随着你对软件系统了解的深入,会发现其实一切都是协议。 http 是一套web 通讯的协议;计算机语言是开发工具提供的协议; 操作系统是内核空间与应用空间的协议..., 这些协议被各种规范约束--并形成了各种技术。 所以,每种技术的背后都一套协议,规则和思想。了解这些才能算真正了解了相关技术。 在这篇文章里面,我以GNU/Linux作为平台,从应用的角度出发来把相关的课程来串一串提供一个“物理视图”,让初学者有个全局的认识,能够有一个方向和切入角度,至少知道该找些什么资料来看。 操作系统接口它是内核对应用空间提供的一套协议,主要包括: ABI可执行文件的结构。系统调用(system call)。sysfs 文件系统接口(linux kernel)。ELF是编译, 链接生成的,执行的时候,由ld 解析,加载在到内存,最后控制权交给程序入口代码,程序开始执行。因此,它提供了2类视图:链接视图和执行视图。 从链接视图上看,ELF由众多的 Section组成,编译器先把源码编译成.o文件,主要是提取函数,全局变量等生成符号表,把它们填充到相应的 Section里面去。 在这个阶段,所有的符号都是没法定位到地址的。 Link的时候,对.o文件进行合并,对各个文件内的符号进行重定位,安排它们的地址,如下图所示, link完成后,g_u8 和 g_flag2都有地址了。 对于动态链接的函数,在link阶段没法安排地址,需要放到 dynsym Section里面去,在 ld的时候,来进行定位 -- 这就是所谓的 "函数重定位"。linux系统提供了可执行程序readelf来解析 ELF文件格式,我们可以使用它来了解一下ELF文件的一些通用的Section。 bss 是没有指定初始化值的数据, 有些编译器会默认全部初始化为0。data 全局变量初始化。text 代码。init 程序初始化运行的代码。fini 程序结束运行的代码。symtab 符号表, 它是源码编译生成的产物,可以为代码运行,调试提供信息。 属于辅助性质,不参与 load 和运行, 可以用 strip来删除掉。'offset Align' 是各个Section在ELF文件内的偏移地址,我们以二进制的方式打开ELF文件,根据偏移地址,就可以查看相应Section的二进制内容。 从下图中可以看到 .interp的内容是 "/lib64/ld-linux-x86-64.so.2", 上面这些就是编译,链接生成ELF文件的过程:编译器以源文件作为输入,先提取各个文件的全局变量和函数,生成符号表,再把它们链接到一起,链接的时候对各个符号进行定位,分配地址。对于动态链接库的函数,则推迟到'加载'程序到内存的时候进行定位。编译链接后,代码和数据分散到了相应的section里面,程序加载的时候,需要把Section 合并成Secgment,然后,以Secgement为单位加载到内存页面里面去,我们来看一下Segment的结构。 ELF有9种Segment,其中比较核心的是 -- ...

November 5, 2019 · 2 min · jiezi

Docker入门基础之应用实战

Docker入门基础之应用实战当我们掌握了Docker镜像和容器的基本用法后,我们现在能做些什么事情呢?现在我们就来看看使用Docker容器如何安装常见的软件,然后运行一个动态网站。 下面我们来学习: 1、安装Nginx2、安装PHP3、使用MySQL服务4、运行wordpress博客安装Nginx运行一个Alpine的容器,选择Alpine作为系统基础镜像是因为Alpine轻巧的体积,基础镜像只有5.53MB,相比ubuntu镜像的88.9MB要小十几倍。 root@ubuntu:~# docker run -it -p 8080:80 alpine sh安装nginx apk add nginx修改nginx配置 vi /etc/nginx/conf.d/default.confdefault.conf内容如下: server { listen 80 default_server; root /home/www; index index.php index.html; }创建Hello World mkdir /home/www && echo "Hello World" > /home/www/index.html创建/run/nginx目录 mkdir /run/nginx启动nginx nginx在浏览器中访问 http://192.168.43.122:8080 nginx安装成功,WEB服务访问正常! 安装PHP现在我们来安装PHP,方法还是一样,使用 apk add 命令来安装php7,php-fpm以及相关扩展。 apk add --no-cache php7 php7-fpm php7-ftp php7-pdo php7-mysqli php7-simplexml php7-xmlwriter php7-zlib php7-imagick php7-memcached php7-sockets php7-mcrypt php7-zip php7-pgsql php7-pdo_odbc php7-odbc php7-curl php7-iconv php7-xml php7-json php7-gd php7-session php7-opcache php7-pdo_sqlite php7-mbstring php7-common php7-pdo_mysql以上顺带安装了很多php扩展,可根据实际需求增减。 ...

July 16, 2019 · 1 min · jiezi

Docker入门基础之容器使用

Docker入门基础之容器使用Docker简介Docker 是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。 容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。 Ubuntu Docker 安装1、Docker官方安装方法Docker 要求 Ubuntu 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的 Ubuntu 版本是否支持 Docker。获取安装包: root@ubuntu:~# wget -qO- https://get.docker.com/ | sh安装完成后有提示: If you would like to use Docker as a non-root user, you should now consider adding your user to the "docker" group with something like: sudo usermod -aG docker runoob Remember that you will have to log out and back in for this to take effect! 启动docker服务 ...

July 16, 2019 · 3 min · jiezi

Golang入门-用一句话说明Go语言的命令

Go命令是管理Go资源的工具安装好Go环境后会内置很多有用的命令工具。 初步的了解一下这些命令的用途,对写代码很有帮助 有一些命令是非常常用的,比如 run、build、get、test、get,有一些命令在使用IDE后很少会用到,IDE代劳了,比如fmt、vet。 下面是常用命令的清单和简单的一句话说明,看看有没有你还没用过的命令吧! 常规用法:`go <命令> [参数]`命令:bug :创建一个bug报告执行完命令后,会用浏览器访问github.com/golang/go 的issue。自动填写一些内容,引导你如何提交一个bug报告 build:编译包以及其依赖最常用的命令之一。默认情况下,会在命令所在目录生成一个当前操作系统对应的可执行文件。安装完整版的Go环境,可以交叉编译其他操作系统的二进制可执行文件 clean:清空对象文件和缓存文件前面提到的build命令和下面的test命令会生成一些文件和目录,clean会清理掉这些文件,包括build命令生成可执行文件 doc:打印包中的文档和标记符打印出包或指定文件的说明文档,加上-all 参数,可以看到包里的所有函数列表和文档。创建一个go文件,写入一下代码 /*这是一个范例*/package mainimport "fmt"//main 主函数func main() { SayHi()}//SayHi 打印字符串Hello worldfunc SayHi() { fmt.Println("Hello world!!")}执行命令 go doc -all -u env :打印出你现在的Go环境信息查看各个go的开发环境参数,忘记GOPATH和GOROOT路径就可以用这个打印出来了 fix:用go的新版本的API更新 go fix [packages]如果你升级了go,担心以前的代码不兼容,那么就可以用 go fix fmt:格式化代码文件go的代码格式标准是唯一的,用go fmt可以格式化代码文件,很多IDE就是调用这个命令来在保存文件时调整格式。 generate:根据指令生成go文件查找当前包相关的源代码文件,找出所有包含”//go:generate”的注释,提取并执行该特殊注释后面的命令,类似shell执行命令。 get :下载和安装go包以及其依赖包的命令go get <包的路径> install:编译和安装包及其依赖包可执行文件会被安装在$GOPATH/bin目录下。 list :列出目录下的所有包和模块,每行一个。 mod :详细内容可以参考文章: 拜拜了,GOPATH君!新版本Golang的包管理入门教程 run : 运行go项目非常常用。它会编译包,然后直接运行起来,不会在当前目录生成二进制文件。 test:运行调试用于运行_text.go文件中的Test开头并且参数为 *testing.T的函数 tool :运行指定的go工具 version:查看当前go版本 vet:查看包中可能出现的错误例如,给整型%d占位符提供一个字符串参数,就会检查出类型错误,但是这个代码编译是不会报错的。 总结这些命令大部分使用起来都很简单,想了解更多可以运行go help [命令名]查看详细说明。 也有一些命令使用起来是需要花点时间学习的,比如 generate、test、mod,如果有想要了解更多关于Go语言开发的同学,可以在评论区或私信告诉我们,一起学习一起讨论。 ...

April 29, 2019 · 1 min · jiezi

(第一讲)Spring Initializr-快速入门Spring Boot的最好选择

Spring Initializr [http://start.spring.io/]是引导你快速构建Spring Boot项目的不二选择。 它允许你通过简单的操作步骤,就可以构建出一个完整的Spring Boot应用程序。你可以通过Spring Initializr引导界面构建如下类型的Spring Boot应用: Web应用程序Restful 应用程序Batch应用程序Spring Boot对很多第三方框架提供了良好的支持,可以通过对应的artifactId获得他们,这里列举其中的一部分供参考: spring-boot-starter-web-services:用于构建可与外界交互的SOAP Web服务应用程序spring-boot-starter-web:可用于构建Web应用程序或者基于Restful风格的应用程序spring-boot-starter-test:可用于构建并编写单元测试和集成测试spring-boot-starter-jdbc:构建基于JDBC的应用程序spring-boot-starter-hateoas:通过引入HATEOAS功能,让你轻松实现RESTful服务spring-boot-starter-security:使用Spring Security对系统用户进行身份验证和鉴权spring-boot-starter-data-jpa:基于Hibernate实现的Spring Data JPAspring-boot-starter-cache:开启基于Spring Framework的缓存支持spring-boot-starter-data-rest:使用Spring Data REST提供REST服务在本讲中,我将通过使用Spring Initializr来演示如何快速创建一个简单的Web应用程序。 使用Spring Initializr构建Web应用程序使用Spring Initializr构建Web应用程序是一件非常简单快速的事情。 如上图所示,我们需要执行如下的几个操作: 通过浏览器访问Spring Initializr官网 ,然后再执行下面的几个选择项 设置groupId : com.ramostear.spring.boot设置artifactId: spring-boot-quick-start项目名称:默认为spring-boot-quick-start基础包名:默认即可(你也可以选择修改)(通过点击More options展开)在搜索框中分别检索并选择如下几个组件:Web,Actuator,DevTools最后,点击“Generate Project”生成并下载项目将项目导入到IntelleJ IDEA中Spring Boot项目目录结构下图显示了在IDEA中导入刚才下载的项目目录结构: SpringBootQuickStartApplication.java:Spring Boot运行的主文件,它负责初始化Spring Boot自动配置和Spring应用程序上下文application.properties : 应用程序配置文件SpringBootQuickStartApplicationTests : 用于单元测试的简单启动器pom.xml : Maven构建项目的配置文件,包括了Spring Boot Starter Web等相关依赖项。特别指出,它会自动将Spring Boot Starter Parent作为整个工程的父依赖。核心的代码src/main/java 包下放置我们主要的逻辑代码,src/test/java包下放置项目的测试代码,src/main/resources包下放置项目的配置文件以及一些静态资源文件,如页html文件,css文件和js文件等。我们从上到下依次进行介绍。 SpringBootQuickStartApplication.javapackage com.ramostear.spring.boot.springbootquickstart;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class SpringBootQuickStartApplication { public static void main(String[] args) { SpringApplication.run(SpringBootQuickStartApplication.class, args); }}@SpringBootApplication : 负责初始化Spring Boot 自动化配置项和Spring应用程序上下文SpringApplication.run() : 负责启动Spring Boot应用程序的静态方法application.propertiesSpring Boot应用程序的配置文件,这里我们简单的设置一下项目启动的端口为8080(默认端口8080)和应用名称为Spring Boot Quick Start: ...

April 21, 2019 · 2 min · jiezi

Go mod 使用

go modules 是 golang 1.11 新加的特性。现在1.12 已经发布了,是时候用起来了。Modules官方定义为:模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。如何使用 Modules ?把 golang 升级到 1.11(现在1.12 已经发布了,建议使用1.12)设置 GO111MODULEGO111MODULEGO111MODULE 有三个值:off, on和auto(默认值)。GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形:当前目录在GOPATH/src之外且该目录包含go.mod文件当前文件在包含go.mod文件的目录下面。当modules 功能启用时,依赖包的存放位置变更为$GOPATH/pkg,允许同一个package多个版本并存,且多个项目可以共享缓存的 module。go modgolang 提供了 go mod命令来管理包。go mod 有以下命令:命令说明downloaddownload modules to local cache(下载依赖包)editedit go.mod from tools or scripts(编辑go.modgraphprint module requirement graph (打印模块依赖图)initinitialize new module in current directory(在当前目录初始化mod)tidyadd missing and remove unused modules(拉取缺少的模块,移除不用的模块)vendormake vendored copy of dependencies(将依赖复制到vendor下)verifyverify dependencies have expected content (验证依赖是否正确)whyexplain why packages or modules are needed(解释为什么需要依赖)如何在项目中使用示例一:创建一个新项目在GOPATH 目录之外新建一个目录,并使用go mod init 初始化生成go.mod 文件➜ ~ mkdir hello➜ ~ cd hello➜ hello go mod init hellogo: creating new go.mod: module hello➜ hello lsgo.mod➜ hello cat go.modmodule hellogo 1.12go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。go.mod 提供了module, require、replace和exclude 四个命令module 语句指定包的名字(路径)require 语句指定的依赖项模块replace 语句可以替换依赖项模块exclude 语句可以忽略依赖项模块添加依赖新建一个 server.go 文件,写入以下代码:package mainimport ( “net/http” “github.com/labstack/echo”)func main() { e := echo.New() e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, “Hello, World!”) }) e.Logger.Fatal(e.Start(":1323"))}执行 go run server.go 运行代码会发现 go mod 会自动查找依赖自动下载:$ go run server.gogo: finding github.com/labstack/echo v3.3.10+incompatiblego: downloading github.com/labstack/echo v3.3.10+incompatiblego: extracting github.com/labstack/echo v3.3.10+incompatiblego: finding github.com/labstack/gommon/color latestgo: finding github.com/labstack/gommon/log latestgo: finding github.com/labstack/gommon v0.2.8# 此处省略很多行… ____ __ / // / ___ / // / _ / _ //_////___/ v3.3.10-devHigh performance, minimalist Go web frameworkhttps://echo.labstack.com____________________________________O/_______ O\⇨ http server started on [::]:1323现在查看go.mod 内容:$ cat go.modmodule hellogo 1.12require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.2.8 // indirect github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-isatty v0.0.7 // indirect github.com/valyala/fasttemplate v1.0.0 // indirect golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect)go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit,详见 Modules官方介绍。 go 会自动生成一个 go.sum 文件来记录 dependency tree:$ cat go.sumgithub.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=… 省略很多行再次执行脚本 go run server.go 发现跳过了检查并安装依赖的步骤。可以使用命令 go list -m -u all 来检查可以升级的package,使用go get -u need-upgrade-package 升级后会将新的依赖版本更新到go.mod也可以使用 go get -u 升级所有依赖go get 升级运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)运行 go get -u=patch 将会升级到最新的修订版本运行 go get package@version 将会升级到指定的版本号version运行go get如果有版本的更改,那么go.mod文件也会更改示例二:改造现有项目(helloword)项目目录为:$ tree.├── api│ └── apis.go└── server.go1 directory, 2 filesserver.go 源码为:package mainimport ( api “./api” // 这里使用的是相对路径 “github.com/labstack/echo”)func main() { e := echo.New() e.GET("/", api.HelloWorld) e.Logger.Fatal(e.Start(":1323"))}api/apis.go 源码为:package apiimport ( “net/http” “github.com/labstack/echo”)func HelloWorld(c echo.Context) error { return c.JSON(http.StatusOK, “hello world”)}使用 go mod init *** 初始化go.mod$ go mod init helloworldgo: creating new go.mod: module helloworld运行 go run server.gogo: finding github.com/labstack/gommon/color latestgo: finding github.com/labstack/gommon/log latestgo: finding golang.org/x/crypto/acme/autocert latestgo: finding golang.org/x/crypto/acme latestgo: finding golang.org/x/crypto latestbuild command-line-arguments: cannot find module for path _/home/gs/helloworld/api首先还是会查找并下载安装依赖,然后运行脚本 server.go,这里会抛出一个错误:build command-line-arguments: cannot find module for path _/home/gs/helloworld/api但是go.mod 已经更新:$ cat go.modmodule helloworldgo 1.12require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.2.8 // indirect github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-isatty v0.0.7 // indirect github.com/valyala/fasttemplate v1.0.0 // indirect golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect)那为什么会抛出这个错误呢?这是因为 server.go 中使用 internal package 的方法跟以前已经不同了,由于 go.mod会扫描同工作目录下所有 package 并且变更引入方法,必须将 helloworld当成路径的前缀,也就是需要写成 import helloworld/api,以往 GOPATH/dep 模式允许的 import ./api 已经失效,详情可以查看这个 issue。更新旧的package import 方式所以server.go 需要改写成:package mainimport ( api “helloworld/api” // 这是更新后的引入方法 “github.com/labstack/echo”)func main() { e := echo.New() e.GET("/", api.HelloWorld) e.Logger.Fatal(e.Start(":1323"))}一个小坑:开始在golang1.11 下使用go mod 遇到过 go build github.com/valyala/fasttemplate: module requires go 1.12 这种错误,遇到类似这种需要升级到1.12 的问题,直接升级golang1.12 就好了。幸亏是在1.12 发布后才尝试的go mod ????♂️到这里就和新创建一个项目没什么区别了使用replace替换无法直接获取的package由于某些已知的原因,并不是所有的package都能成功下载,比如:golang.org下的包。modules 可以通过在 go.mod 文件中使用 replace 指令替换成github上对应的库,比如:replace ( golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a)或者replace golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a参考链接Modules官方介绍Golang 1.11 新功能介紹 – ModulesWhat are Go modules and how do I use them?go mod doesn’t work for github.com/gomarkdown/markdown/html 再探go modules:使用与细节初窥Go moduleReferences[1] Modules官方介绍: https://github.com/golang/go/...[2] issue: https://github.com/golang/go/...[3] 这种错误: https://github.com/golang/go/...[4] Modules官方介绍: https://github.com/golang/go/...[5] Golang 1.11 新功能介紹 – Modules: https://www.lightblue.asia/go...[6] What are Go modules and how do I use them?: https://talks.godoc.org/githu...[7] go mod doesn’t work for github.com/gomarkdown/markdown/html : https://github.com/golang/go/...[8] 再探go modules:使用与细节: https://www.cnblogs.com/apoce...[9] 初窥Go module: https://tonybai.com/2018/07/1…最后,感谢女朋友支持和包容,比❤️也可以在公号输入以下关键字获取历史文章:公号&小程序 | 设计模式 | 并发&协程 ...

March 17, 2019 · 3 min · jiezi

JavaScript闯关笔记

介绍通过Array/Object/Function基础类型编写。看到自己不了解的或者比较新颖的用法便会写上。不定时更新内容。本文首发于我的个人网站: Timbok.top目录Array迭代方法split和joinObjectobject映射Functionpromiseasync-awaitArray迭代方法every()方法对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 trueconst arr = [1,2,3,4];const result = arr.every((item, index, arr)=>{ return item > 2;});console.log(result); // falsesome()方法 对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 trueconst arr = [1, 2, 3, 4];const result = arr.some((item, index, arr)=>{ return item > 2;});console.log(result); // truefilter()方法对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组const arr = [1, 2, 3, 4];const result = arr.filter((item, index)=>{ return item > 2;});console.log(result); // [3, 4]map()方法 对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组const arr = [1, 2, 3, 4];const result = arr.map((item, index)=>{ return item * index;});console.log(result); // [0, 2, 6, 12]forEach()方法 对数组中的每一项运行给定函数。这个方法没有返回值,本质上与使用 for 循环迭代数组一样const arr = [1, 2, 3, 4];const result = arr.forEach((item, index)=>{ // 执行某些操作});reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。对空数组是不会执行回调函数的。arr.reduce(callback,[initialValue])callback (执行数组中每个值的函数,包含四个参数)previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))currentValue (数组中当前被处理的元素)index (当前元素在数组中的索引)array (调用 reduce 的数组)initialValue (作为第一次调用 callback 的第一个参数。)无返回值const arr = [1, 2, 3];arr.reduce((pev, item)=>{ console.log(pev, item);}, 0);// 运行结果依次为:0 1; undefined 2; undefined 3; 有返回值// pev为上次迭代return的值const arr = [1, 2, 3, 4];const result = arr.reduce((pev, item)=>{ console.log(pev); return pev + item;}, 0);console.log(result); // 10// pev运行结果依次为:0, 1, 3, 6split和joinsplit(): 用于把一个字符串分割成字符串数组。const string = ‘1, 2, 3, 4, 5’;string.split(’,’); // [“1”, “2”, “3”, “4”, “5”]如果string为空,则返回一个空数组const string = ‘’;string.split(’,’); // [""]string.split(); // [""]join(): 用于把数组中的所有元素放入一个字符串。const array = [1, 2, 3, 4, 5];array.join(); // ‘1,2,3,4,5’ 默认用,分割array.join(’|’); // “1|2|3|4|5” 默认用,分割Objectobject映射定义一个object作为配置对象来存放不同状态,通过链表查找const statusMap = { 1:()=>{ console.log(‘a1’) }, 2:()=>{ console.log(‘b2’) } /* n…. */}// 执行let a = 1 statusMapa // a1这样比较清晰,将条件配置与具体执行分离。如果要增加其他状态,只修改配置对象即可。FunctionpromiseECMAscript 6 原生提供了 Promise 对象。Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。Promise 对象有以下两个特点:1、对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:pending: 初始状态,不是成功或失败状态。fulfilled: 意味着操作成功完成。rejected: 意味着操作失败。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。简单实现:function _promise(params) { return new Promise((resolve, reject)=>{ params>0 ? resolve(‘正数’) : reject(‘负数’); });}_promise(1).then(res=>console.log(res)) // 正数_promise(-1).catch(res=>console.log(res)) // 负数Promise.allPromise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。let p1 = new Promise((resolve, reject) => { resolve(‘成功了’)})let p2 = new Promise((resolve, reject) => { resolve(‘success’)})let p3 = Promise.reject(‘失败’)Promise.all([p1, p2]).then((result) => { console.log(result) //[‘成功了’, ‘success’]}).catch((error) => { console.log(error)})Promise.all([p1,p3,p2]).then((result) => { console.log(result)}).catch((error) => { console.log(error) // ‘失败’})需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。Promise.race顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(‘success’) },1000)})let p2 = new Promise((resolve, reject) => { setTimeout(() => { reject(‘failed’) }, 500)})Promise.race([p1, p2]).then((result) => { console.log(result)}).catch((error) => { console.log(error) // 打开的是 ‘failed’})async-awaitES2017 标准引入了async 函数,使得异步操作变得更加方便。async 函数是什么?一句话,它其实就是promise 和Generator 函数的语法糖。asyncasync 用来表示函数是异步的,定义的函数会返回一个promise对象,可以使用then方法添加回调函数。async function test() { return 123;}test().then(res => { console.log(res);// 123});若 async 定义的函数有返回值,return 123;相当于Promise.resolve(123),没有声明式的 return则相当于执行了Promise.resolve();awaitawait 可以理解为是 async wait 的简写。await 必须出现在 async 函数内部,不能单独使用。function notAsyncFunc() { await Math.random();}notAsyncFunc();//Uncaught SyntaxError: Unexpected identifierawait 后面可以跟任何的JS 表达式。虽然说 await 可以等很多类型的东西,但是它最主要的意图是用来等待 Promise 对象的状态被 resolved。如果await的是 promise对象会造成异步函数停止执行并且等待 promise 的解决,如果等的是正常的表达式则立即执行。function sleep(second) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(’ enough sleep~’); }, second); })}function normalFunc() { console.log(’normalFunc’);}async function awaitDemo() { await normalFunc(); console.log(‘something, ~~’); let result = await sleep(2000); console.log(result);// 两秒之后会被打印出来}awaitDemo();// normalFunc// VM4036:13 something, // VM4036:15 enough sleep希望通过上面的 demo,大家可以理解我上面的话。错误处理上述的代码好像给的都是resolve的情况,那么reject的时候我们该如何处理呢?function sleep(second) { return new Promise((resolve, reject) => { setTimeout(() => { reject(‘want to sleep’); }, second); })}async function errorDemo() { let result = await sleep(1000); console.log(result);}errorDemo();// VM706:11 Uncaught (in promise) want to sleep// 为了处理Promise.reject 的情况我们应该将代码块用 try catch 包裹一下async function errorDemoSuper() { try { let result = await sleep(1000); console.log(result); } catch (err) { console.log(err); }}errorDemoSuper();// want to sleep// 有了 try catch 之后我们就能够拿到 Promise.reject 回来的数据了。最后一点,await必须在async函数的上下文中的。参考文章 ...

February 23, 2019 · 3 min · jiezi

Python开发以太坊智能合约指南(web3.py)

在以太坊上获得一个基本的智能合约是一个很简单的事,只需google查询“ERC20代币教程”,你会发现有关如何做到这一点的大量信息。以编程方式与合约交互完全是另一回事,如果你是一个Python程序员,那么教程就很少。所以写这个Python中的以太坊智能合约开发指南。按我的统计对我们来说幸运的是,2017年Web3.py的第4版发布,这意味着现在比以往更容易运行python脚本并观察区块链上发生的神奇事情。像幽灵般的。Piper Merriam,Jason Carver以及其他所有在Web3.py上努力工作以使我们其他人生活更轻松的人大声呼喊,在Sempo,我们正在使用以太坊来使灾难般的响应更加透明,而且它是只有Web3.py才能真正实现。设置首先我们进行设置,确保我们安装了相关的python库。Python库无处不在,但它们的用途是什么?有很多与以太坊相关的python库,但是当人们谈论以太坊时,有两个会出现很多:Web3.py和Pyethereum。乍一看,你应该使用哪一个并不明显。Pyethereum以太坊虚拟机(EVM)的Python实现。反过来,EVM是以太坊协议的一部分,它实际运行智能合约中的代码并确定其输出。因此,如果你想在Python中运行以太坊节点,那么Pyethereum是一个很好的起点。即使你非常高兴在不运行自己的节点的情况下运行智能合约,Pyethereum仍然是一个很好的库,它包含许多功能,可以执行有用的功能,例如从私钥计算用户的地址等等。Web3.py用于实际与以太坊区块链交互的库。我们谈论的事情包括在账户之间转移以太网,发布智能合约以及触发附加现有智能合约的功能。它受到流行的JavaScript库Web3.js的启发,它将成为我们在本教程中使用的主库。好的,少说多做!起初我尝试使用Python3.5版本,但在运行时我遇到了问题,显然是由Python的类型提示造成的。基于Python3.6创建虚拟环境解决了这个问题,所以我建议你做同样的事情。继续并pip-install web3 (确保你获得版本4)。除非你喜欢花钱,否则你需要在以太坊测试网上使用钱包,例如Ropsten和其他大量以太玩法。一个简单的方法是下载Chrome的Metamask扩展,并从那里创建一个新帐户。确保你还选择左侧的’Ropsten Test Net’。即使你的现有钱包中包含真正的以太币,我也强烈建议你为开发目的创建一个新的钱包。我们将使用私钥做一些相对无法预测的事,所以如果它们不小心变成公共主网络的话就不会有问题(公私钥?)为新创建的钱包获取测试Ether非常简单:只需访问faucet.metamask.io并点击“请求来自faucet的1个 以太”。对于我们将要做的事情,这应该是充足的。最后,因为我们将在没有托管我们自己的节点的情况下使用Ropsten TestNet,我们需要一个可以连接Blockchain的供应商。Infura.io适用于此,所以去那里创建一个免费帐户。记下Ropsten TestNet的提供者URL(看起来像https://ropsten.infura.io/FE2…)。部署智能合约使用Python来部署智能合约而不运行自己的节点是非常困难的,所以我们将在这一步上做点儿手脚。对于许多智能合约用例,你只需要执行一次。正如我之前提到的,有关如何部署ERC20合约的百万条指南,因此我们将部署一些不同的(并且更方便地更短)。问:谁喜欢在互联网上分享他们的意见?大家都喜欢?好答案。以下我称之为“Soap Box”肥皂盒的智能合约允许任何人向区块链广播他们想要的任何意见,在永恒的剩余时间(给予或接受)可以看到它。但是有一个问题:只有支付了必要的0.02以太网费用的地址才能播出他们的意见。听起来不太公平,但就这样。Remix,以太坊的在线代码编辑器非常出色,因此在那里创建一个新文件并粘贴以下代码。它是用Solidity(Smart Contracts的编程语言)编写的。如果代码没有太多意义并不重要,我们将在稍后详细介绍相关部分,但最终这是一个Python教程。pragma solidity ^0.4.0;contract SoapBox {// Our ‘dict’ of addresses that are approved to share opinions //我们批准分享意见的地址的“字典” mapping (address => bool) approvedSoapboxer; string opinion; // Our event to announce an opinion on the blockchain //我们的事件发布对区块链的意见 event OpinionBroadcast(address _soapboxer, string _opinion);// This is a constructor function, so its name has to match the contract //这是一个构造函数,所以它的名字必须与合约相匹配 function SoapBox() public { } // Because this function is ‘payable’ it will be called when ether is sent to the contract address. //因为这个函数是“支付”,所以当以太网被发送到合约地址时将被调用。 function() public payable{ // msg is a special variable that contains information about the transaction // msg是一个特殊变量,包含有关交易的信息 if (msg.value > 20000000000000000) { //if the value sent greater than 0.02 ether (in Wei) //如果发送的值大于0.02 ether(在Wei中) // then add the sender’s address to approvedSoapboxer //然后将发件人的地址添加到approvedSoapboxer approvedSoapboxer[msg.sender] = true; } } // Our read-only function that checks whether the specified address is approved to post opinions. //我们的只读函数,用于检查指定地址是否被批准发布意见。 function isApproved(address _soapboxer) public view returns (bool approved) { return approvedSoapboxer[_soapboxer]; } // Read-only function that returns the current opinion //返回当前意见的只读函数 function getCurrentOpinion() public view returns(string) { return opinion; }//Our function that modifies the state on the blockchain //我们的函数修改了区块链上的状态 function broadcastOpinion(string _opinion) public returns (bool success) { // Looking up the address of the sender will return false if the sender isn’t approved //如果发件人未获批准,查找发件人的地址将返回false if (approvedSoapboxer[msg.sender]) { opinion = _opinion; emit OpinionBroadcast(msg.sender, opinion); return true; } else { return false; } }}以下是Metamask变得非常有用的地方:如果你点击重新混音窗口右上角的“run”运行标签并在“Environment”环境下拉列表中选择“Injected Web3”注入的Web3,则“Account”帐户下拉列表中应填充你的帐户地址早在MetaMask中创建。如果没有,只需刷新浏览器即可。然后单击“create”创建。Metamask应该弹出一个弹出窗口,要求你确认交易。如果没有,只需打开Metamask扩展并在那里执行:你将在Remix控制台底部收到一条消息,告知你合约的创建正在等待处理。单击链接以在Etherscan上查看其状态。如果刷新并且“To”收件人字段填充了合约地址,则合约已成功部署。一旦你记下了合约地址,我们就该开始通过Web3.py与合约进行交互了。在我看来,有四种(半)方式可以与以太坊智能合约进行互动。最后两个(一半)经常混在一起,但差异很重要。我们已经看到了第一个:在区块链上部署智能合约。现在我们将介绍其余的python:向合约发送以太:真正自我解释,将以太币从钱包发送到智能合约的地址。希望换取有用的东西。调用函数:执行智能合约的只读功能以获取某些信息(例如地址的余额)。与功能进行交易:执行智能合约的功能,该功能可以更改区块链的状态。查看事件:查看由于先前的功能交易而发布到区块链的信息。将以太币发送给合约一些(但不是全部)智能合约包括“payable”应付功能。如果你将Ether发送到合约的地址,则会触发这些功能。一个典型的用例就是ICO:将以太送到合约中,然后返回给你的是代币。首先,我们将从导入开始,创建一个新的web3对象,通过Infura.io连接到Ropsten TestNet。import timefrom web3 import Web3, HTTPProvidercontract_address = [YOUR CONTRACT ADDRESS]wallet_private_key = [YOUR TEST WALLET PRIVATE KEY]wallet_address = [YOUR WALLET ADDRESS]w3 = Web3(HTTPProvider([YOUR INFURA URL]))w3.eth.enable_unaudited_features()你可以在Metamask中的帐户名称旁边的菜单中找到你的钱包私钥。因为我们使用的Web3.py的某些功能尚未经过完全审核以确保安全性,所以我们需要调用w3.eth.enable_unaudited_features()来确认我们知道可能会发生问题的情况。我告诉过你我们用私钥做了一些危险的事情!现在我们将编写一个函数,将以太币从我们的钱包发送到合约:def send_ether_to_contract(amount_in_ether): amount_in_wei = w3.toWei(amount_in_ether,’ether’); nonce = w3.eth.getTransactionCount(wallet_address) txn_dict = { ’to’: contract_address, ‘value’: amount_in_wei, ‘gas’: 2000000, ‘gasPrice’: w3.toWei(‘40’, ‘gwei’), ’nonce’: nonce, ‘chainId’: 3 } signed_txn = w3.eth.account.signTransaction(txn_dict, wallet_private_key) txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction) txn_receipt = None count = 0 while txn_receipt is None and (count < 30): txn_receipt = w3.eth.getTransactionReceipt(txn_hash) print(txn_receipt) time.sleep(10) if txn_receipt is None: return {‘status’: ‘failed’, ’error’: ’timeout’} return {‘status’: ‘added’, ’txn_receipt’: txn_receipt}首先让我们回顾一下交易字典txn_dict:它包含了定义我们发送给智能合约的交易所需的大部分信息。to:我们将以太送到哪里(在这种情况下是智能合约)。Vaule:我们送多少钱单位wei。gas:gas是衡量在以太坊上执行交易的计算工作量度。在这种情况下,我们指定了我们愿意执行此交易的天然气量的上限。gasPrice:我们愿意为每单位gas支付多少钱(以wei为单位)。Nonce:这是一个地址nonce而不是更常见的工作证明。它只是发送地址所做的先前交易次数的计数,用于防止双重花费。Chain ID:每个以太坊网络都有自己的链ID:主网的ID为1,而Ropsten为3。你可以在这里找到更长的列表。关于gas限制的快速说明:有一些功能可以让你估算交易将使用多少gas。但是,我发现选择限制的最佳方法是计算出你愿意支付多少钱,然后再让交易失败,然后再去做。一旦我们定义了交易的重要部分,我们就会使用我们钱包的私钥对其进行签名。然后它就可以发送到网络了,我们将使用sendRawTransaction方法。在矿工决定将其包含在一个区块中之前,我们的交易实际上不会完成。一般而言,你为每个单位支付的费用Gas(记住我们的天然气价格参数)决定了一个节点决定将你的交易包含在一个区块中的速度(如果有的话)。https://ethgasstation.info/是…,可以确定你将等待你的交易包含在一个区块中的时间。此时间延迟意味着交易是异步的。当我们调用sendRawTransaction时,我们会立即获得交易的唯一哈希值。你可以随时使用此哈希来查询你的交易是否已包含在块中。我们知道,当且仅当我们能够获得交易收据时才将交易添加到区块链中(因为所有好的购买都带有收据吗?)。这就是为什么我们创建循环来定期检查我们是否有收据: txn_receipt = None count = 0 while txn_receipt is None and (count < 30): txn_receipt = w3.eth.getTransactionReceipt(txn_hash) print(txn_receipt) time.sleep(10)值得注意的是,交易可以添加到区块链中,但仍然因各种原因而失败,例如没有足够的gas。这就是将以太符号发送给合约的Python代码。让我们快速回顾一下我们在Solidity中写的应付函数:function() public payable{ if (msg.value >= 20000000000000000) { approvedSoapboxer[msg.sender] = true; } }Msg是智能合约中的一个特殊变量,其中包含有关发送到智能合约的交易的信息。在这种情况下,我们使用msg.value,它给出了交易中发送的Ether数量(在Wei而不是raw Ether中)。同样,msg.sender给出了进行交易的钱包的地址:如果已经发送了足够的以太币,我们会将其添加到已批准帐户的字典中。继续运行send_ether_to_contract函数。希望你能收到回执。你还可以通过在Etherscan的Ropsten Network部分查找你的钱包地址来检查交易是否完成。我们将在下一节中获得Python中的更多信息。调用一个函数我们刚刚向我们的智能合约发送了一些以太币,因此我们想检查我们的钱包地址是否已被批准分享意见是有意义的。为此,我们在智能合约中定义了以下功能:function isApproved(address _soapboxer) public view returns (bool approved) { return approvedSoapboxer[_soapboxer]; }与python相比,这个函数附带了很多额外的东西,比如声明类型(地址和bool)。尽管如此,这个函数只需要一个地址(_soapboxer参数),在有效(但不完全)的哈希表/python dict中查找相应的批准布尔值并返回该值。你调用的时候一个智能合约函数,以太坊节点将计算结果,并将其返回给你。这里的事情变得有点复杂:调用是只读的,这意味着它们不会对区块链进行任何更改。如果上述函数包含一行代码来记录数字时间,则检查地址是否已批准:approvedCheckedCount[_soapboxer] = approvedCheckedCount[_soapboxer] + 1然后,当调用该函数时,该节点将计算approvedCheckedCount的新值,但一旦返回结果就丢弃它。作为只读的交换,函数调用不会花费你运行任何以太,因此你可以愉快地检查帐户是否已被批准而不必担心成本。让我们跳回到我们的python文件的顶部并添加一些更多的设置代码。import contract_abicontract = w3.eth.contract(address = contract_address, abi = contract_abi.abi)你需要创建另一个名为contract_abi的python文件。这将包含一个大的JSON信息字符串,Python需要与我们在智能合约中定义的函数进行交互,称为应用程序二进制接口(ABI)。你可以在Remix中找到智能合约的ABI的JSON字符串:单击编译“Compile”选项卡。单击详细信息“Details”——应显示包含大量信息的模式。向下滚动到ABI部分,然后单击复制到剪贴板“Copy to clipboard”图标。将复制的字符串粘贴到contract_abi.py文件中,该文件应如下所示:abi = “”"[ { A BIG LIST OF ABI INFO SPREAD ACROSS MULTIPLE DICTS }]““我们添加到主python文件的另一行现在使用此ABI JSON字符串并使用它来设置合约对象。如果你浏览合约,你会注意到它包含一个函数属性,其中包含我们在智能合约中创建的三个函数。现在我们将创建一个python函数,该函数调用Smart Contract智能合约的isApproved函数来检查指定的地址是否被批准分享意见。def check_whether_address_is_approved(address): return contract.functions.isApproved(address).call()那很短暂。你现在可以使用它来检查你的钱包地址是否已获批准。如果你之前运行了send_ether_to_contract函数并发送了足够数量的以太,那么希望你能回到true。与函数交易我们正在与智能合约进行最后的主要互动:广播意见。再一次,我们来看看我们的Solidity Code:function broadcastOpinion(string _opinion) public returns (bool success) { if (approvedSoapboxer[msg.sender]) { opinion = _opinion; emit OpinionBroadcast(msg.sender, opinion); return true; } else { return false; } }这里没有什么新东西:我们采用传入的_opinion参数并使用它来设置全局变量意见。(如果你愿意,可以通过getter函数查询实习生)。有一条线有点不同:emit OpinionBroadcast(msg.sender, opinion)我们很快就会介绍。 当你通过交易与智能合约的功能进行交互时,功能对智能合约状态所做的任何更改都会在区块链上发布。为了换取这种特权,你必须向矿工支付一些(希望很小)的以太量。Python时间:def broadcast_an_opinion(covfefe): nonce = w3.eth.getTransactionCount(wallet_address) txn_dict = contract.functions.broadcastOpinion(covfefe).buildTransaction({ ‘chainId’: 3, ‘gas’: 140000, ‘gasPrice’: w3.toWei(‘40’, ‘gwei’), ’nonce’: nonce, }) signed_txn = w3.eth.account.signTransaction(txn_dict, private_key=wallet_private_key) result = w3.eth.sendRawTransaction(signed_txn.rawTransaction) tx_receipt = w3.eth.getTransactionReceipt(result) count = 0 while tx_receipt is None and (count < 30): time.sleep(10) tx_receipt = w3.eth.getTransactionReceipt(result) print(tx_receipt) if tx_receipt is None: return {‘status’: ‘failed’, ’error’: ’timeout’} processed_receipt = contract.events.OpinionBroadcast().processReceipt(tx_receipt) print(processed_receipt) output = “Address {} broadcasted the opinion: {}”\ .format(processed_receipt[0].args._soapboxer, processed_receipt[0].args._opinion) print(output) return {‘status’: ‘added’, ‘processed_receipt’: processed_receipt}这实际上与将Ether发送到智能合约时使用的过程相同。我们将创建并签署一个交易,然后将其发送到网络。再一次,交易是异步的,这意味着无论函数被告知在Solidity代码中返回什么,你实际得到的东西总是交易的哈希。鉴于交易本身并没有返回任何有用的信息,我们需要其他东西。这导致我们采用最后(半)方式与智能合约进行互动。事件events我将事件称为与智能合约交互的“一半”方式,因为从技术上讲,它们是由交易发出的。 事件是智能合约以易于阅读的形式在区块链上记录事物的方式,它们基本上只是一组可以使用特定交易的收据查找的值。我们在智能合约的最顶层定义了一个:event OpinionBroadcast(address _soapboxer, string _opinion);然后,当我们使用broadcastOpinion函数时,我们使用它向区块链发出信息。将交易添加到块后,你可以使用交易哈希查询区块链以查找OpinionBroadcast事件发出的特定值。这是我们在函数broadcast_an_opinion中的最后一点python代码。你会注意到我们要求事件发出的信息存储在’args’属性中。这个事件非常公开。实际上,任何人都可以轻松使用Etherscan或类似工具来查看智能合约发出的所有事件的日志。Etherscan会自动检测“Transfer”转移事件并列出所有事件。Nifty如果你已经做到这一点,你就有权发表意见。继续用你选择的意见运行broadcast_an_opinion。如果一切顺利进行,你应该很快就会收到已处理的收据,以及已放入区块链的OpinionBroadcast事件的打印输出。Nice。这是完整的python代码:import timefrom web3 import Web3, HTTPProvidercontract_address = [YOUR CONTRACT ADDRESS]wallet_private_key = [YOUR TEST WALLET PRIVATE KEY]wallet_address = [YOUR WALLET ADDRESS]w3 = Web3(HTTPProvider([YOUR INFURA URL]))w3.eth.enable_unaudited_features()contract = w3.eth.contract(address = contract_address, abi = contract_abi.abi)def send_ether_to_contract(amount_in_ether): amount_in_wei = w3.toWei(amount_in_ether,’ether’); nonce = w3.eth.getTransactionCount(wallet_address) txn_dict = { ’to’: contract_address, ‘value’: amount_in_wei, ‘gas’: 2000000, ‘gasPrice’: w3.toWei(‘40’, ‘gwei’), ’nonce’: nonce, ‘chainId’: 3 } signed_txn = w3.eth.account.signTransaction(txn_dict, wallet_private_key) txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction) txn_receipt = None count = 0 while txn_receipt is None and (count < 30): txn_receipt = w3.eth.getTransactionReceipt(txn_hash) print(txn_receipt) time.sleep(10) if txn_receipt is None: return {‘status’: ‘failed’, ’error’: ’timeout’} return {‘status’: ‘added’, ’txn_receipt’: txn_receipt}def check_whether_address_is_approved(address): return contract.functions.isApproved(address).call()def broadcast_an_opinion(covfefe): nonce = w3.eth.getTransactionCount(wallet_address) txn_dict = contract.functions.broadcastOpinion(covfefe).buildTransaction({ ‘chainId’: 3, ‘gas’: 140000, ‘gasPrice’: w3.toWei(‘40’, ‘gwei’), ’nonce’: nonce, }) signed_txn = w3.eth.account.signTransaction(txn_dict, private_key=wallet_private_key) result = w3.eth.sendRawTransaction(signed_txn.rawTransaction) tx_receipt = w3.eth.getTransactionReceipt(result) count = 0 while tx_receipt is None and (count < 30): time.sleep(10) tx_receipt = w3.eth.getTransactionReceipt(result) print(tx_receipt) if tx_receipt is None: return {‘status’: ‘failed’, ’error’: ’timeout’} processed_receipt = contract.events.OpinionBroadcast().processReceipt(tx_receipt) print(processed_receipt) output = “Address {} broadcasted the opinion: {}”\ .format(processed_receipt[0].args._soapboxer, processed_receipt[0].args._opinion) print(output) return {‘status’: ‘added’, ‘processed_receipt’: processed_receipt}if name == “main”: send_ether_to_contract(0.03) is_approved = check_whether_address_is_approved(wallet_address) print(is_approved) broadcast_an_opinion(‘Despite the Constant Negative Press’)打包封装所以关于它。正如我所提到的,我们还没有达到使用python实际部署智能合约很容易的地步,但其他一切都在那里。在Sempo,我们正在使用上面提到的所有技术来使问题响应更加透明。感谢Sebastian Dirman指出w3.toWei(value, ‘ether’)是一种更好的方式在Ether和Wei之间进行转换——只需将以太量乘以1000000000000000000即可导致类型错误!======================================================================分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。汇智网原创翻译,转载请标明出处。这里是原文Python以太坊智能合约开发指南 ...

December 17, 2018 · 4 min · jiezi