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

내용출처: https://youtu.be/Q8g0tULRWn0

설명 굿!

 

map함수는 callbackFunction을 실행한 결과를 가지고 새로운 배열을 만들 때 사용한다.

반복문 돌면서 배열안의 요소들을 짝지어서 실행시켜주는 것.

기존객체를 건들지 않고 새 배열을 만든다. 

 

 

map함수는 배열에서 사용가능하다  //for문이랑 비슷

//map 함수 배열에서 사용가능
const arr = [1, 2, 3];
console.log(arr);
//배열안에 있는 값을 2씩 더해보려고 함
//for문쓰는 법
// const res = [];
// for (let i = 0; i < arr.length; i++) {
//   res.push(arr[i] * 2);
// }

// console.log(res);
//map함수 쓰기
//map함수 안에 callback함수 들어감
const res1 = arr.map((값) => {
  return 값 * 2;
});
console.log(res1);

 

(안에 들어가는 인자 내마음대로 '값'이라고 지정해서 씀)

 

 

 

화살표 간단하게 짧게 줄여서 쓸 수도 있다. 

//화살표 간단하게 짧게 줄여서 쓸 수 도 있음
const res2 = arr.map((값) => 값 * 3);
console.log(res2);

 

만약에 인덱스에도 접근하고 싶으면 다음에 적으면 됨 

(값, 인덱스) 두번째에 들어가는게  인덱스자리야

//만약에 인덱스에도 접근하고 싶으면
const res3 = arr.map((값, 인덱스) => 인덱스);
console.log(res3);

지금은 배열안에 있는 게 숫자인데

객체여도 가능하다

/지금은 배열안에 있는게 숫자인데 객체여도 가능하다!
const items = [
  { id: 1, name: "kaka" },
  { id: 2, name: "coco" },
];
const result = items.map((아이템) => {
  id: 아이템.id;
});

console.log(result);

 

 

객체를 return할 때는 중괄호를 해주고 리턴해야한다!!

//객체를 return할때는 중괄호를 해주고 리턴해야되지요
const result1 = items.map((아이템) => {
  return { id: 아이템.id };
});

console.log(result1);

 

 

 

추가 설명 여기 보기 ▼

map 사용법

map을 사용하는 방법은 callbackfn을 통해 주어진 3개의 인자(요소 값, index, 순회하는 대상 객체)를 사용해 새로운 값을 만드는 함수를 등록하는 것이다.

const numbers = [1];

numbers.map((number, index, source) => {

    // number: 요소값
    // index: source에서 요소의 index
    // source: 순회하는 대상

    console.log(number);
    // 1

    console.log(index);
    // 0

    console.log(source);
    // [1]

    return number * number;
});

map을 사용하는 경우 주로 number에 해당하는 요소 값을 많이 활용하지만 index와 array도 함께 사용할 수 있다.



출처: https://7942yongdae.tistory.com/48 [프로그래머 YD]

 

 

Javascript - Array map 사용법

오늘은 Javascript의 Array가 가지고 있는 map의 정의와 사용법 그리고 활용 방법에 대해 이야기해보려고 합니다. 일단 Array가 가진 map 함수가 어떤 함수인 지부터 살펴보도록 하죠. map 정의 Array.prototy

7942yongdae.tistory.com

 

 

 

reduce함수 

반복되는 모든 것에 쓸 수 있음. 초기값활용해서 쓸 수 있다. 

reduce메서드 배열.reduce((누적값, 현잿값, 인덱스, 요소) => { return 결과 }, 초깃값);

누적값을 쓴다는 것에 명심!!!

출처: https://www.zerocho.com/category/JavaScript/post/5acafb05f24445001b8d796d

 

(JavaScript) map, reduce 활용하기 - 함수형 프로그래밍

안녕하세요. 이번 시간에는 map과 reduce 메서드에 대해 알아보겠습니다. 배열에 있는 바로 그 map과 reduce 맞습니다. 많은 분들이 forEach는 사용하시는데 map과 reduce는 잘 안 쓰시더라고요. 그리고 redu

www.zerocho.com

(코드따라치면 이해할 수 있음요.)

 

//reduce 함수/////////////////////////////////////////////////
//배열.reduce((누적값, 현잿값, 인덱스, 요소)
//=> { return 결과 }, 초깃값);
const abc = [1, 2, 3];
const result2 = abc.reduce((누적값, 현재값, 인덱스) => {
  console.log(누적값, 현재값, 인덱스);
  return 누적값 + 현재값;
}, 0);

console.log(result2);

//a가 초기값인 0부터시작해서 return하는 대로 누적되는거임
//0,1,0
//1,2,1
//3,3,2

//초기값이 없을경우!
//abc=1,2,3
const resultNoInit = abc.reduce((누적값, 현재값, 인덱스) => {
  console.log(누적값, 현재값, 인덱스);
  return 누적값 + 현재값;
});

console.log("초기값없는경우", resultNoInit);
//0,1,0
//1,2,1
//3,3,2
//6

