728x90

미콘캐시 

온라인 및 모바일 서비스상의 결제, 보상 및 거래에 사용되는 암호화폐 플랫폼”을 제공하는 것을 목표로 합니다.

미콘캐시는 플랫폼에 연동된 서비스에서 얻은 보상을 자산의 가치로 만들어 드립니다. 사용자는 서비스가 종료되더라도 미콘캐시로 새로운 서비스에 대한 요구를 충족할 수 있으며, 개인간 거래를 할 수 있으며, 실물경제에서 사용할 수 있는 시스템으로 순환됩니다

-> 아하 얘네도 자기네 플랫폼을 살리고 그 외로도 쓰일 코인을 만든거구만, 그게 미콘캐시

 

미콘캐시(MCH)는 온라인 및 모바일 서비스 상의 결제, 보상 거래에 사용되는 자체 암호화폐 플랫폼이다.
전국 1만3700여 곳의 ATM기를 통해 출금이 가능하다는 점이 특징이다.

 

미콘캐시의 간편 결제 서비스 'M.PAY'는 비트코인, 이더리움, 트론, 비트코인캐시, 라이트코인 등 주요 코인을 시세에 맞는 M.PAY로 전환해서 현금처럼 사용할 수 있도록 지원한다.

 

Q. 온라인 결제와 관련된 암호화폐가 다양하게 있다. 미콘캐시만의 특징은?
A. 미콘캐시는 앱 지갑 내의 M.PAY 결제 서비스를 통해 온라인 결제 및 보상이 이루어진다. 미콘캐시뿐만 아니라 다양한 가상자산들이 M.PAY로 전환되어 사용될 수 있다. M.PAY는 MCH와 1:1로 교환되며 사용시 시세를 반영하여 결제가 이루어진다. 그밖에 암호화폐는 M.PAY 전환시 해당 암호화폐와 MCH 시세를 반영하여 전환된다.

https://www.hankyung.com/it/article/202202092121v

 

[인터뷰] 조재도 회장 "미콘캐시, 포스트코로나 시대 맞춰 '영역 확장'"

[인터뷰] 조재도 회장 "미콘캐시, 포스트코로나 시대 맞춰 '영역 확장'", 산업

www.hankyung.com

 

728x90
728x90

 

이거 따라할 때 생겼던 에러와 해결내용

https://medium.com/pinata/how-to-build-erc-721-nfts-with-ipfs-e76a21d8f914

 

How to Build ERC-721 NFTs with IPFS

Using Open Zeppelin, Truffle, and Pinata

medium.com

 

1. ERC721

오픈제플린 최신버전은 ERC721URIStorage 가 ERC721를 대체한다. 이름 바꿔줘야함.

못가져옴 사용이 안되어서 에러 뜸

,DeclarationError: Undeclared identifier.
  --> project:/contracts/UniqueAsset.sol:29:3:
   |
29 |   _setTokenURI(newItemId, metadata);

 

//기존
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

//ERC721.sol가 없고 이제 ERC721URIStorage.sol이거 씀
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";

 

 

 

근데 나는 여전히 계속 빨간줄 뜨더라고 그래서 

../node_modules/

로 경로 찾아주니까 빨간줄 사라짐

 

2. DeclarationError

사실 무엇보다

변수들을 못찾았는데

 

DeclarationError: Undeclared identifier. Did you mean "hash"?
  --> project:/contracts/UniqueAsset.sol:24:11:
   |
24 |   require(hashes[hash] != 1);
   |           ^^^^^^

,DeclarationError: Undeclared identifier. Did you mean "hash"?
  --> project:/contracts/UniqueAsset.sol:25:3:
   |
25 |   hashes[hash] = 1;
   |   ^^^^^^

그이유는!!!!

 

괄호를 잘못 묶었더라고..ㅋㅋㅋㅋ...

contract 안에 함수 있는건데 

contract랑 함수를 따로 해서 contract에 담아둔 애들이름 못 쓰는 거였다

 

 

 

결과 컴파일 잘된다!

 

3. 경고 Visibility for constructor is ignored.

 

경고 뜨는 건

 

  Warning: Visibility for constructor is ignored. If you want the contract to be non-deployable, 
making it "abstract" is sufficient.

(경고: 생성자의 가시성은 무시됩니다. 계약을 배포할 수 없도록 하려면 "추상"으로 만드는 것으로 충분합니다.)
  --> project:/contracts/UniqueAsset.sol:16:4:
   |
16 |    constructor() public ERC721("UniqueAsset", "UNA") {}
   |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

 

 

생성자에 더이상 가시성(public/ external)이 필요하지 않다고 한다.

왜냐면 생성자는 처음에 배포됐을 때만 읽히니까 다음에 또 불려질 일이 없다.

 

public을 지웠더니 경고가 사라졌다.

 

 

 

 

 

추가적으로는

솔리디티 버전을 코드랑 잘 맞춰줘야하는 것. 필요할 때 f1눌러서 버전수정했다.

 

 

 

 

따라한 내용 정리

필요한 것

  • vscode(nodejs, 솔리디티확장자)
  • IPFS 설치
  • Ganache — 이더리움의 로컬 블록체인 — 설치
  • 트러플 설치
  • NodeJS 설치
  • 피나타 API 키

스마트 계약 작성

 

(오픈제플린에서 가져와서 쓸거임)

 

새프로젝트 폴더 만듦

mkdir mySpecialAsset

디랙토리 초기화

npm init -y

 

Truffle을 활용해 스마트 계약 프로젝트 초기화

truffle init

Open Zeppelin 계약에 액세스할거라서 설치

npm install @openzeppelin/contracts

 

contracts 폴더 구조 안에 파일 생성! (솔리디티 버전 맞춰서 하기)

UniqueAsset.sol 

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

import "../node_modules/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "../node_modules/@openzeppelin/contracts/utils/Counters.sol";

contract UniqueAsset is ERC721URIStorage{
    //Counters to help us increment the identifiers for the tokens we mint.
    // 카운터 라이브러리(갯수세는역할함)를 쓴다. 이 유형으로
  using Counters for Counters.Counter;
    //변수만듦to keep track of all of the tokens we’ve issued.
  Counters.Counter private _tokenIds;
    //mapping for the IPFS hashes associated with tokens. 
  mapping(string => uint8) hashes;

   constructor() ERC721("UniqueAsset", "UNA") {}


// 특정IPFS해시에 아직 mint안되어있으면 mint하는 함수
// a method to our contract that will allow us to mint an NFT for a specific IPFS hash if not token has been minted yet for that hash
                           //nft수령인//NFT를 생성할 콘텐츠와 관련된 IPFS 해시 //자산에 대한 JSON 메타데이터에 대한 링크를 참조해야 한다
function awardItem(address recipient, string memory hash, string memory metadata) public returns (uint256)
{

  require(hashes[hash] != 1);
  hashes[hash] = 1;
  _tokenIds.increment();
  uint256 newItemId = _tokenIds.current();
  _mint(recipient, newItemId);
  _setTokenURI(newItemId, metadata);
  return newItemId;
}
}

폴더구조 migrations 들어가서 

2_deploy_contract.js

파일 생성

var UniqueAsset = artifacts.require("UniqueAsset");
module.exports = function (deployer) {
  deployer.deploy(UniqueAsset);
};

 

 

다 하면 컴파일

truffle compile

 

만약 오류가 발생하면 Ganache가 실행 중인 포트를 수동으로 설정 (아니면 나처럼 버전, 모듈경로 등의 문제일수도 있음 위에 참조)

truffle-config.js

(가나슈 퀵스타 누르면 뜨는 창에있는거랑 동일하게)

 

 

truffle migrate

이걸로 

 NFT 스마트 계약을 배포

 

 

 

이제 스마트 계약을 처리했으므로 기본 자산을 IPFS로 가져와 관련 NFT를 발행할 때 사용할 수 있는지 확인

 

 

IPFS에 자산 추가

우리는 Pinata를 사용하여 자산을 IPFS에 추가하고 고정된 상태로 유지하도록 할 것입니다.

https://app.pinata.cloud/pinmanager

 

uploadFile.js

업로드할 파일 준비

 

작업을

npm i axios form-data

쉽게 하려면 두 가지 종속성이 필요하다

 

이 스크립트에

const pinataApiKey = "YOURAPIKEY";
const pinataSecretApiKey = "YOURSECRETKEY";
const axios = require("axios");
const fs = require("fs");
const FormData = require("form-data");
const pinFileToIPFS = async () => {
  const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;=
  let data = new FormData();
  data.append("file", fs.createReadStream("./pathtoyourfile.png"));
  const res = await axios.post(url, data, {
    maxContentLength: "Infinity", 
    headers: {
      "Content-Type": `multipart/form-data; boundary=${data._boundary}`
      pinata_api_key: pinataApiKey, 
      pinata_secret_api_key: pinataSecretApiKey,
    },
  });
  console.log(res.data);
};
pinFileToIPFS();

 

