科普 | 什么是Soilidity?



什么是Soilidity?

Solidity是一种面向合约的高级编程语言,用于实现智能合约。Solidity已经被设计用于以太坊虚拟机。

Solidity===智能合约。

Solidity的代码封装在合约中。合约是以太坊应用程序的基本构建块——所有变量和函数都属于一个合约,这将是所有项目的起点。

pragma solidity >=0.5.0 <0.6.0; 

contract HelloWorld { 

}

在上面的代码中,我们提供了考虑到我们的代码与b/w 0.5到0.6的任何版本兼容的版本。我们还创建了一个名为“HelloWorld”的合约。

状态变量和整数

状态变量永久存储在合约存储中。这意味着它们被写入以太坊区块链。可以把它们想象成写入DB。

contract Example { 

// This will be stored permanently in the blockchain 

uint myUnsignedInteger = 100; 

string name = "vivek"

}

Uint数据类型是一个无符号整数。它应该是非负的。

数据类型

值类型:

Boolean(true / false),Integers(int / uint), Address(以太坊地址的大小),String, enum

引用类型:

数组、结构、映射

数学运算

Addition: x + y 

Subtraction: x — y, 

Multiplication: x * y 

Division: x / y 

Modulus / remainder: x % y (for example, 13 % 5 is 3, because if you divide 5 into 13, 3 is the remainder)

Exponential Operation uint x = 5 ** 2; // equal to 5² = 25

结构

类似于C语言中的struct。当我们需要创建具有多个属性的复杂数据类型时,我们使用结构体。

