함수 제어자 :
private(내부의 다른 함수들에 의해서만 호출)
internal(private + 상속하는 컨트랙트에서도 호출)
external(외부에서만 호출 가능)
public(어디서든)
상태 제어자 :
view
pure
사용자 정의 제어자
onlyOwner
aboveLevel
...
이더를 받을 수 있는 특별한 함수 유형
일반 웹 서버에서 API 함수를 실행할 때 달러를 보낼 수 있냐? NO
이더리움은 돈(이더), 데이터, 코드 모두 이더리움 위에 존재하기 때문에, 함수 실행과 동시에
지불이 가능
contract OnlineStore {
function buySomething() external payable {
// 함수 실행에 0.001이더가 보내졌는지 확실히 하기 위해 확인:
require(msg.value == 0.001 ether);
// 보내졌다면, 함수를 호출한 자에게 디지털 아이템을 전달하기 위한 내용 구성:
transferThing(msg.sender);
}
그리고 프론트엔드에서 이 함수를 실행한다면?
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})
value: web3.utils.toWei(0.001) 이게 msg.value
(payable이 없는데 이더 보내려고 하면 거부됨)
uint levelUpFee = 0.001 ether;
function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level++;
}
누가 컨트랙트로 이더를 보내면, 해당 컨트랙트 이더리움 계좌에 이더가 저장되고 갇힘
이더를 인출하는 함수
contract GetPaid is Ownable {
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
}
transfer 함수를 사용해 특정 주소로 이더를 전달
this.balance는 컨트랙트 내 전체 잔액
초과지불을 했다면, 돌려주는 함수도 만들 수 있지
uint itemFee = 0.001 ether;
msg.sender.transfer(msg.value - itemFee);
만약 구매자 판매자를 이어주는 컨트랙트라면 판매자 주소를 저장해놨다가,
구매자가 사면 그 돈을 판매자에게 전달할 수도
seller.transfer(msg.value).
function withdraw external onlyOwner {
owner.transfer(this.balance);
};
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
좀비 전투를 위한 기능을 추가하기 때문에 따로 파일을 파자
pragma solidity ^0.4.19;
import "./zombiehelper.sol";
contract ZombieBattle is ZombieHelper {
}
모든 게임은 일정 수준의 무작위성을 필요로 하는데, 솔리디티에서는 난수를 어떻게 발생시킬까?
적어도 안전하지는 못한다.
// Generate a random number between 1 and 100:
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
이렇게 현재 시간, 증가하는 랜덤 논스를 사용해 꽤나 안전하고 랜덤해 보이는데...
만약 동전 던지기로 앞-뒤 맞추는 함수가 있다면, 그리고 내가 노드라면,
못 맞추면 트랜잭션을 공유를 안하고, 맞추면 공유하는 거지
오라클을 사용해 블록체인 외부의 난수 함수에 접근할 수 있긴 하다
오라클 : 블록체인 외부에서 데이터를 받아오는 안전한 방법 중 하나
우린 공부하니까 너무 쿠사리 주진 말자
uint randNonce = 0;
function randMod(uint _modulus) internal returns(uint) {
randNonce++;
return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
}
좀비 전투 방식
내가 내 좀비 중 하나 고르고, 상대방 좀비를 공격 대상으로 선정
공격하는 쪽이 이길 확률 70%
모든 좀비들은 전투 결과에 따라 증가하는 winCount, lossCount 가짐
공격하는 놈이 이기면 레벨 오르고 새로운 좀비 생김
지면 lossCount만 올라가고 아무일 없음
공격한 놈 재사용 대기시간 활성화
uint attackVictoryProbability = 70;
function attack(uint _zombieId, uint _targetId) external {
}
공격을 할 때, 남의 좀비를 쓰면 안되니 주인인지 확인해야 겟지
맨날 쓰잖아 require(msg.sender == zombieToOwner[_zombieId];
계속 쓰는 거면 아싸리 modifier로 만들자
modifier ownerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
};
//...
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) {
// 1. 이 함수를 `ownerOf`를 사용하도록 변경하게:
function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) {
zombies[_zombieId].name = _newName;
}
// 2. 이 함수에도 똑같이 적용하게:
function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) {
zombies[_zombieId].dna = _newDna;
}
function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
Zombie storage myZombie = zombies[_zombieId];
Zombie storage enemyZombie = zombies[_targetId];
uint rand = randMod(100);
}
좀비 순위표도 만들거여
struct 수정 해야것지?
그리고 좀비 생성할 때도 수정해줘야 것지?
struct Zombie {
string name;
uint dna;
uint32 level;
uint32 readyTime;
uint16 winCount;
uint16 lossCount;
}
function _createZombie(string _name, uint _dna) internal {
// 2. 여기서 새로운 좀비의 생성을 수정하게:
uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime),0,0)) - 1;
난수가 0~99이고 공격자 이길 확률을 70으로 세팅해뒀다. 어찌해야 내가 이길 확률이 70%가 될 것이며, 이기면 무엇을 해야 할까?
if(rand <= attackVictoryProbability) {
myZombie.winCount++;
myZombie.level++;
enemyZombie.lossCount++;
feedAndMultiply(_zombieId, _targetId, "zombie");
}
내가 지면 카운팅을 반대로 해주면 될 것이고,
이기고 지던 간에 쿨타임을 돌려야지
else {
myZombie.lossCount++;
enemyZombie.winCount++;
}
_triggerCooldown(myZombie);
}