코드 넣기 

Pinata에서 로그인하고 새 키 만들면 뜨는

"YOURAPIKEY"와 비밀키"YOURSECRETKEY"를 넣기 

 

 

node uploadFile.js

성공적으로 됐으면 이렇게 뜸

 

 

 

해당 해시는 자산의 검증 가능한 표현이고 IPFS 네트워크의 자산을 의미한다. 누군가가 자산을 변조하고 변경한 경우 해시가 달라진다. 이 해시는 우리의 스마트 계약을 통해 NFT를 발행할 때 사용해야 하는 것이다. 공개 게이트웨이를 제공하는 모든 IPFS 호스트는 이제 콘텐츠를 표시할 수 있다.. 라고 위 링크 글에 있다.

 

이게 Pinata에 자산을 업로드 하는 거라는데 pinata 내 계정에는 뭔가 달라진게 안보인다.

게이트웨이를 통해서 봐야돼서 그런건가 흠...

 

 

이상

뒤에 json 파일 만드는 것 이전까지 해봤다.

 

 

 

 

 

https://stackoverflow.com/questions/68608281/file-import-callback-not-supported-on-truffle-migrate

 

File import callback not supported on truffle migrate

I have a truffle project with the following contract (elided) that I'm running with truffle migrate: 1 pragma solidity >=0.6.0; 2 3 // implements the ERC721 standard 4 import &qu...

stackoverflow.com

 

 

 

https://forum.openzeppelin.com/t/function-settokenuri-in-erc721-is-gone-with-pragma-0-8-0/5978/21?page=2 

 

Function _setTokenURI() in ERC721 is gone with pragma ^0.8.0

I have the same error, now it’s OK: // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract HarddiskCafe is ERC72

forum.openzeppelin.com

 

728x90
728x90

Exiting: Review successful transactions manually by checking the transaction hashes above on Etherscan.

Error:  *** Deployment Failed ***
"Migrations" -- Returned error: ENOENT: no such file or directory

 

아까는 배포가 되었다가 안됐던 이유가

가나슈 네트워크 연결한 7545에서 테스트넷(Ropsten테스트 네트워크로 하면된다길래..) 해보겠다고 다른거로 바꿔놔서 그랬다.

network7545내가 만든네트워크임

 

배포명령어가 좀 다르긴한대 비슷비슷하네 명령어 정리 해보자

truffle migrate --network development

아까는 명령어 문제인가 싶어서 위에 껄로 하긴 했는데 이건상관없는 거였음( truffle migrate 평소대로 이거해도같음)

 

 

명령어

//truffle프로젝트 초기화
truffle init

//각 네트워크에 배포된 컨트랙트의 주소표시
truffle networks

//컨트랙트 배포
truffle deploy

//개발모드로 접속(ganache-cli실행)
truffle develop

//모든 명령목록, 특정명령에 대한 정보를 표시
truffle help

배포

 truffle migrate

재배포시 

 truffle migrate --reset

 

 

(truffle deploy is an alias for truffle migrate. They both do the same thing.

별칭같은 거래 둘이 같대.)

728x90
728x90

truffle migrate시에 

payable 부분에서 오류가 나서 참고를 하려고 했지만

 

팀원한테 물어보니 payable이 truffle버전 5이상부터 된다는 얘기를 해서 (하지만 관련문서를 찾아봐도 잘안나온다 솔리디티 버전에 관한 얘기는 있는데 "address payable not supported in solc 0.4.x or earlier." 나는 solc버전이 0.8이었어서...)

npm uninstall -g truffle 하고

npm i g truffle@5.5.0

로 다시 까니까 됐다.

 

 

 

 

 

 

(packagelock json이 이런거 통일시켜주는 애려나? 근데 그거 있으면 npm i 모듈 깔때 에러 떠서 삭제하고 하는데 흠...)

 

 

728x90
728x90

설명출처: https://www.youtube.com/watch?v=NnZ0KOy2yJY&list=PLJQKWHLhBrxI43w0DU4uQrhWv4Pm1OFlx&index=37 

 

특징

1. 송금하기

2. 외부 스마트컨트랙 함수 부르기

3. 가변적인 gas

4. 이스탄불 하드포크, call 사용권장 가스가격 상승해서

5. re-entrancy 재진입

 

call vs Delegate call

 

 

Delegate call:

1. msg.sender가 본래의 스마트 컨트랙 사용자를 나타낸다.

2. delegate call이 정의된 스마트 컨트랙(즉 caller)이 외부 컨트랙의 함수들dmf 마치 자신의 것처럼 사용하고

(실질적인 값도 caller에 저장된다)

 

조건 

외부 스마트컨트랙과 caller 스마트컨트랙은 같은 변수를 갖고 있어야한다

 

 

 

 

 

 

블록체인 특성상 이미 배포된 코드를 수정할 수 없다.

Delegate Call를 사용하면 B코드에 적힌 코드로 적립된 포인트가 A에 저장된다.

그래서 스마트컨트랙B 기존꺼를 끊고 새로운 스마트컨트랙B2를 이어준다.

 

 

728x90
728x90

payable 이더를 받을때나 보낼때 쓰는 키워드

 

 

 

payable을 생성자에 넣을 때

 

 

생성자에 페이어블 넣으면 배포했을 때 생성자함수도 구현돼서 이더를 보낼수 있대

 

저 이더에 5넣고 배포 누르니까 빠져나감

 

 

 

특정계정 권한주기

 

 

modifier로 함수들에 조건 추가

 

 

 

 

강의: 인프런 솔리디티 깨부수기

728x90
728x90

주소.balance    주소의 현재 갖고 있는 이더의 잔액

( msg.value       송금액을 의미한다 달라! )

msg.sender      스마트컨트랙을 사용하는 주체, 보내는 자

주고 받는거 해봄

 

 