const abc2 = [0, 2, 4];
const result3 = abc2.reduce((누적값, 현재값, 인덱스) => {
  console.log(누적값, 현재값, 인덱스);
  return 누적값 + 현재값;
}, 0);

console.log(result3);
//0,0,0
//0,2,1
//2,4,2
//6

const result4 = abc2.reduce((누적값, 현재값, 인덱스) => {
  console.log(누적값, 현재값, 인덱스);
  return 누적값 + 현재값;
}, 5);

console.log(result4);
//5,0,0
//5,2,1
//7,4,2
//11

실제 결과

 

 

그래서 map함수예시도 reduce써서 가능하다

 

//map함수 예제를 reduce로 만들어보겠다
const 원투쓰 = [1, 2, 3];
결과 = 원투쓰.reduce((누적값, 현재값) => {
  누적값.push(현재값 % 2 ? "홀수" : "짝수");
  return 누적값;
}, []);

//초기값 빈배열......
//홀수, 짝수,홀수

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
728x90

수업때 배운걸 바탕으로 코드르 짠건데

수업때 쓴 코드예제가 이거다(추정) 

https://newpouy.github.io/jekyll/update/2018/03/19/naivecoin-korean-translate-version.html

 

Naivecoin Korean Translate Version #1

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

newpouy.github.io

 

우리팀이 한 내용은 ,시각화에서 꽤나 시간을 잡아먹었지만, 

제네시스 블록 만들고 

노드 켜면 제네시스 블록 만들고 db생성 연결돼서 제네시스 블록이 들어감

채굴하면 그 내용도 들어감

 

각노드가 db가지고 있어서 내용들어가고 ws웹소켓으로 둘이 연결을 시키면 블록 추가할때 받아옴. 길이가 긴쪽걸로 받아와. 지갑 생성은 지갑주소address띄우고 시각화정도만!

 

(우리팀한 거 백단내용 중간만 정리한거. 에러천국리뷰도 조만간 올리겠다 안에 팀플한 전체코드링크있음)

https://stepby-yun.tistory.com/115?category=0 

 

블록체인 채굴, p2p서버통신, 블록업데이트 완료 (코드 적어놓고 정리)

각 노드, 참여자 컴퓨터에 3001이 관리자 6001이 노동자.. http서버는 모든 걸 보여주기 용으로 돌리는거 라고 대충이해했는데.. 이후에 db서버랑, 리액트서버랑 얘네가 어떻게 맞물리는지 사실 아직

stepby-yun.tistory.com


~~최소한의 블록체인 짜기~~~ 내용출처 상위링크

이제 트랜젝션 거래 내용을 짜야하는데 전체 구조를 제대로 이해못했는지 잘안된다. 문서보면서 정리하면서 해보자

나름의 코드풀이임..

(보니까 얘는 TypeScript를 썼더라고 그래도 npm start하면 알아서 자바스크립트로 뽑아 변환해주더라)

 

  • 블록과 블록체인
  • 기존 데이터에 새로운 블록 더하기
  • 노드(node) 간 통신과 싱크 맞추기
  • 간단한 HTTP를 통한 노드 제어

(첫번째 장에 써있는 이거 . 이건 다 한거임 그래도 다시 보기.. 함수명이 좀 다르지만)

 

블록구조 짜기

 

블록구조- 순서 , 데이터, 타임스템프, 해쉬값(블록데이터기반으로 계산됨, 블록의 고유한 식별자기능), 이전해쉬값

 

class Block {

constructor (인덱스, 해시~~~)

 this.인덱스= 인덱스;

~~

}

 

(우리는 Block에 헤더랑 바디 넣고, BlockHeader추가로 만들어서 거기에 버전이랑 인덱스 넣었음)

 

최초의 블록(제네시스블록) - 블록체인의 시작. 안의 값은 일일이 넣어주면 됨.

const genesisBlock

 

블록체인 싹 저장할 배열 (javascript in memory 방식으로 저장. 컴끄면 사라짐)

const blockchain= []    //1장에서는 genesisBlock를 넣은 걸봐서 제네시스 블록 넣어서 보려나봄

 

 

필요한 함수 만들기

 

해쉬값계산함수

const calculateHash

 

 

다음블록만드는 함수(이전 해쉬 블록 알아야함)

const generateNextBlock

 

 

블록의 유효성 검사할 함수

 

그 조건임 

  • 이전 블록보다 인덱스값이 1 클 것.
  • previousHash값이 이전블록의 hash값일 것.
  • hash값이 유효한 값일 것. 다음의 코드로 이를 검사할 수 있어요.

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

1. 만약 이전블록인덱스가 새 블록인덱스보다 길이 더 길면

틀림 뱉어

2. 이전블록해시와 새블록이전해시가 같지 않으면

틀림뱉어

3. 새블록의 해시 계산한것이 새블록해시와 같지 않으면

틀림 뱉어

 

다 통과하면 맞음 뱉어

}

 

 

 

