Solidity初学

Lesson 1

    《区块链技术进阶与实战》一书里提到了区块链编写的语言——Solidity,于是我决定去看一看。看到了一个对于初学者,包括刚接触编程的人都很友好的一个学习网站:https://cryptozombies.io/。
    接下来我进行一些自己的总结:首先,和c++ java一样,需要引入头文件。Solidity引入头文件的方法就是加上一句pragma solidity ^0.4.19;。Solidity一些地方和python很像,比如x的y次方,都会使用x ** y来表示。但是Solidity的private、public关键字是声明在类型后面的:Zombie[] public zombies;比如这样,声明的就是一个Zombie类型的动态数组。需要注意的是,动态数组使用.push()方法会返回一个值,就是加入的元素的索引。但是下标是从0开始的,所以使用x=array.push(tmp)-1就可以获得刚刚插入的tmp元素的下标。如果是私有类型的方法,在方法名前面还得加上下划线_

function _createZombie(string _name, uint _dna) private {
        zombies.push(Zombie(_name, _dna));
    }

但是如果是public的方法,就不需要加上这个下划线。

最后附加上这个网站游戏的通关代码:

pragma solidity ^0.4.19;

contract ZombieFactory {

    event NewZombie(uint zombieId,string name,uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna))-1;
        NewZombie(id,_name,_dna);
    } 

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

(其实在你每一关出错的时候会出现查看正确答案的按钮)
    但是,到这里还没有结束。下面就是介绍以太坊的Web3.js库了,
下面是使用Web3.js的代码:

// Here's how we would access our contract:
var abi = /* abi generated by the compiler */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* our contract address on Ethereum after deploying */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory` has access to our contract's public functions and events

// some sort of event listener to take the text input:
$("#ourButton").click(function(e) {
  var name = $("#nameInput").val()
  // Call our contract's `createRandomZombie` function:
  ZombieFactory.createRandomZombie(name)
})

// Listen for the `NewZombie` event, and update the UI
var event = ZombieFactory.NewZombie(function(error, result) {
  if (error) return
  generateZombie(result.zombieId, result.name, result.dna)
})

// take the Zombie dna, and update our image
function generateZombie(id, name, dna) {
  let dnaStr = String(dna)
  // pad DNA with leading zeroes if it's less than 16 characters
  while (dnaStr.length < 16)
    dnaStr = "0" + dnaStr

  let zombieDetails = {
    // first 2 digits make up the head. We have 7 possible heads, so % 7
    // to get a number 0 - 6, then add 1 to make it 1 - 7. Then we have 7
    // image files named "head1.png" through "head7.png" we load based on
    // this number:
    headChoice: dnaStr.substring(0, 2) % 7 + 1,
    // 2nd 2 digits make up the eyes, 11 variations:
    eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
    // 6 variations of shirts:
    shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
    // last 6 digits control color. Updated using CSS filter: hue-rotate
    // which has 360 degrees:
    skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
    eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
    clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
    zombieName: name,
    zombieDescription: "A Level 1 CryptoZombie",
  }
  return zombieDetails
}

    这些代码主要是获取刚才的Solidity代码生成的随机基因数,实际效果是你输入一个名字,它会生成名字相应的僵尸基因对应的样子。
以上就是完成该网站第一课的内容。

Lesson 2

    在Solidity中,mapping默认是键值对方式存储数据。下面是mapping的写法:

//key是地址,value是一个uint类型的值(在Solidity中,address是一个类型)
mapping (address => uint) public accountBalance;

msg.sender:一个全局变量,可以被所有函数调用。指的是当前调用者(智能合约)的address。使用msg.sender来更新 mapping的例子:

mapping (address => uint) favoriteNumber;

function setMyNumber(uint _myNumber) public {
  // 更新我们的 `favoriteNumber` 映射来将 `_myNumber`存储在 `msg.sender`名下
  favoriteNumber[msg.sender] = _myNumber;
  // 存储数据至映射的方法和将数据存储在数组相似
}

function whatIsMyNumber() public view returns (uint) {
  // 拿到存储在调用者地址名下的值
  // 若调用者还没调用 setMyNumber, 则值为 `0`
  return favoriteNumber[msg.sender];
}

msg.sender拥有以太坊区块链的安全保障,所以安全性较高。
require:用该方法声明一个function,如果不满足条件就会返回一个error信息。例:

require(ownerZombieCount[msg.sender] == 0);

继承:

contract ZombieFeeding is ZombieFactory {

}
//合约ZombieFeeding 继承了ZombieFactory

在solidity中,你有两个位置能存储数据,一个是storage,一个是memory,相当于电脑的硬盘和RAM,storage是永久存储在区块链中的,而方法外是无法访问到方法内存储在memory中的变量的。
声明方式如下:

Sandwich storage mySandwich = sandwiches[_index];
Sandwich memory anotherSandwich = sandwiches[_index + 1];

Chapter 8 中设置了一个小错误,当你在一个.sol文件中引用另一个.sol文件合约内的private函数的时候,是无法访问到的,这个时候就需要用到internal和external概念了。external和public差不多,但是external是只有外部合约才能调用的,本合约内部无法调用。而internal类似于private,不同的是继承本合约的合约也能访问到。internal和external声明的位置和public,private相同。
当和区块链中其他的非自己拥有的合约互动的时候,需要用到接口——interface的概念。以下是接口的写法:

contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

对,没错,和你之前想的类似java的interface声明方法不同,根本就没有interface关键字!接口的声明和合约类似,不同的是只声明一个方法,而且没有其他状态变量,并且函数没有函数体。这其实就是一个合约的骨架。
下面是一个合约调用NumberInterface 中的getNum函数的例子:

contract MyContract {
  address NumberInterfaceAddress = 0xab38... 
  // ^ The address of the FavoriteNumber contract on Ethereum
  NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
  // Now `numberContract` is pointing to the other contract

  function someFunction() public {
    // Now we can call `getNum` from that contract:
    uint num = numberContract.getNum(msg.sender);
    // ...and do something with `num` here
  }
}

这样,你就能调用任何别人的声明为public或者external的区块中的函数。
Lesson2的新增的zombiefeeding.sol文件内容如下:

pragma solidity ^0.4.19;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);

  // Modify function definition here:
  function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    // Add an if statement here
    if(keccak256(_species) == keccak256("kitty")){
      newDna = newDna - newDna % 100 + 99;
      //replace the last 2 digits of DNA with 99
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    // And modify function call here:
    feedAndMultiply(_zombieId, kittyDna,"kitty");
  }
}

Lesson 3

你可能感兴趣的:(Solidity初学)