이더보내는 스마트컨트랙트 코드  (출처: https://github.com/D-One0914/BreakingSolidityBasic/blob/main/lec33.sol

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.5.0 < 0.9.0;

//주소.balance
//주소의 현재 갖고 있는 이더의 잔액을 의미함.  //  msg.value는 송금액을 의미한다 달라!

// msg.sender
// msg.sender는 스마트컨트랙을 사용하는 주체라고 볼 수 있다 
// 앞으로 설명할 call 과 delegate call에서 주요 내용이니 관심잇게 봐라

contract MobileBanking{
    event SendInfo(address _msgSender, uint256 _currentValue);
    event MyCurrentValue(address _msgSender,uint256 _value);
    event CurrentValueOfSomeone(address _msgSender, address _to, uint256 _value);


   //이더를 보낼라면 
   //을 넣을 줄 알았지만!!! 보내는 주소, 받을 사람 주소만 있음 되는구나.. 이더를 받아야되기때문에 payable써준다 //함수전체도 이더보는거라 payable써줌
    function sendEther(address payable _to) public payable {
        require (msg.sender.balance >= msg.value , "your balance is not enough");
        //트랜스퍼 함수를 통해 이더 송금
        _to.transfer(msg.value);
        //보낸 나의 주소, 내 잔고
         emit SendInfo(msg.sender, (msg.sender).balance);
    }
    //그냥 현재 잔고확인 함수인듯?
    function checkValueNow() public{
        emit MyCurrentValue(msg.sender, (msg.sender).balance);
    }
    //어떤 계정의 잔고 확인하고 싶을때, 확인하고 싶은 주소 넣기
    function checkUserValue(address _to) public{
       emit CurrentValueOfSomeone(msg.sender, _to, _to.balance);
    }

}

 

 

이더송금하기 

sendEther함수 사용

 

1. 코드를 컴파일한다

또는 우클릭해서 원하는 코드 컴파일.

 

2. ACCOUNT 에서 현재 내 계정주소 확인하고  Deploy 배포한다.

 

1로 표시한거 &gt;&nbsp; 계정주소 넣을 부분 2로 표시한거 &gt; 이더금액 넣는 부분 3로 표시한거 &gt;&nbsp; Deploy 배포 4로 표시한거 &gt; 함수사용하는 부분 5로 표시한거 &gt;&nbsp; 내역&nbsp; &nbsp; &nbsp; &nbsp;(숫자 이거 순서 아님주의!!!)

3. 함수 실행

 

Deploy했으면

Deployed Contracts 가 생성될거다.

함수에 값넣어서 이더송금할 수 있다.

 

sendEther는 송금받을 주소를 넣는 곳이다.

 

Account에서 보낼 주소를 골라서 복사해서 sendEther에 넣는다.

그리고 Account에서 다시 내 계정주소로 와있는지 확인한 다음에

보낼 이더 값을 Value에 넣는다. Ether로 되어있는지 확인한다.

그리고 sendEther를 눌러 송금함수를 실행한다. 

 

잘보내졌으면 저렇게 내역이 뜬다.

ACCOUNT 눌러서 남은 잔고와 받은 잔고를 맨위에 사진처럼 확인할 수 있다. 

 

 

 

 

현재계정 잔고확인하기

checkValueNow함수 사용

 

 

 

 

 

 

특정계정의 잔고 확인하기 

checkUserValue함수

에 _to 확인할 계정주소 넣고

원래 보낼 주소로 되어있는지 ACCOUNT확인하고

 

함수 실행하면

_msgSender 주체

_to 확인한 계정

_value 잔고

 

 

 

 

 

728x90
728x90

솔리디티 (이더리움 등 블록체인 플랫폼에서 스마트 계약 작성과 구현에 사용되는 계약 지향 프로그래밍 언어)

쓸 때 여기서 바로 할 수 있긴한데

https://remix.ethereum.org/

 

Remix - Ethereum IDE

 

remix.ethereum.org

 

vscode 비주얼 스튜디오에서 작업하고 싶으니까 작업 환경을 설정해보자

 

먼저 당연히 깔려있어야할

 

비주얼스튜디오 설치

Visual Studio Code

Node.js 설치

Node.js

NPM설치

npm   (Node Packaged Manager를 전역에 설치. npm install -g npm)

 

트러플 설치!!

npm install -g truffle

 

 

트러플은 이더리움 프레임워크로 소스코드를 쉽게 compile,deploy 작업을 할 수있게 해줌

 

설치했으면 버전확인하고

 

이제

비주얼 스튜디오에 Solidity 솔리디티 확장자를 설치할거임!

vscode 열고 Ctrl + Shift + X 누르거나 여기 누르면 솔리디티 검색해서 깔면 됨!

 

(파이썬도 깔아줘야한대서 깔긴했는데 왜까는지 모르겠음..내가 본 문서에서는 얘기 없어서)

 

 

암튼 이제 됐고

truffle init

 

하면 

이렇게 생겨남

이제 내가 작업할 솔리디티 파일을 

constracts에 넣으면 됨 

클립토 좀비에서 레슨1의 완성된 코드 하나 가져와서 만듦

코드 복붙해보면 

 

지금은 안뜨는데 아까는 

이렇게 버전이 안맞다고 떴었음

여기 들어가서 Set compiler version을 0.4.19로 맞춰줘.

 

근데 난 이게 안됐음

알고보니까 솔리디티 확장자가 최신버전이더라고 

거기서 버전을 맞춰줘야됐었음

톱니바퀴 눌러서 환경설정 들어가면

 

여기서 버전 설정해주면 빨간줄 사라짐!!

 

환경설정 끝~~

 

 

 

이제 버전 문제 해결됐으면

 

truffle develop

디벨롭하고

run & deploy 누르고 

Activate하기 

 

 

 

 

그럼 이거 켜짐 

연결안되어있으면 connect

그리고 compile 누르면

 

여기서부터는 잘모르겠음..

deploy 누르고 

call해보고 했는데 뭘하려고 하는지 이해못함

 

 

 

728x90
728x90

https://cryptozombies.io/ko/lesson

 

#1 Solidity Tutorial & Ethereum Blockchain Programming Course | CryptoZombies

CryptoZombies is The Most Popular, Interactive Solidity Tutorial That Will Help You Learn Blockchain Programming on Ethereum by Building Your Own Fun Game with Zombies — Master Blockchain Development with Web3, Infura, Metamask & Ethereum Smart Contracts

cryptozombies.io

내가 볼라고 적는거임

다들 클립토좀비 들어가서 공부해

 

레슨 1의

챕터 2: 컨트랙트

 

솔리디티 코드는 컨트랙트안에 싸여있음

 

솔리디티 소스 코드는 

버전선언으로 시작해야함!

 

pragma solidity ^0.4.19; //1. 여기에 솔리디티 버전 적기

//2. 여기에 컨트랙트 생성
contract ZombieFactory{
    
}

 

컨트랙트를 위한 뼈대 갖추기 완성!

 

챕터 3: 상태 변수 & 정수

상태변수는 컨트랙트 저장소에 영구적으로 저장돼!

즉 이더리움 블록체인에 기록된다는 것.

 

contract Example {
//이 변수가 블록체인에 영구적으로 저장됨
uint myUnsignedUnteger = 100;
}

 

챕터 4: 수학 연산

  • 덧셈: x + y
  • 뺄셈: x - y,
  • 곱셈: x * y
  • 나눗셈: x / y
  • 모듈로 / 나머지: x % y (이를테면, 13 % 5는 3이다. 왜냐면 13을 5로 나누면 나머지가 3이기 때문이다)

솔리디티는 지수 연산도 지원하지 (즉, "x의 y승", x^y이지):

uint x = 5 ** 2; // 즉, 5^2 = 25

챕터 5: 구조체

복잡한 자료형을 필요로 할 때 솔리디티로는 구조체를 쓰면 됨

struct Person {
uint age;
string name;
}

구조체를 통해 여러 특성가진 보다 복잡한 자료형을 생성할 수 있지.

 

 

컨트랙트 안에 구조체 있는 모습

pragma solidity ^0.4.19;

contract ZombieFactory {

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

    struct Zombie {
        string name;
        uint dna;
    }

}

챕터 6: 배열

어떤 것의 모음집이 필요할 때 

배열을 사용할 수 있지

 

솔리디티에도 정적배열, 동적배열  두 종류배열 있어. 

//2개의 원소를 담을 수 있는 고정길이의 배열;
uint[2] fixedArray;

//고정길이의 배열, 5개의 스트링을 담을 수 있다.
string[5] stringArray;

//동적배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다.
uint[] dynamicArray;

 

구조체의 배열을 생성할 수 있다!

아까 우리가 sturct Person {uint age; string name;} 이렇게 만들었던 구조체를 이용하면 

Person[] people; // 이는 동적 배열로, 원소를 계속 추가할 수 있다.

상태변수가 블록체인에 영구적으로 저장할 수 있는 것처럼 구조체의 동적배열을 생성하면 

마치 데이터베이스처럼 컨트랙트에 구조화된 데이터를 저장하는데 유용하다. 

 

Public 배열

public으로 배열을 선언할 수 있음 솔리디티는 이런 배열을 위해 getter메소드를 자동적으로 생성한다. 

Person[] public people;

다른 컨트랙트들은 이 배열을 읽을 수는 있게되지만 쓸 수는 없다.

이는 컨트랙트에 공개데이터를 저장할 때 유용한 패턴이다.

 

 

 

 

지금까지 한 코드

pragma solidity ^0.4.19;

contract ZombieFactory {

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

    struct Zombie {
        string name;
        uint dna;
    }

    // 좀비들을 다른 앱에 자랑하고 싶어서 좀비 군대 저장소를public으로 함.
    //Zombie구조체의 public배열을 생성하고 이름을 zombies로 한다.


    Zombie[] public zombies;

}

 

챕터 7: 함수 선언

솔리디티에서 함수 선언

function eatHam(string _name, uint _amount){
}

(함수의 인자명을 _로 시작하는 건 전역변수랑 구별하려고 하는 관례때문임~)

 

이 함수를 오출 할때는 이렇게 할 수 있겠지

eatHam("jun",100);

챕터 8: 구조체와 배열 활용하기

새로운 구조체를 생성하기 

 

전에 만들어 놓은 Person구조체를 사용할거야.

//구조체 Person 틀
struct Person {
  uint age;
  string name;
}
//Person배열을 peole로 이름지음
Person[] public people;

그 틀로 새로운 Person satoshi을 찍어낼거야. 

//Person틀쓸거고 변수명은 satoshi임  // age, name
Person satoshi = Person(172,"Satoshi");
//people배열에 이 사람을 추가한다.
peole.push(satoshi);

이 두 줄을 한 줄로 표현하면 이거임

people.push(Person(16,"Jun"));

 

 

챕터 9: Private / Public 함수

솔리디티에서 함수는 기본적으로 public으로 선언된다. 

누구나 또는 다른 어느 컨트랙트가 우리의 컨트랙트의 함수를 호출하고 코드를 실행할 수 있다는 의미다.

하지만 이건 공격에 취약해질 수 있는 가능성이 있어서 

기본적으로 함수는 private로 선언하고 공개할 함수만 public으로 선언하는 게 좋다

 

private함수를 선언하는 방법

unit[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}

private는 컨트랙트 내의 다른 함수들만이 이 함수를 호출해서 numbers 배열로 무언가를 추가할 수 있다걸 의미.

함수 인자명과 마찬가지로 private함수명도 언더바_로 시작하는 것이 관례임.

 

 

챕터 10: 함수 더 알아보기

함수의 반환값, 함수의 제어자

 

반환값

함수에서 어떤 값을 반환받으려면 다음과같이 선언해야함.

 

string greeting = "what's up dog";
function sayHello() public returns (string) {
//솔리디티에서 함수 선언은 반환값 종류를 포함한다. 지금은 string이야. 
return greeting;
}

함수제어자

위에 함수는 솔리디티에서 어떤값을 변경하거나 무언가를 쓰는등 상태를 변화시키지 않는다. 

이렇게 함수가 데이터를 보기만 하고 변경하지 않는 건 view함수로 선언한다.

function sayHello() public view returns (string) {
}

 

728x90
728x90

https://github.com/jsoyun/naivecoin-chapter6.git

 

GitHub - jsoyun/naivecoin-chapter6

Contribute to jsoyun/naivecoin-chapter6 development by creating an account on GitHub.

github.com

순서는 이렇게 됨

1. 채굴을 해서 coinbase를 만들어 보상금 50받아 (잔고balance에 50쌓이는 걸 볼 수 있음)

2. 보낼 금액 양  /  보낼 지갑 주소 

적어서 sendTx 트랜잭션풀에 보낸다.

3. 채굴 누르면 트랜잭션풀에 있던 애들을 블록으로 넣어줌 채굴됨 (이때도 채굴한 사람은 보상금 50받을거임)

4. 난이도 올라가는 걸 보려면  (코드에서 넣어놓은 이전에 만들어진 블록과 최신블록 차이많이나면 안되는 제한 걸어놔서 서버 다시 돌리고 하는 게 좋음)

10이상 채굴했을 때 난이도 올라가고 넌스도 올라가는 걸 볼 수 있음

 

노드를 하나만 해서 보내는 테스트할 때는

공개키 지갑주소 04~ 에 숫자만 바꿔서 해봤음.

 

내 지갑 주소고.

 

 

현재 잔고는 0

거래하게 코인베이스 채굴할게

처음 제네시스 블록, 그리고 그다음에 코인베이스 50담겨서 들어옴.

잔고 눌렀을 때도 보임

 

 

 

이제 보낼 금액이랑 보낼 주소 해서 

트랜잭션 풀에 넣어보겠음 

트랜잭션 풀을 보면(얘는 시각화안함...json형태로 띄워져있음) 잘들어온게 보임

그럼 이제 채굴을 눌러서 block에 넣고 화면에 띄우면

트랜잭션이 블록에 들어간걸 볼 수 있음

 

 

 

그리고 잔고를 확인했을 때도 채굴을 한번더 해서 보상받아서 100으로 늘어난걸 볼 수 있음

 

 

사실 지금까지는 보내는 주소를 그냥 자기 자신한테 해서 효과없는거라 채굴해서 보상받은 금액만 올라갔는데

주소를 해시값 갯수 지키고 앞에 04 지킨 상태에서 숫자만 하나 바꿔서 보내봄

 

99를 다른 노드에 보내고 

채굴해서 

잔돈 1에 50 생겨서 51됨

 

 

 

728x90
728x90

 


 


 

728x90
728x90

값이 undefined라서 찾을 수 없을 때 많이 뜸

값이 타입이 달라서 가져올 수 없다거나.

배열이라서 가져올라면 map함수를 써야한다거나

[]를 묶어줘야하거나 빼줘야하거나. 등의 이유..

 

 

 

 

나는 기존의 server갈아엎고 naivecoin6의 서버 코드를 쓰고 있어서 

block header없고 block만 있어서 a.header.index가 인식 못하는 것!

 

 

 

이렇게 해서 블록의 정보들은 뜨게 되었는데

 

 

트랜잭션은 안보임

 

 

 

블록체인 구조를 보면 

설명해준 학우분 짱.

이렇게 생겼단 말임

그래서 data부터는 배열로 안에 들어간 값이라 보여줄라면 map함수를 써야함!

채굴된 Block으로 보면 이러함.

 

 

 

 

 

해결한 코드

  {chainBlocks &&
        chainBlocks.map((a) => (
          <div style={marginBottom} key={a.index}>
            {/* <div>바디 : {a.body}</div> */}
            {a.data.map((b) => (
              <>
                <div>txId : {b.id}</div>

                {b.txIns.map((c) => (
                  <>
                    <div>signature : {c.signature}</div>
                    <div>txOutId: {c.txOutId}</div>
                    <div>txOutIndex: {c.txOutIndex}</div>
                  </>
                ))}
                {b.txOuts.map((c) => (
                  <>
                    <div>address : {c.address}</div>
                    <div>amount: {c.amount}</div>
                  </>
                ))}
              </>
            ))}

            <div>인덱스 : {a.index}</div>
            <div>넌스 : {a.nonce}</div>
            {/* <div>버전 : {a.version}</div> */}
            <div>시간 : {a.timestamp}</div>
            <div>난이도 : {a.difficulty}</div>
            {/* <div>머클 루트 : {a.merkleRoot}</div> */}
            <div>이전 해쉬 : {a.previousHash}</div>
          </div>
        ))}

728x90
728x90

cors에러 경우

CORS(Cross-Origin Resource Sharing)

자기가 속하지 않은 포트로 정보 보내려고 할 때 주소가 달라서 보안상 안된다고 하는것

 

얘는 별도처리 없이
모두에게 허용하는 방법임!!

const cors = require("cors");

하고 

구동되는 함수에 

app.use(cors());

이렇게 하면 해결된다. 

 

 

 

 

특정 도메인에만 허용하고 싶으면 변수에 특정 도메인 주소만 넣으면 됨.

let corsOptions = {
    origin: 'https://www.domain.com',
    credentials: true
}

app.use(cors(corsOptions));

 

 

 

 

 

 


 

특정 도메인을 허용할 때 사용!

Access-Control-Allow-Origin response 헤더를 추가하는 방법도 있음.

app.get('/', (req,res) => {
  res.header("Access-Control-Allow-Origin", "*");
  ...
}

 

미들웨어 서버 이용하기
proxy


proxy를 쓰는 경우 밑에 유튜브 영상처럼

/api 설정을 해줘야함.!

 

client에 

setupProxy.js

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function (app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:3001',
      changeOrigin: true
    })
  );
};

