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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容