참고로 나 저기 들어가는 ,"새블록, 이전블록" 이렇게 써놓은 거, 파라미터(매개변수parameter)가 이름 내마음대로 지어줘도 되는건지 몰랐음.. 그래서 req, res도 막 거꾸로 써도 되더라고. 이전에 보내준 값(전달인자 argument)을 여기서 어떤 이름으로 쓸지 변수 정하고 이 함수 안에서 쓰는 것이었다!!!! 이전에 보내준 값이름이랑 이름이 달라도 상관없는 것!..ㅋㅋㅋ그동안 이름같은게 많길래 헷갈렸는데 이렇게 유동적이었다뉘 

 

 

 

 

블록의 구조 검사하는 함수 

const isValidBlockStructure {

블록 인덱스 타입은 숫자

블록 해시 타입은 문자열string(왜 끈, 줄이냐면 한글자씩연달아 메모리에 저장되는게 끈같아서..)

블록 시간기록, 타임스탬프는 숫자

블록 데이터,정보는 문자열

}

 

전체 블록체인을 검사하는 함수

최초의 블록검사하고, 이어지는 모든 블록검사할거야~

(이함수는 replaceChain, 새블록들어왔을때 전체 체인 교체할 경우에 쓸거임)

 

const isValidChain =(유효한정보의블록체인)=>{

최초의 블록을 json형태로 내보내라.

만약에 최조블록이 첫번째 인덱스가 아니면 틀림뱉어.

for문으로 돌릴건데, 두번째인덱스부터 블록길이까지 돌릴거임

만약에 새로들어온 i번째 블록의 검증이 실패하고, 새로들어온i-1번째 블록검증실패하면

틀림뱉어

 

다 통과하면 맞음 뱉어

}

 

 

 

블록체인은 원본체인 길이를 다같이 갖고 있는 개념이라서

유효성 검증 거친 옳은 체인은 하나만 존재해야해!

c가 채굴해서 블록체인에 연결한 다음에 

동시에 a도 채굴하고 b도 채굴했을때 누구꺼로 하냐의 고민을

더 긴 체인을 옳은 체인으로 간주해. 

(이유는 체인이 길다는 건 다른 노드가 아직 받지 못한 블록을 갖고 있다볼수있어서)

https://newpouy.github.io/jekyll/update/2018/03/19/naivecoin-korean-translate-version.html

그래서 만드는

 

긴체인으로 전체 교체하는 함수

const replaceChain =(새블록)=>{

만약에 새블록이 전체 블록체인을 검사하는 함수를 잘 통과됐고 새블록길이가 전체블록길이보다 길면

콘솔로 "받은 블록체인이 유효하다. 현재블록체인을 받은 블록체인으로 교체한다" 띄움

 

아니면

콘솔로 "받은 블록체인 안유효해" 띄움

}

 

 

다른 노드와 통신

 

노드(참여자) 사이에 통신이 있어야 블록체인할 수 있겠지? 그래야 탈중앙화해서 우리끼리 알아서 거래하지.

데이터 싱크를 맞춰야하기 때문에 계속 통신하면서 우리가 가진 원본이 같은지 확인해야되는 게 물론 가장 큰이유지.

 

  • 노드가 새 블록을 만들면 그것을 네트워크로 방출(발송,broadcast)해야한다.
  • 노드 간에 연결될 때, 각자 지니고 있는 가장 마지막 블록이 무엇인지를 파악한다.
  • 자기보다 긴 체인과 연결되면 상대가 가진 블록 중 내가 가진 블록 이후의 모든 블록을 추가하여 싱크를 맞춘다. (긴걸로 대체된다는 그 얘기임)

노드간의 통신에는 웹소켓을 쓸거임 

 

먼저 노드를 제어하기 위한 http서버를 만들자

 

const initHttpServer =(내포트) =>{

express모듈써서 쓸 서버 app이라고 만들어놓음

그리고 서버통해서 json형태로 정보 띄우게 할거임

어떻게 쓸거냐면

/blocks버튼 쓸거야-요청들어오면 getBlockchain함수, 기존에 있는 갖고 있는 블록 다 보이게 응답할거야

/mineBlock 버튼, 근데 정보를 내가 보내주는,, 

새블록은 요청한 바디의 데이터를 다음블록생성함수에 넣은거야.

새블록으로 응답할게

/peers 버튼, 웹소켓으로 연결한 주소랑 포트번호 보여줄게

/addPeer버튼 req.body.peer보내서 노드 연결해줄게. 

}

 

서버실행해.listen (내포트주소,()=>{

내포트주소는 여기야

})

 

 

 

이제 이 서버로 

  • 블록의 리스트 가져오기
  • 새로운 블록을 만들기
  • 노드 목록을 가져오거나 새로운 노드를 추가하기 curl명령어로도 노드를 제어할 수 있어요.

할수 있음

curl http://localhost:3001/blocks

get이거 써서 

 

 

 

전체구조

 

노드, 참여자는 

두개의 웹서버와 통신할거야. 

1. 사용자가 노드를 제어하기 위한 http서버

2. 다른 노드들과 통신하기 위한 웹소켓서버

 

(팀플때는 노드별로 웹소켓서버를 만들어줬었는데 둘이연결할 웹소켓서버 하나만 있으면 상관없겠네)

 

728x90

+ Recent posts