서버

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

출처:

https://surprisecomputer.tistory.com/32

 

[Node.js] express cors 사용하기

1. 서론 리버스 프록시 서버로 NGINX를 두고 한 워크스테이션에서 Swagger와 node.js 서버를 함께 구동한 적이 있다. Swagger의 포트를 8085로 지정하고 node.js 서버는 443번으로 지정했는 데, CORS 에러가 발

surprisecomputer.tistory.com

https://firework-ham.tistory.com/70

 

 

 

https://www.youtube.com/watch?v=BHSJw8PDwj0&list=PL9a7QRYt5fqkZC9jc7jntD1WuAogjo_9T&index=22 

 

728x90
728x90

스마트컨트랙트

 

 

객체지향언어의 class와 비슷하다

 

멤버 필드하나

생성자 하나

단일 클래스 만들어서 샐행하고 사용해보자

 

 

코드자동 컴파일

 

 

 

 

pragma solidity ^0.4.0;
contract SimpleCoin {
    //멤버 필드
    //public int num
    //map 변수 타입
    mapping (address => uint256) public coinBalance ;
    //mapping(string => addresss) public tenDatabase;
   // 생성자

    constructor() public {
        coinBalance
        [msg.sender] = 10000; //멤버필드 초기화
    }

    //메서드
    //해당주소에 얼마만큼 보내겟다
    function transfer(address _to, uint256 _amount) public {
        coinBalance[msg.sender] -= _amount;
        coinBalance[_to] += _amount;
    }
}

 

 


좀비 클립토에 있던 내용

솔리디티는 지수 연산도 지원하지 (즉, "x의 y승", x^y이지):

uint x = 5 ** 2; // 즉, 5^2 = 25

 

 

 

728x90
728x90

server에서 트랜잭션 풀에 보내주는 코드 

 app.post("/api/sendTransaction", (req, res) => {
    try {
      //객체로 받음 ().뭐시기)
      const address = req.body.address;
      const amount = parseInt(req.body.amount);
      if (address === undefined || amount === undefined) {
        throw Error("invalid address or amount");
      }
      const resp = blockchain_1.sendTransaction(address, amount);
      res.send(resp);
    } catch (e) {
      console.log(e.message);
      res.status(400).send(e.message);
    }
  });

