以太坊部署笔记(二)

2.solidity语言简述

2.1合约基本元素
以太坊部署笔记(二)_第1张图片
2.2 合约的生命周期
以太坊部署笔记(二)_第2张图片
调用destroy函数之后,合约仍然存在于区块链之上,但是函数无法被调用,调用后会抛出异常。

2.3 数据类型分类
1.值类型(值传递)
2. 引用类型(指针传递)
这两种类型的的特点相比大家也很熟悉,就不再赘述,在sol中只需要对变量进行修饰,其中值类型用memory,指针用storage。
2.4函数声明
以太坊部署笔记(二)_第3张图片
几个非常非常重要的关键字

修饰符 说明
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 = _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 = 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 = 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张图片

以太坊部署笔记(二)_第5张图片
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
以太坊部署笔记(二)_第6张图片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()

你可能感兴趣的:(区块链)