以太坊部署笔记(二)

2.solidity语言简述 2.1合约基本元素
以太坊部署笔记(二)
文章图片

2.2 合约的生命周期
以太坊部署笔记(二)
文章图片

调用destroy函数之后,合约仍然存在于区块链之上,但是函数无法被调用,调用后会抛出异常。
2.3 数据类型分类
1.值类型(值传递)
2. 引用类型(指针传递)
这两种类型的的特点相比大家也很熟悉,就不再赘述,在sol中只需要对变量进行修饰,其中值类型用memory,指针用storage。
2.4函数声明
以太坊部署笔记(二)
文章图片

几个非常非常重要的关键字

修饰符 说明
public 公有,任何人(拥有以太坊账户的)都可以调用
private 私有, 只有智能合约内部可以调用
external 仅合约外部可以调用,合约内部需使用this调用
internal 仅合约内部和继承的合约可以调用
view/constant 函数会读取但是不会修改任何contract的状态变量
pure(纯净的) 函数不使?任何智能合约的状态变量
payable 调用函数需要付钱,钱付给了智能合约的账户
returns 返回值函数声明中使用
构造函数
仅在部署合约时调一次次,完成对合约的初始化。
constructor
2.5. 地址(Address)
作为以太坊的一种特殊的类型,大小为20个字节 ,20 * 8 = 160位 ,所以可以用一个 uint160 编码。地址是所有 合约的基础,所有的合约都会继承地址对象,通过合约的地址串,调?合约内的函数。
属性/方法 含义
balance 获取余额
transfer 转账
call 合约内部调用合约
2.6 Enums枚举类型
枚举类型是在Solidity中的一种用户字定义类型。 枚举可以显示的转换与整数进行转换,但不能进行隐式转换。显示的转换会在运用时检查数值范 围,如果不匹配,将会引起异常。 枚举类型应至少有一名成员,枚举元素默认为uint8,当元素数量足够多时,会自动变为uint16, 第一个元素默认为0,使用超出范围的数值时会报错。
pragma solidity ^0.4.0; contract test { enum WeekDays { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }WeekDays currentDay; WeekDays defaultday = WeekDays.Sunday; function setDay(weekDays _day) public { currentDay = _day; } function getDay() public view returns(uint256) { return uint256(currentDay); } function getDefaultDay() public view returns(uint256) { return uint256(defaultday); }

2.7字典/映射/hash表(mapping)
1.键key的类型允许除映射外的所有类型,如数组,合约,枚举,结构体,值的类型,无限制。
2.无法判断一个mapping中是否包含某个key,因为它认为每一个都存在,不存在的返回0或false。
3.映射可以被视作为一个哈希表,在映射表中,不存储键的数据,仅仅存储它 的 keccak256 哈希值,用来查找值时使用。
4.映射类型,仅能用来定义状态变量,或者是在内部函数中作为storage类型的引用。
5.不支持length
6.key不支持string 类型
mapping(uint => string) id_names;
2.8重要的两个全局变量
1.msg.sender
每一次和以太坊交互时都会产生意笔交易,这笔交易的执行者就是msg.sender。
简而言之:谁调用的,msg.sender就是谁,每笔交易的msg.sender都可以不同。
2.msg.value
我们在介绍payable关键字的时候说,如果函数修饰为payable,那么这个函数可以接收转账,这 笔钱通过remix的value输入框传递进来。 在转账操作中,这笔钱是通过我们调用一个函数从而产生一笔交易而转入合约的,换句话说,是 这笔交易附带了一笔钱。在合约中,每次转一的value是可以通过msg.value来获取到的。
注意,
1.单位是wei
2.有msg.value,就必须有payable关键字
2.9 区块和交易的属性
函数 含义
blockhash(uint blockNumber) 哈希值(byte32)
block.coinbase (address) 当前块矿?的地址。
block.difficulty (uint)当前块的难度
block.gaslimit (uint)当前块的gaslimit
block.number (uint)当前区块的块号
block.timestamp (uint)当前块的时间戳
msg.data (bytes)完整的调?数据(calldata)
gasleft() (uint)当前还剩的gas
msg.sender (address)当前调?发起?的地址
msg.sig (bytes4)调?数据的前四个字节(函数标识符)
msg.value (uint)这个消息所附带的货币量,单位为wei
now (uint)当前块的时间戳 等同于block.timestamp
tx.gasprice (uint) 交易的gas价格
tx.origin (address)交易的发送者(完整的调?链)
2.10 修饰器(modifier)
修改器(Modifiers)可以庸来轻易的改变一个函数的行为。==比如用于在函数执行前检查某种前置条件 ==。修改器是一种合约属性,可被继承,同时还可被派生的合约重写(override)。下面我们来看一段示 例代码:
pragma solidity ^0.4.24; contract Test { uint256 public value ; address public owner; constructor() public { owner = msg.sender; }modifier onlyOwner(address caller) { //require(msg.sender == owner); require(caller == owner); //_;代表这个修饰器所修饰函数的代码 _; } //使用修饰器,将仅管理员可以执行的限定放到函数外面 function changeValue(uint256 _value) onlyOwner(msg.sender) public{ value = https://www.it610.com/article/_value; } }

2.11 合约创建和外部调用
1.new关键字,返回值是一个address, 需要显示转化类型 后才能使用
2. contract1形式,此时contract1是空的,需要赋值地址才能使用·,否则报错。
//10表示contract1中constructor需要的参数 address addr1 = new Contract1(10); c1 = C1(addr1); return c1.getValue();

2.12 合约继承
is关键字
pragma solidity ^0.4.0; contract Base1{ function data() pure returns(uint){ return 1; } } contract Base2{ function data() pure returns(uint){ return 2; } } contract son1 is Base1, Base2{ } contract son2 is Base2, Base1{ }

2.13 内置数学函数
keccak256(…) returns (bytes32)
哈希函数,代替sha3(废弃)
pragma solidity ^0.4.24; contract Test { function test() public pure returns(bytes32){ bytes memory v1 = abi.encodePacked("abc", "b", uint256(1), "hello"); return keccak256(v1); } }

3.NODEJS 和ES6语法简述 由于我们所部署的DAPP的架构是完全支持JavaScript中的ES6语法的,所以需要对ES6语法的基本规则进行一个简述。
3.1. 定义变量
1.使用 const 来定义一个常量,常量也就是不能被修改,不能被重新赋值的变量。
2.使用 let 来定义一个变量,?不要再使用 var 了,因为 var 有很多坑;可以认为 let 就是修 复了bug的 var 。比如,var允许重复声明变量而且不报错;var的作用域让人感觉疑惑。
3.2 解构赋值
数组的解构赋值
const arr = [1, 2, 3] //我们得到了一个数组 let [a, b, c] = arr //可以这样同时定义变量和赋值 console.log(a, b, c); // 1 2 3

对象的解构赋值(常用)
const obj = { name: '俊哥',address:'深圳', age: '100'} //我们得到了一个对象 let {name, age} = obj //可以这样定义变量并赋值 console.log(name, age); //俊哥 100

函数参数的解构赋值(常用)
const person = { name: '小明', age: 11} function printPerson({name, age}) { // 函数参数可以解构一个对象 console.log(`姓名:${name} 年龄:${age}`); } printPerson(person) // 姓名:?明 年龄:11

3.3函数扩展
箭头函数。
将 function 换成 => 定义的函数,就是箭头函数 只适合用于普通函数,不要用在构造函数,不要用在成员函数,不要用着原型函数。
function add(x, y) { return x + y } // 这个箭头函数等同于上面的add函数 (x, y) => x +y; // 如果函数体有多个,则需要用大括号包裹 (x, y) => { if(x >0){ return x + y }else { return x - y } }

3.4 Class继承
由于js一开始被设计为函数式语言,万物皆函数。所有对象都是从函数原型继承而来,通过 继承某个函数的原型来实现对象的继承。但是这种写法会让新学者产生疑惑,并且和传统的OOP 语言差别很大。ES6 封装了class语法来大大简化了对象的继承。
class Person { constructor(name, age){ this.name = name this.age = age } // 注意:没有function关键字 sayHello(){ console.log(`?家好,我叫${this.name}`); } }class Man extends Person{ constructor(name, age){ super(name, age) } //重写父类的方法 sayHello(){ console.log('我重写了父类的方法!'); } } let p = new Person("小明", 33) //创建对象 p.sayHello() // 调用对象p的方法,打印 let m = new Man("王五", 33) m.sayHello() // 我重写了父类的?方法!

3.5同步与异步
同步调用(阻塞)
var fs = require("fs"); var data = https://www.it610.com/article/fs.readFileSync('input.txt'); console.log(data.toString()); console.log("程序执?结束!");

异步调用(非阻塞)
var fs = require("fs"); fs.readFile('input.txt', function (err, data) { if (err) return console.error(err); console.log(data.toString()); }); console.log("程序执?结束!");

promise写法:
let fs = require('fs')//解决办法:把每?个异步函数都封装成?个pomise let readFilePromise = () => { return new Promise((resolve, reject) => { try { fs.readFile('./1.txt', 'utf-8', function (err, data) { console.log('读取?件: ', data) resolve(data) }) } catch (e) { reject(e) } }) }let writeFilePromise = (data) => { return new Promise((resolve, reject) => { fs.writeFile('./2.txt', data, 'utf-8', function (err) { if (err) { reject(err) } resolve('写?成功!') }) }) }let statPromise = () => { return new Promise((resolve, reject) => { fs.stat('./2.txt', function (err, stat) { if (err) { reject(err) } // console.log('?件状态:', stat) resolve(stat) }) }) } //如果想使?async,await,promise, //调?函数的外?修饰为async //promise函数前?加上await let checkStat2 = async () => { try { let data = https://www.it610.com/article/await readFilePromise() let res = await writeFilePromise(data) console.log('res :', res) let stat = await statPromise() console.log('stat:', stat) } catch (e) { console.log(e) } }

3.path模块和fs模块
名字 作用
path.basename 返回一个路径的最后一部分
path.dirname 返回一个路径的目录名
path.extname 返回一个路径的扩展名
path.join 用于拼接给定的路径片段
path.normalize 将一个路径正常化
path.resolve([from …], to) 基于当前的执行目录,返回一个绝对路径,退一层演示
fs.stat/fs.statSync 访问文件的元数据,比如文件大小,文件的修改时间
fs.readFile/fs.readFileSync 异步/同步读取文件
fs.writeFile/fs.writeFileSync 异步/同步写入文件
fs.readdir/fs.readdirSync 读取文件夹内容
fs.unlink/fs.unlinkSync 删除文件
fs.rmdir/fs.rmdirSync 只能删除空文件夹
4.Web3JS模块 4.1智能合约编译原理
  1. 源代码—> solidity 编译器 —> abi/ bytecode —>部署到某个网络。
    4.2. web3调用图示
    以太坊部署笔记(二)
    文章图片
以太坊部署笔记(二)
文章图片

4.3 编译合约
在compile.js填入如下代码:
//导入·solc编译器 let solc = require('solc') //0.4.26 let fs = require('fs') //读取合约 let sourceCode = fs.readFileSync('./contracts/SimpleStorage.sol', 'utf-8') let output = solc.compile(sourceCode, 1) module.exports = output['contracts'][':SimpleStorage']

4.4 启动Ganache UI
以太坊部署笔记(二)
文章图片
4.5 部署合约
在depole.js中添加如下代码:
let {bytecode, interface} = require('./01-compile')let Web3 = require('web3') //2. new 一个web3实例 let web3 = new Web3() //3. 设置网络 web3.setProvider('HTTP://192.168.28.30:7545')const account = '0xd5957914c31E1d785cCC58237d065Dd25C61c4D0'console.log(web3.currentProvider) //1. 拼接合约数据let contract = new web3.eth.Contract(JSON.parse(interface)) //2. 拼接bytecode contract.deploy({ data: bytecode, //合约的bytecode arguments: ['HelloWorld'] //给构造函数传递参数,使用数组 }).send({ from: account, gas: '3000000', //不要用默认值,一定要写大一些, 要使用单引号 gasPrice: '1', }).then(instance => { console.log('address :', instance.options.address) })

4.6 获取链上合约实例
创建interaction.js,这个是与合约交互的文件。
调用前需要将链上的合约实例找到,这样才能完成交互,需要用到 :
1.web3 : 指定网络
2. ABI:二进制接口
3. address:合约地址
let Web3 = require('web3') //2. new 一个web3实例 let web3 = new Web3() //3. 设置网络 web3.setProvider('HTTP://192.168.28.30:7545')let abi = [{ "constant": true, "inputs": [], "name": "getValue", 9"outputs": [{"name": "", "type": "string"}], "payable": false, "stateMutability": "view", "type": "function" }, {"constant": false, "inputs": [{"name": "_str", "type": "string"}], "name": "setValue", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, {"inputs": [{"name": "_str", "type": "string"}], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }]let address = '0x0FE5006b70A0D58AD3c4d4BC9DAC02C970510Cf6' //此处abi已经json对象,不需要进?parse动作 let contractInstance = new web3.eth.Contract(abi, address) console.log('address :', contractInstance.options.address)module.exports = contractInstance

【以太坊部署笔记(二)】4.7 调用合约
let test = async () => { try { let v1 = await instance.methods.getValue().call({ from: from }) console.log('v1:', v1)let res = await instance.methods.setValue('HelloHangTou').send({ from: from, }) console.log('res:', res) let v2 = await instance.methods.getValue().call({ from: from }) console.log('v2:', v2) } catch (e) { console.log(e) } }test()

    推荐阅读