req.body.amout가 

숫자가 아니라 문자여서 

숫자로 반환하려고 parseInt(req.body.amount)

 

 

client

AppWeeklySales.js에서 

 const sendTx = async () => {
    const data = blockData;
    const user = addressData;
    await axios.post(`/api/sendTransaction`, { amount: data, address: user });
  };

보낼주소 ,  보낼값 (amount랑 address에 보낼 것)

 

보낼값이  {[]} 이렇게 배열로 묶어줬었는데

여기서는 객체로 보내줄라고 []배열표기를 뺀거임. 

 

 

728x90
728x90

팀플때한거는 지갑생성만 해봤었음!

밑에 내용은 여기 내용과 같슴다.

https://newpouy.github.io/jekyll/update/2018/03/24/naivecoin-kr-translate-4.html

 

Naivecoin Korean Translate Version #4

navie코인 번역 원문: lhartikk.github.io

newpouy.github.io

이번 챕터는 wallet 인터페이스를 만드는 것! 

사용자는 

  • private key 비밀키를 가진 지갑을 만들 수 있어야하고
  • 지갑에 잔액을 볼 수 있어야하고
  • 코인을 다른 노드로 보낼 수 있어야함

유저는 비트코인처럼 어떤 노드로 코인을 보내고 또 코인을 받을 나만의 주소만 가질 수 있으면 됨

(트랜잭션 인풋이니 아웃풋이니 뭐니 알필요없이 )

private key 비밀키를 만들고 저장하기

 

우리는 비밀키를 암호화하지는 않은 채로 특정경로( node/wallet/private_key )에 만들도록 할게

 

const privateKeyLocation 

= 특정경로

비밀키 생성하는 함수

const generatePrivateKey = () =>{

ec 암호화?

그걸 비밀키라고 명청

비밀키를 16진수로 내보내

}

 

초기지갑 함수

const initWallet = ()=>{

만약에 비밀키가 특정경로에 있으면 내보내

특정경로에 있지 않으면

비밀키 생성하고

생성했다고 콘솔로 알려줘

}

 

 

비밀키private key로부터 공개키public key(=address 주소의 역할)를 만들어낼 거임

 

지갑에서 공개키가져오는 함수

const getPublicFromWallet = () =>{

비밀키= 지갑에서 비밀키가져와

키= 비밀키를 'hex'화

키를 내보내

}

 

지금 우리는 비밀키를 암호화하지 않아서 보안상 매우 위험한 상황이야. 

이 과정은 간단히 만드는데 초점을 두고 있음요.

지갑은 

오직 하나의 비밀키를 가져야 하고 이로부터 공개가능한 접근가능한 address 주소를 가진 지갑을 만들거야.

 

Wallet balance 지갑 잔고

이전 챕터에서 우리는 블록체인상에 코인을 가지고 있다는 것을

쓰이지 않은 트랜잭션 아웃풋 unspent transaction outputs 한 꾸러미를 가지고 있다는 것을 의미한다고 했음

이 아웃풋 목록은 당연히 자신만의 비밀키와 매칭된 공개 주소값을 가지고 있지.

 

그렇다면 '잔고'계산은 쉬울거야

그냥 '쓰이지 않은 트랜잭션 아웃풋 unspent transaction outputs ' 을 다 더해버리면 돼!

 

잔고를 가져오는 함수 

const getBalance = (address, unspentTxOuts쓰이지않은트랜잭션아웃풋들) =>{

내보내

쓰이지 않은 트랜잭션아웃풋에서

 트랜잭션아웃풋의 주소와 주소가 일치한 애들 걸러내고 

맵함수로 트랜잭션아웃풋의 양들 

다 더해

}

 

잔고조회에는 비밀키가 필요없어. 이는 누구나 주어진 address주소의 잔고를 알수 있다는 뜻!

 

트랜잭션 생성 Generating transactions

코인을 보낼 때 사용자 즉 유저는 트랜잭션 인풋,아웃풋 알필요없다햇지

그럼 사용자 A가 단 한번의 트랜잭션으로 그가 가진 50코인 중 단지 10 코인만 B한테 보내고 싶다면 어떻게 해야될까?

 

방법은 

10코인은 B에게 보내고 

나머지 40코인은 자신A에게 돌려보내는 거지.

 

전체 트랜잭션 아웃풋은 항상 소진spent 되어야해!

만약에 부분으로 나누고 싶다면 새로운 아웃푸을 생성함으로 가능해.

 

 

 

 

트랜잭션 인풋을 만들어야하는데 (받은 코인을 확인하고 보내줘야돼서 그런거라 이해함.)

이를 위해

'소진되지 않은 트랜잭션 아웃unspent transaction outputs'

목록을 돌면서 우리가 원하는 금액이 될때까지 반복문을 돌리는 것임. 

 

트랜잭션아웃풋목록에서 원하는 만큼 찾는 함수

const findTxOutsForAmount =(amount, muUnspentTxOuts) =>{

현재양은 = 0;

사용안된트랜잭션아웃풋들 담겨진 곳 = [];

for (나의 소진되지않은트랜잭션아웃풋들 중의 나의 소진되지않은트랜잭션아웃풋) {

사용안된트랜잭션아웃풋들 담겨진 곳

에다가  나의 소진되지않은트랜잭션아웃풋 을 집어넣어

 

현재 양 = 현재양 + 나의 소진되지않은트랜잭션아웃풋의 양

 

만약에 (현재 양 >= amount) {

남는 양 = 현재양 = amount ;

내보내.  사용안된트랜잭션아웃풋들 담겨진 곳,  남는 양 

}

}

 

아니면 에러 뱉어 '거래보낼 코인이 충분하지 않아' 

 

}

 

 

leftOverAmount 남은 금액양은 나중에 자신에게 다시 back할 금액이야

소진되지않은 트랜잭션 아웃풋을 가진만큼 트랜잭션 txIns를 만들어낼 수 있어. 

 

서명되지않은 TxIn으로 함수

const toUnsignedTxIn = (unspentTxOut) =>{

트랜잭션인풋 = 새 트랜잭션인풋함수;

트랜잭션인풋의 트랜잭션아웃풋 아이디 = unspentTxOut의 트랜잭션아웃풋 아이디

트랜잭션인풋의 트랜잭션아웃풋 인덱스 = unspentTxOut의 트랜잭션아웃풋 인덱스

내보내 트랜잭션인풋;

}

 

const {

사용안된트랜잭션아웃풋들 담겨진 곳includedUnspentTxOuts

남은금액양 leftOverAmount 

} = findTxOutsForAmount(amount, myUnspentTxouts);

 

서명되지 않은 TxIn인풋들 함수

const unsignedTxIns =

사용안된트랜잭션아웃풋들 담겨진 곳에서

서명되지않은 트랜잭션인풋 하나씩 꺼내. 

 

 

 

다음으로는 두 개의 트랜잭션 아웃풋들txOuts를 만들어야해요

하나는 보낼것.

다른 하나는 다시 back할 것. 

 

만약 txIns 트랜잭션인풋들이 보낼 금액과 같다면

남은금액 leftOverAmount값은 0이므로 

back하기 위한 트랜잭션은 만들지 않아도 되지. 

 

트랜잭션아웃풋들을 만드는 함수

const createTxOuts =(수신자주소, 내 주소 , 양, 남은금액양 ) => {

const txOut1  = 새 트랜잭션아웃(수신자주소, 양)

만약에 (남은금액양 === 0) {

[txOut1] 배열에 담아서 내보내

}

아니면 

const leftOverTx =  새 트랜잭션아웃풋(내 주소, 남은금액양)

내보내 [txOut1 , leftOverTx]

 

}

 

마지막으로 트랜잭션 id값을 생성하고

트랜잭션인풋들에 

서명하면 끝임

 

const tx = 새 트랜잭션함수;

tx의 트랜잭션인풋들은 = 서명하지 않은 트랜잭션 인풋들;

tx의 트랜잭션아웃풋들은 = 트랜잭션아웃풋들을 만드는 함수(수신자주소, 내주소, 양, 남은금액양)

tx의 아이디 = 트랜잭션아이디가져오는 함수(tx)

 

tx의 트랜잭션 인풋 = tx의 트랜잭션인풋들을 맵함수로 

트랜잭션인풋을 골라서

그것의 서명 = 사인한다트랜잭션인풋에(tx, Index, privateKey, unspentTxOuts)

내보내 트랜잭션인풋

 

 

 

지갑사용하기 Using the wallet

지갑을 사용하기 위한 버튼기능 렛츠고

 