struct Person { 

uint age; 

string name;

数组

数据的集合称为数组。两种类型:固定数组和动态数组。

顾名思义,固定数组有预定义的大小,而动态数组没有大小。

// Array with a fixed length of 2 elements: 

uint[2] fixedArray;

// another fixed Array, can contain 5 

strings: string[5] stringArray;

// a dynamic Array — has no fixed size, can keep growing: 

uint[] dynamicArray;

我们可以组合结构和数组。创建一个结构,然后拥有一个结构数组。就像在面向对象范例(如Java)中有一个对象和一个对象数组一样。

pragma solidity >=0.5.0 <0.6.0; 

contract StudentFactory { 

struct student { 

string name; 

uint roll; } student[] public students; // creates an array named students of student type objects

函数声明

function eatHamburgers(string memory _name, uint _amount) public { }

函数的可见性是公开的。有两种方式可以传递参数给Solidity函数:

按值和按引用

eatHamburgers(“vitalik”, 100);

私人/公共函数

在Solidity中,函数默认是公共的,因此任何人都可以在网络中调用公共函数。然而,出于安全考虑,我们将函数设为私有,这样只有所有者才能调用函数。

function _eatHamburgers(string memory _name, uint _amount) private { 

}

按照惯例,私有函数的开头带有下划线。

内部/外部关键字

还有两种类型的函数可见性。内部类似于私有,除了它可以被继承的合约访问,即继承。

外部类似于公共。除了声明了这个函数的联系人之外,所有的合约都可以调用这个函数。

在函数中返回

函数声明包含返回值的类型。

function sayHi() public view/pure returns (string memory) { 

return “Hi”;

}

这些函数可以标记为pure/view。当我们甚至没有访问传递的数据时,我们就将函数标记为pure。如果函数不修改数据,只查看数据,那么它将被标记为view。

类型转换

数据类型之间的转换称为类型转换。

uint8 a = 5; 

uint b = 6;

// line below throws an error because a*b returns a uint, not uint8: 

uint8 c = a * b;

// we have to typecast b as a uint8 to make it work: 

uint8 c = a * uint8(b);

事件

事件用于向前端传达后端区块链网络上发生了一些事情。

// declare the event 

event NotifyOnFrontend(uint x); 

function add(uint _x, uint _y) public returns (uint) { 

uint result = _x + _y; 

//fire an event to let the frontend know the function was called 

emit NotifyOnFrontend(result); 

return result; 

}

我们的前端代码应该已经安装了web3,并且应该监听“NotifyOnFrontend”事件,这样才能工作。我们的JavaScript框架或普通JS将不得不监听这个事件来接收它:

YourContract.NotifyOnFrontend(function(error, result) { 

// do something with result 

})

映射

这是存储有组织数据(如数组和结构)的另一种方法

mapping (address => uint) public accountBalance;

这是一个键值存储。address是键,accountBalance是值。

这可以用于在区块链中存储多个对象(数据)。检查示例如下:

例子:

contract Example { 

struct UserInfo { 

unit age; string dob;

mapping(string => UserInfo) allusers;

function setUserInfo(string _name, uint _age, string _dob) public {
allusers[_name].age = _age;
allusers[_name].dob = _dob;
}

function getUserInfo(string name) public view returns(uint, string) { 

return (allusers[name].age, allusers[_name].dob); 

}

现在,如果可以用不同的值多次调用setUserInfo,比如:

setuserInfo("Vivek",26, 25/05/1995) setuserInfo("Supu", 23, 01/09/1998)

要获得这些值,只需传递名称:

getUserInfo("Vivek"); // 26 25/05/1995 

getuserInfo("Supu"); // 24 01/09/1998

全局变量

这些变量可用于像msg.sender这样的所有函数。我们所编写的任何Solidity程序,都应该由所有者调用。发送者的地址存储在msg.sender 全局变量中。

require

require用于验证这两个语句,并据此做出决定。如果条件为真,则代码成功运行,否则就抛出错误

function sayHi(string memory name) public returns (string memory) { /Compares if _name equals “Vivek” Throws an error and exits if not true. Solidity doesn’t have native string comparison, so we compare their keccak256 hashes to see if the strings are equaq 

require(keccak256(abi.encodePacked(name)) == keccak256(abi.encodePacked(“Vivek”)));

// If it’s true, proceed with the function: 

return “Hi!”;

sayHi(“Vivek”) // executes successfully 

sayHi(“Supu”) // throws an error

因此,require对于在运行函数之前验证某些条件必须为真非常有用。

继承

有时候,与其制定一个非常长的合约,还不如将代码逻辑拆分为多个合约来组织代码。

contract Animal {

function catchphrase() public returns (string memory) { 

return “Animal”;

} contract Cat is Animal {

function anotherCatchphrase() public returns (string memory) { 

return “Cat is an Animal”;

}

}

import

将代码拆分为多个文件,并使用import来使用另一个文件中的功能。

这通常是在Solidity项目中处理长代码库的方式。

存储和内存

存储是指永久存储在区块链上的变量。内存变量是临时的,在对合约的外部函数调用之间会被删除。可以把它想象成电脑的硬盘与内存。

与区块链网络中的其他合约交互

关于这一点,我将写一篇单独的文章。现在,保持简短:

为了与其他合约交互,我们声明了一个类似object的接口。我们创建了一个合约,并在里面声明了一个函数,我们想要从另一个合约调用或使用它。函数只是骨架,它不包含主体。

contract GetNumber { 

function getNum(uint _num) public returns(uint){ 

return _num; 

}

假设有一个合约,我们想要使用上面的getNum函数。为此,我们将在项目中创建一个合约,并声明一个getNum函数框架(没有函数体)。

contract NumberInterface { 

function getNum(uint _num) public returns(uint); 

}

现在我们可以从NumberInterface合约中调用getNum函数。

在将合约部署到以太坊后,它就变成了不可变的,也就是说它不能被修改。部署到合约中的初始代码将永久地停留在区块链上。这就是安全性在Solidity中如此重要的原因之一。如果我们的合约代码中有一个缺陷,就没有办法在以后修补它。必须告诉我们的用户开始使用具有修复功能的不同智能合约地址。

函数修饰符

函数修饰符看起来就像函数,但是使用关键字修饰符而不是关键字函数。这些用于特殊情况,例如当您只希望您的所有者而不是所有人做某事时。

这有助于更新DApp的关键部分,同时防止其他用户破坏我们的合约。我处理过的一个用例是——当我们想在执行任何用例之前验证语句时。

gas

用户支付gas费来在以太坊网络上运行合约。gas以以太(以太坊上的货币)为单位计算。我们的函数的总gas成本等于它所有单独操作的总gas成本。

更多关于存储的内容

存储内存被永久写入到区块链中。全世界成千上万的节点需要将这些数据存储在它们的硬盘上,并且随着区块链的增长,这些数据量也会随着时间的推移而增长。所以这样做是有代价的。

为了降低成本,我们希望避免将数据写入存储,除非绝对必要。有时,这涉及到看似低效的编程逻辑——比如每次调用函数时都要在内存中重新构建数组,而不是简单地将该数组保存在全局存储变量中以便快速查找。

因此,建议尽可能使用内存类型,这样数据就不会永久存储,从而节省成本。循环在Solidity中将比使用存储更便宜。所以尽可能for 循环中使用内存。这与Java、Python等语言中所做的完全相反,因为for循环的计算成本更高。

For循环

Syntax类似于Javascript。

for (uint i = 1; i <= 10; i++) { // body }

应付修饰符

支付功能是使Solidity和以太坊如此酷的部分原因——它们是一种可以接收以太坊的特殊类型的功能。当我们在一个普通的web服务器上调用一个API函数时,我们不能在调用函数的同时发送美元——也不能发送比特币。

但在以太坊中,因为货币(以太坊)、数据(交易有效载荷)和合约代码本身都在以太坊上,所以我们可以同时调用一个函数并向合约支付费用。

这允许一些非常有趣的逻辑,比如为了执行一个函数,需要向合约支付一定的费用。

注意:

在以太坊中,当我们在合约上调用一个函数时,我们将其作为交易广播到网络上的一个或多个节点。节点在网络上收集一些交易,试图成为第一个解决计算密集型数学问题的“工作证明”,然后将这组交易连同他们的工作证明(PoW)发布为一个块到网络的其余部分。

代币

所以基本上,代币只是一个合约,它记录了谁拥有多少代币,以及一些函数,以便这些用户可以将他们的代币转移到其他地址。

assert 与 require 的差异

Assert类似于require,如果为false则抛出错误。assert和require之间的区别是,当一个函数失败时,require会退还用户剩余的gas,而assert不会。

Metamask

这是Chrome和Firefox的浏览器扩展,允许用户安全地管理他们的以太坊账户和私钥,并使用这些账户与使用Web3.js的网站进行交互。

应用程序二进制接口。

在部署的合约之后,它会在以太坊上得到一个固定的地址,在那里它将永远存在。在以太坊网络中部署智能合约后,还会生成一个ABI。基本上,它是以JSON格式表示的合约方法,告诉Web3.js如何以我们的合约能够理解的方式格式化函数调用。

Web3Js

以太坊的JS前端库被称为web3.js。

Source:https://medium.com/coinmonks/learn-all-about-solidity-ethereum-45d709c4de77

聯系郵箱:0xniumao@gmail.com