app.post('/mineTransaction' ,(요청, 응답) =>{

주소는 요청.body.주소

양은 요청.body.양

const resp = 트랜잭션가지고 다음블럭생성하는 함수 generatenextBlockWithTransaction(주소, 양)

응답.해서 resp를 내보내

 

})

 

 

 

 

 

 

이것처럼 사용자는 

주소랑 코인금액(양)만 제공하면 됨.

블록체인의 노드가 나머지는 알아서 할거야 물론 코드짰으니까 가능한거겠지 쿸

 

결론 

우리가 지금 한거는 

아주 간단한 수준의 트랜잭션 알고리즘을 가진 비암호화된 지갑이야.

이 알고리즘은 2개의 아웃풋들만 만들어낼뿐이지만,

실제 현실 블록체인은 다르겠지?

 

우리는 이 지갑에서 50코인의 인풋을 가지고 5,15,20의 아웃풋을 만들 수 있어.

하지만 이것도  /mineRawBlock 인터페이스를 사용해서 조절가능하지.

 

블록체인에서 직접 채굴을 함으로써 원하는 트랜잭션을 발생시킬 수도 있어!

발생시킨 트랜잭션을 블록체인에 연결시키는 것을 다음챕터에서 할거임요.

이번 내용 코드는 이거

https://github.com/lhartikk/naivecoin/tree/chapter4

 

GitHub - lhartikk/naivecoin: A tutorial for building a cryptocurrency

A tutorial for building a cryptocurrency. Contribute to lhartikk/naivecoin development by creating an account on GitHub.

github.com

 

 

 

 

 

 

 

 

 

 

 

728x90
728x90

드디어 트랜잭션 호달달 여기부터는 팀플에서 안해본 작업이라서 화이팅,,,해볼게,,

naivecoin 이와 내용같음.

https://newpouy.github.io/jekyll/update/2018/03/22/naivecoin-kr-translate-3.html

 

Naivecoin Korean Translate Version #3

navie코인 번역 원문: lhartikk.github.io

newpouy.github.io

 

트랜잭션 개념

트랜잭션(거래내역)을 써서 우리가 만드는 블록체인이 암호화폐로 쓰일 수 있게 되는거지. 화페를 가졌다는 증거를 보여줄 수 있으면 그걸 보내면서 쓰는거야 이때 필요한 개념은 public-key 암호화, 서명, 트랜잭션인풋 과 아웃풋 등이 있다

 

 

퍼블릭키 암호화와 서명

퍼블릭키 암호화를 위해서는 키 한 쌍 비밀키secret key와 공개키public key가 필요하다. 

공개키는 비밀키로부터 만들어지지만 반대는 불가능! 공개키는 말그대로 public 누구에게나 공유가능

비밀키로 암호메세지를 만들수 있고 그 메세지와 공개키가진 누구나 그 암호메세지가 특정 비밀키로 만들어졌다는 걸 증명할 수 있어. (영지식 증명)

 

퍼블릭 키 암호화하려고 elliptic라이브러리 사용한 다음 두개 암호화함수 쓸거야

 

  • Hash function (SHA256) for 작업 증명과 블록의 고유성 확보
  • Public-key cryptography (ECDSA) for 트랜잭션

유효한 비밀키 private key는 32byte문자열로 이루어진다. (밑에는 예시)

19f128debc1b9122da0635954488b208b829879cf13b3d6cac5d1260c0fd967c

유효한 공개키 public key는 '04'가 붙은 64byte 문자열이다. ->코인을 수신하는 측에서 사용.

04bfcab8722991ae774db48f934ca79cfb7dd991229153b9f732ba5334aafcd8e7266e47076996b55a14bf9913ee3145ce0cfc1372ada8ada74bd287450313534a

 

 

트랜잭션 

트랜잭션의 아웃풋

transaction outputs (txOut): 코인을 어디로 보낼지에 대한 정보 = 주소와 코인의 양 으로 구성됨

 주소는  ECDSA(타원곡선 디지털서명 알고리즘) 퍼블리키값. 

유저가 특정코인에 접근하기 위해 해당 공개키에 대응하는 private key를 가지고 있어야함

 

class TxOut {

구조

주소 address

양 amount

}

 

 

트랜잭션의 인풋

transaction inputs (txIn): 코인이 어디로부터 왔는지 정보. -오직 비밀키로부터 생성된 서명signature값만 가진다!

각가의 txIn은 이전의 Output을 참조하고 서명 통해 열림. 서명의 역할은 오직 공개키와 한쌍인 비밀키를 가진 사용자만이 트랜잭션을 만들 수 있음을 보증한다. 

class TxIn {

}

 

블록체인은 비밀키 그자체를 가지지 않고 공개키와 서명만을 가진다

 

결과적으로 

트랜잭션 인풋txIn은 코인을 풀고

트랜잭션 아웃풋txOut은 코인을 잠그는 역할을 한다. 

 

트랜잭션 구성(구조)

class Transaction {

(타입스크립트는 안에 타입넣는데 자바스크립트는 안에 아무것도 안넣었음)

}

 

트랜잭션 아이디 Transaction id

트랜잭션 아이디는 트랜잭션 컨텐츠로부터 계산된 해시값이다. 트랜잭션 아이디의 서명은 해시에 포함되지 않지만 

나중에 트랜잭션에 추가될 것임 

//여기 어려워서 map함수랑 reduce함수 개념다시 잡고 왔음

 

트랜잭션 아이디 갖는 함수 

const getTransactionId = (트랜잭션) =>{

txIn컨텐츠 = 트랜잭션.txIns  을 map함수로 (트랙인풋의 txOutId 와  트랙인풋의 txOutIndex)를 계속 더해주고

그리고 reduce로 반복해서 누적값과 현재값을 더해준 것(초기값은 ' ' 빈값)

 

txOut컨텐츠 = 트랜잭션.txOuts 을 map함수로 (트랙아웃풋의 주소 와  트랙아웃풋의 양) 을 계속 더해주고

그리고 reduce로 반복해서 누적값과 현재값을 더해준 것(초기값은 ' ' 빈값)

}

 

여기서 쓴걸로 받아온다고 추정함

 

트랜잭션 서명 

서명된 이후의 트랜잭션 내용이 수정될 수 없다. 트랜잭션이 공개되어서 블록체인에 연결되지 않은 사람도 누구나 그 거래내역에 접근할 수 있어

트랜잭션 인풋에 서명할 때 오직 txId만 서명될거야. 만약에 트랜잭션의 어느 부분이라도 변경되면 txId의 값은 변경되고 이는 해당 트랜잭션과 서명을 무효화한다. 

 

트랜잭션 인풋에 서명하는 함수 

const signTxIn = (트랜잭션, tx인풋인덱스, 비밀키, aUnspentTxOuts) {

 

txIn 은 트랜잭션의 인풋들[인덱스들어간]

dataToSign은 트랜잭션의 아이디

referencedUnspentTxOut는 보내지 않은 트랜잭션아웃풋(인풋의 아웃풋 아이디, 인풋의 아웃풋 인덱스순서, aUnspentTxOuts)을 찾은 것. 

referencedAddress는 참조된보내지지않은 아웃풋의 주소

key는 공개키를 hex로 암호화한 것

signature서명은 암호화

 

서명 뱉어내.

 

누군가가 트랜잭션에 변경을 시도하면 어떻게 되는지 보자

한 노드의 해커가 "코인을 a에서 b주소로 보내" 라고 내용을 가진 거래내용을 txId와 함께 받았다.

해커는 b주소를 c로 바꿔. 수진주소 변경돼서 txId는 더이상 유효하지 않아.

서명 역시 txId기반으로 만들어져서 더 이상 유효하지 않아. 

그래서 변조된 트랜잭션은 다른 노드들에게 받아들여질 수 없다. 

 

 

 

Unspent transaction outputs

보내지지 않은 트랜잭션 아웃풋

 

트랜잭션 인풋은 항상 UtxO 보내지지 않은 트랜잭션 아웃풋을 참조해야해. 

우리가 블롳게인에서 코인을 갖는다는 것은 보내지지 않은 거래아웃풋 목록을 가진다는 것.

이 아웃풋들의 퍼블릭,공개키는 private key 비밀키에 대응한대

트랜잭션 유효성 검증이란 측면에서 이 uTxO보내지지 않은 거래아웃풋은 아주 중요함. 

그리고 이거는 현재 최신상태의 블록체인에서 나와야 되니까 우리는 업데이트를 구현할 거야. 

 

 

보내지지 않은 트랜잭션 아웃풋의 데이터구조

uTxO의 데이터구조

class UnspentTxOut {

트랜잭션 아웃풋 아이디

트랜잭션 아웃풋 인덱스

주소

}

 

보내지지않은 트랜잭션 의 목록의 자료구조는 단순한 배열이다. 

let unspentTxOuts = [] ;

 

 

Updating unspent transaction outputs

새로운 블록이 더해질때마다 uTxO 보내지지않은 트랜잭션업데이트해야한다. 

새로운 트랜잭션(거래내역)은 기존의 트랜잭션 아웃풋 목록에 영향을 주고 새로운 아웃풋을 발생시키기 때문이다.

이를 위해 새로 생성된 블록으로부터 '새로운 보내지지않은 트랜잭션 아웃풋들'을 순회하는 돌아다니면서 찾아보는 작업을 할거야. 

 

새로운보내지지않는 트랜잭션 아웃풋들 함수

const newUnspentTxOuts = 

새로운 트랜잭션에서 하나씩 작업할거야

(그배열의 트랜잭션아웃풋목록들에서 아웃풋, 인덱스를 내보낼거야

아이디랑, 인덱스랑, 아웃풋의 주소, 아웃풋의 양을

새로운 보내지지 않은아웃풋에 넣어서..)

 

그리고 그걸 누적값의 배열에 현재값을 더한 값.

 

 

 

블록에서 이미 소비된 트랜잭션 아웃풋들에 대해서도 알아야함. 

새 트랜잭션의 인풋을 검사하면 알수 있다!

 

소비된트랜잭션아웃풋들 함수

const consumedTxOuts = 

새 트랜잭션에서 돌면서 인풋들 찾아내

찾아낸 누적값에 현재값을 더해. (누적값의 초기값은 빈[]배열임)

그리고 그걸(트랜잭션인풋) 새로운 보내지지 않은 트랜잭션아웃풋으로 보내는데

보낼때 (트랜잭션인풋의 아웃풋 아이디, 트랜잭션인풋의 아웃풋 인덱스, ' ', 0) 을 담아서 

배열로 내보내????

 

//어렵다..ㅠㅠㅠ

 

이미 소비된 아웃풋을 제거하고 이제 새로운 트랜잭션 아웃풋을 만들 수 있게됨

 

결과적으로(?)보내지지않은트랜잭션아웃풋들 함수

const resultingUnspentTxOuts =

보내지않은 트랜잭션 아웃풋에 

필터 거쳐라 

findUnspentTxOut(보내지지않은tx아웃풋의 아웃풋아이디, uTxO의 아웃풋인덱스, 소비된 아웃풋)가 아닌 것!

그리고 거기에 새로운보내지않은트랙잰션아웃풋들 넣어라 

 

 

 undateUnspentTxOuts함수가 이 작업들을 총괄 수행할거임. 

그리고 이함수는 반드시 트랜잭션의 유효성이 검증된 이후에 수행되어야한다.

 

 

Transactions validation 트랜잭션 유효성 검증

이제 마침내 무엇이 트랜잭션을 유효하게 만드는지 모습을 잡을 수 있게 되었음

Correct tracnsaction structure 정확한 트랜잭션 구성

트랜잭션은 정의된 방식을 따라야만 한다. 

 

트랜잭션구조가 맞는지 보는 함수

const isValidTransactionStructure =(트랜잭션) =>{

만약에

트랜잭션의 아이디가 문자열string이 아니면

트랜잭션아이디를 찾을 수 없다고 뜨게 하고

틀림 내보내

...

}

 

Valid transaction 유효한 트랜잭션 아이디

ID 확인해야함 

if (트랜잭션아이디가져왔을 때 == 트랜잭션 아이디 가 아니면 ) {

유효한 아이디아니라고 뜨고 

틀림 내보내

}

 

Valid txIns

트랜잭션 인풋들의 서명도 사용되지 않은 아웃풋을 잘 참조하고 있는지 확인해야함 

const validateTxIn = (트랜잭션인풋, 트랜잭션, a안보내진트랜잭션아웃풋) {

만약에 참조된트랜잭션 아웃풋이 없을 때 null일때 {

참조된 트랜잭션 아웃풋 찾을 수 없다고 띄우고 + 트랜잭션 인풋을 JSON형태로 문자로 보여

그리고 틀림 내보내

}

 

주소는 = 언급된 트랜잭션아웃풋의 주소

키는 = 주소를 hex화한거(?)

return key.verify(transaction.id, txIn.signature);

}

 

Valid txOut values

유효한 아웃풋 값인지 확인!

아웃풋의 코인개수와 인풋의 코인갯수도 같아야만한다. 아웃풋이 50개면 인풋도 50개!

 

전체 트랜잭션인풋의 값들

const totalTxInValues = 

트랜잭션의 인풋들

을 맵함수로 하나씩 작업하는데

(인풋들을 getTxInAmount로 )

그걸 초기값 0부터 시작해서 누적값과 현재값 더해줘

 

전체 트랜잭션아웃풋들의 값들 

const totalTxOutValues = 

트랜잭션의 아웃풋들

을 맵함수로 하나씩 트랜잭션 아웃풋 수량꺼내고

그걸 초기값 0부터 시작해서 누적값과 현재값 더해줘

 

만약에 (전체 트랜잭션아웃풋들의 값이 전체 트랜젝션잇풋값과 다르면) {

전체 트렌잭션아웃풋 값들이  트렌잭션 안에 있는 전체 인풋값들과 다르다! 

띄우고 

트랜잭션 아이디 보여주고

틀림 내보냄

}

 

Coinbase transaction

트랜잭션 인풋은 항상 

보내지지않은 트랜잭션 아웃풋들 unspent transaction outputs을 참조해야해!

 

하지만 최초의 코인은 어디를 참조해야하지?

이를 해결하기 위해 coinbase transaction이 필요해! 

코인베이스 트랜잭션은 오직 아웃풋만 포함해. 인풋은 없어.

코인베이스 트랜잭션이 새로운 코인을 돌게 만드는 펌프같은 역할을 할거야. (나는 거래하기 위해서 기존에 넣어둔, 채굴자한테 줄 꽁돈이라고 이해했어.. 내보내기 위해 준비된 양)

코인베이스 아웃풋의 양을 50코인으로 정했어

 

const COINBASE_AMOUNT = 50;

 

코인베이스 트랜잭션은 당연히 노드의 첫 트랜잭션이고

블록 채굴자정보를 포함해. 블록을 발견한 댓가로 채굴자는 첫 코인베이스 트랜잭션 아웃풋으로 코인 50개를 받을거야.

 

코인베이스 트랜잭션의 인풋에는 block의 height값을 넣을게. (코인베이스 트랜잭션에 인풋없다며! ) 여기서 인풋 안에 넣은 블록의 height높이 값이 고유한 ID값 역할한대. 이 고유값이 없으면 코인베이스 트랜잭션은 항상 같은 주소에 50코인을 발행하게 돼

(트랜잭션 인풋은 코인이 어디로부터 왔는지에 대한 정보제공하는 것. 코인을 풀고 코인을 잠그는 역할함.. )

 

 

코인베이스 트랜잭션의 유효성 검증 (은 다른 트랜잭션이랑 좀 다를거야)

const validateCoinbaseTx = (트랜잭션, 블록인덱스) =>{

만약에 트랜잭션 아이디가져온게 !== 트랜잭션 아이디와 달라

유효하지 않은 코인베이스 트랜잭션 아이디야~

틀림뱉음

 

만약에  트랜잭션의 인풋들의 길이가 1이 아니면 

(//빈 문자열은 length가 0이래)

'하나의 txIn은 코인베이스 트랜잭션에 지정되어야한다' 라고 뜸

 

만약에 트랜잭션의 인풋들의 0번째 배열의 트랜잭션아웃풋인덱스가 

블록인덱스가 아니면 

'코인베이스 트랜잭션 안에 있는 그 트랜잭션 인풋의 인덱스는 반드시 그 블록의 높이가 되어야한다.'

틀림 뱉음

 

만약에 트랜잭션의 트랜잭션아웃풋들의 길이가 1이 아니면

코인베이스 트랜잭션안에 있는 트랜잭션 아웃풋들이 유효하지않은 숫자다

틀림뱉음

 

만약에 트랜잭션의 아웃풋들의 0번째배열의 양이 

코인베이스 양과 다르면

'코인베이스 거래내역에서 유효하지 않은 코인베이스 양'

틀림뱉음

 

 

다 맞으면 통과~

}

 

 

 

결론

이 챕터에서 우리는 블록체인의 트랜잭션 거래내역에 대해 알아봤어요

트랜잭션, 거래내역은

인풋과 아웃풋으로 

구성되어있다는 것. 

 

 

기본개념은 간단해.

사용되지 않은 트랜잭션 아웃풋과 블록체인의 잠금해제된 부분이 유효하다는 걸 보여줄거야. 

 

하지만 트랜잭션을 실제로 만드는건 어렵쥐..

인풋과 아웃풋 만들고

프라이빗 키를 사용해서 그들에 서명해야해

이 과정은 다음챕터의 지갑wallet을 배우면 달라질거야

 

아직 연결된 트랜잭션에 대해서도 안살펴봤어.

블록체인이 트랜잭션 거래를 일으키기 위해서는 채굴을 해야만 해.

이때 '트랜잭션 fee'라는 개념도 필요해 다음챕터에서 볼거임

 

 

+

여기까지해본 코드로 해봤는데

map함수로 읽을수 없다고..

코드 확인해보니까 같은뎀..

근데 여기까지 나와있는 코드로 돌려봐도 작동안된다!

챕터4까지 해보고 돌려보자~~

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90
728x90

작업(을 통한)증명 Proof of Work

https://newpouy.github.io/jekyll/update/2018/03/20/naivecoin-kr-translate-2.html

 

Naivecoin Korean Translate Version #2

navie코인 번역 원문: lhartikk.github.io

newpouy.github.io

 

#1에서는 누구나 블록추가할 수 있었지만 이제는 계산을 푸는 작업을 해야 블록추가할 수 있게할거야. 이 계산 푸는 거를 채굴이라고 함. 또한 퍼즐의 난이도를 통해 블록 조절할거야. 좀 있다가는 트랜잭션(거래내역)에 대해서도 다룰거야. 이걸 통해서 보상받고 거래주고받고 하는거겠지

 

 

블록에 추가할 두 가지 속성 (-> 작업증명 퍼즐(블록의 해쉬값 찾기)을 풀기 위한 값)

  • difficulty 난이도  (=유효한 블록의 해쉬값은 얼마나 많은 0으로 시작해야하는가, 작은값찾기임)

해쉬값을 2진수로 표현한 다음 시작부분0의 갯수세어보는 것.

난이도 difficulty 가 4이면 임계값인 nonce가 1,2,3 올라가면서 해쉬값을 찾는다. 해쉬값계산 함수를 보면 

전체값을 합쳐서 hash화한걸 볼 수 있어 

 //다합쳐서 해시로 만들고 리턴
  const hash = cryptojs.SHA256(blockString).toString();
  return hash;

참고로 우리 코드에서도 다음블록찾는 nextBlock함수안에 findBlock함수가 있는데 거기서 nonce++로 수 올리고 있음.

 

 

 

블록의 해시값이 난이도를 만족하는지 

  • nonce 난이도를 만족하는 해시값을 찾기위해 반복한 값

 

//naive코드와 우리 팀플코드의 차이점

naive - 난이도를 만족하는지 확인하는 코드에서 hexToBinary 함수를 썼음 

function hexToBinary(s) {
  //헤더부분을 sha256 암호화한 결과
  //16진수 64자리를 2진수로 변환하기
  const lookupTable = {
    0: "0000",
    1: "0001",
    2: "0010",
    3: "0011",
    4: "0100",
    5: "0101",
    6: "0110",
    7: "0111",
    8: "1000",
    9: "1001",
    A: "1010",
    B: "1011",
    C: "1100",
    D: "1101",
    E: "1110",
    F: "1111",
  };

상단의 이미지로 예시를 들면 

난이도가 4일 때 hash:08f3... 이거를 binaryHash 2진수로 변환한거임 0: "0000", 8:"1000", f:"1111",3:"0011"

(숫자형태아니고 문자열 string형태라서 옆에 붙는다)

우리는 hexToBinary 함수를 안썼어. 그래서 nonce추가되는걸로 난이도 따라 해시값찾는걸로 나와. naive는 binaryHash기준으로 난이도 비교적 쉽게 통과되고 우리는 hash대로. 그니까 난이도 8일때 hash값 지금 0027..나오면 우린 안돼 더 돌려서 hash값 앞에 0이 최소한 8개여야 통과되는 거임. 

 

 

 

 

아무튼 다시 naive코드로 와서

 

블록의 해시값이 난이도 difficulty를 만족하는지 확인하는 코드

const hashMatchesDifficulty=(hash, difficulty)=>{

해시값을 이진수화해.

난이도만큼 0을 반복해.

이진수화한 거가. 지정한문자로 시작하는지 확인. 0이 8개인 문자로. 

}

 

난이도를 만족하는 해시값찾기 위해 같은 블록에서 여러번 계산해줘야한다. 이때 nonce값을 쓰는데 sha256라는 해시함수를 쓴다. 채굴이 바로 매번 다른 nonce값을 시도해보는 과정을 뜻한다. 

 

블록구조, 최초의 블록 구조에 difficulty와 nonce추가

class Block{

난이도 추가

넌스값 추가

}

 

 

블록찾기 

nonce값은 언제 증가시키는 findBlock 함수. 해시값을 찾을 때까지 반복문 돌릴거임.

const findBlock{

초기넌스값은 0

hash를 해시계산함수 안에 블록애들 넣어서 찾아.

만약에 난이도 확인하는 함수에 (해시랑, 난이도)보내서 통과되면

블록을 뱉어내(블록내용들을 담아서)

}

 

그렇게 블록이 채굴되면 네트워크통해서 발송(broadcast)되겠지

 

 

 

난이도(difficulty)를 어떻게 결정할건가

 

난이도값을 계산하는 로직필요해서 상수를 몇 개 정의하면서 만들었어. 하드코딩한거임. 물론 naive에서..ㅋㅋ

  • BLOCK_GENERATION_INTERVAL: 블록은 얼마나 자주 채굴되는가(Bitcoin의 경우 10분 간격이죠.)
  • DIFFICULTY_ADJUSTMENT_INTERVAL: 난이도difficulty는 얼마나 자주 조정되는가(Bitcoin은 2016블록마다 조정돼요.)
// in seconds
const BLOCK_GENERATION_INTERVAL: number = 10;

// in blocks
const DIFFICULTY_ADJUSTMENT_INTERVAL: number = 10;

신기한게 여기서는 그냥 임의로 지정한 숫자 10을 넣었는데 이게 어떻게 10초, 10블록으로 인식하나 싶었거든. 이게 같이 비교되는 애들따라서 인식을 하는거래.  코드를 보자

 

난이도가져오는함수

const getDifficulty=(블록체인)={

가장 마지막 블록= 블록체인.길이-1

만약에

마지막블록.index % BLOCK_GENERATION_INTERVAL(10블록으로 인식됨) 이 0이면 그리고 마지막블록.index가 0이 아니면

난이도 조절함수로 (마지막블록, 블록체인) 보내

 

아니면, 마지막블록의 난이도 뱉어내.

}

 

 

난이도 조절함수 

const getAdjustedDifficulty=(마지막블록,블록체인)=>{

이전조절블록 = 블록체인.길이- BLOCK_GENERATION_INTERVAL(10블록으로 인식됨)

예상시간= BLOCK_GENERATION_INTERVAL(10) 곱하기 DIFFICULTY_ADJUSTMENT_INTERVAL(10) //100이라는 숫자

걸린시간 = 마지막블록.timestamp -이전조절블록.timestamp     //그니까 10블록 생성될동안 걸린시간

 

만약 (걸린시간이 < 100 /2 ){

이전조절블록.난이도 +1  

아니면, (걸린시간 > 100* 2) {

이전조절블록.난이도 -1}

아니면, {

이전조절블록. 난이도 그대로 뱉어내

}

}

 

 

timestamp 

이전에는 아무 역할도 안해서 무슨값이든 상관없었는데 이제 난이도 조절하면서 블록 유효성 검증되는지 사용할거야.

 

유효한 타임스탬프인지 확인하는 함수

const isValidTimestamp = (새블록, 이전블록)=>{

이전블록.생성시간 - 60 < 새 블록.생성시간 그리고 새블록.생성시간 - 60 < 현재타임스탬프함수getCurrentTimestamp()

면 true

}

 

//현재 타임스템프 찍어주는 함수
function getCurrentTimestamp() {
  //Math.round 반올림함수
  return Math.round(new Date().getTime() / 1000);
}
 
new Date().getTime() / 1000이게 1970 년 1 월 1 일 자정 이후의 초를 알려준다고 함.

 

 

 

누적난이도

#1에서는 가장 긴 체인이 옳은 체인이라고 했지만 사실 

difficulty 난이도가 가장 많이 누적된 체인이 옳은 체인이야. 

 

작업증명에서 가장 중요한 건 그 과정이 풀기는 어렵지만 증명하기는 쉬워야한다는 것! 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90

+ Recent posts