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

 

 

 

ㅋㅋ그놈의 에러ㅋ

걍 db안켜서..ㅋㅋ...

나한테 깔려있는 sql처럼 자동인줄알았지

 

sudo service mysql restart

 

 

이건 자동실행시켜주는 거.

 

 

 

 

 

728x90
728x90

알아본다면 당신은 천재임 (못알아봐도 천재)

각 노드, 참여자 컴퓨터에

3001이 관리자 

6001이 노동자..

http서버는 모든 걸 보여주기 용으로 돌리는거

 

라고 대충이해했는데.. 이후에 db서버랑, 리액트서버랑 얘네가 어떻게 맞물리는지 사실 아직도 서버관계를 정확하게 모르겠다! 

 

일단 코드 여기요 (지금까지 한 것만 포크해놓음)

 

https://github.com/jsoyun/4th-project-YunminBlock

 

GitHub - jsoyun/4th-project-YunminBlock: 4th BlockChain Project

4th BlockChain Project. Contribute to jsoyun/4th-project-YunminBlock development by creating an account on GitHub.

github.com

 

 

각 컴퓨터 참여자로 노드 만들어서 

p2p서버 전달 확인해봄

 

참고로 실행할 때는 

node node1/r_httpServer.js

이렇게 했다

 

 

두 서버 각각 열고 

3001
3002

 

 

get 요청인 blocks로 들어가보면

둘다 제네시스 블록이 잘 들어가 있음요 

 

서버 연결!!

post addpeers로 

3002한테 요청하는거임 

 

3002가 6001 웹소켓으로  상대 웹소켓 주소 ws 6001로 연결

문 똑똑

 

 

 

 

peers로 연결된거 확인!

 

 

이제 둘이 연결됐으니

3002에서 채굴해보겠음 

3002 채굴돼서 들어왔지?

 

 

그렇다면 3001을 새로고침해보면 3002랑 연결되어있고 그블록의 길이가 더 길어서 추가됨

 

 

연결되어있으니까 3002에서 연속 2개 추가해도 업데이트 됨

 

 

 

 

 

 

 

3001에서 블록채굴해도 마찬가지로 3002에서 업뎃됨 

연속으로 4,5 값 넣어서 블록추가함


서버 통신 코드

 

 

 

httpServer.js

post로 보내면 

data로 이름지어서 "data" : 웹소켓 주소 가능한거임

 

이 웹소켓 주소 data가 connectToPeers함수로 들어감 얘가 웹소켓 연결해줌

P2PServer.js

 

소켓에 넣고 메세지 핸들러 함수쓰고

얘네가 보내주는거 관리하는거임 

 

 

메세지 핸들러는 이렇게 여기서 변수로 지정해줬음 

(그 검증할 때 변수 10으로 다 잡아서 블록10개 이전과 비교해서 생성시간 알아내는 거 있잖아

이거요 blockchain.js

걔랑 비슷한 원리인듯

신기한게 컴퓨터는 바보라서 걍 숫자 넣어서 계산하는 거기 때문에 시간인지 뭔지 안적어도 가능했음)

 

 

대신 쓸때는 위처럼 MessageType.QUERY_LATEST 이런식으로 해야 얘가 찾겠지?

 

 

연결됐을 때 채굴하면 broadcast로 연결되어있는애들 전부한테 뿌려주는 거

(근데 생각해보니까 마지막 최신블록 담아서 보내주는거 말고도 아예 많이 다르면 전체 교체해야되는 replace함수 써야되는데 broadcast(initMessageHandler(ws)) 이걸로 해줘야 되는거 아닌가....? 흠 정리하고 다시 수정해봐야겠다. )

 

 

 

중간에 맞이했던 에러들

브로드캐스트에 저 함수를넣지 않은 것 말고도 있음

 

갑자기 이거 뜸

(node:11496) Warning: Accessing non-existent property 'Blocks' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:11496) Warning: Accessing non-existent property 'getLastBlock' of module exports inside circular dependency                                                                     cular dependency
(node:11496) Warning: Accessing non-existent property 'createHash' of module exports inside circular dependency                                                                       de circular dependency
(node:11496) Warning: Accessing non-existent property 'isValidTimestamp' of module exports  circular dependency
inside circular dependency

이게 찾아보니까 순환참조? a스크립트에서 만든 함수 내보내고 b에서 a의 함수 가져왔는데 또 가져와서? 생기는 뭐 중복돼서 그런거라고 했는데 

이전에 안뜨다가 갑자기 떠서 당황... 작동은 되는데 불안하죠잉

 

결국 전역변수에 있는 애들 다 지역변수로 넣어서 함... 

 

이런식으로.. 

 

 

 

 

 

 

아 그리고 블록체인 에 

 

이 부분 틀렸다는데 

흠 내가 이해한 거는 새블럭을 현재 시간이랑 어느정도 오차범위까지는 인정하는 코드라고 봤거든 책도 그 얘기고..

 

 

흠..어렵군!!

 

 

 

 

 

728x90
728x90

참고로 나는 이 두 에러가 스벅의 부실한 와이파이 때문이었음.. 

응아니고 mariadb 실행안해서였음요!!  기존에 쓰던 mysql는 초기설정으로 자동실행되게 설정해놓은 거라 마리아디비는 쓸라면 실행해줘야됐음!!!!!! ()

 

ConnectionError [SequelizeConnectionError]: connect ETIMEDOUT 

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)   

 

 


+자동실행되는 설정

출처:&nbsp;&nbsp;https://codecoco.tistory.com/category

 


db연결하는 법 

mysql과 같음 이 코드 mysql연결하는거 보고 한거임(시퀄라이즈가 다른 sql데이터베이스들도 호환가능해서)

출처는 책임 | Node.js 교과서 | 조현영 p.315

 

 

 

package.json에 npm start하기 위해 넣기

start 노드몬 앱 넣기

시작할거를 main에 app.js로 

{
  "name": "4th-project-yunminblock",
  "version": "1.0.0",
  "description": "소윤이 보아라",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon app"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.2",
    "morgan": "^1.10.0",
    "mysql2": "^2.3.3",
    "nunjucks": "^3.2.3",
    "sequelize": "^6.13.0",
    "sequelize-cli": "^6.3.0",
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

 

시퀄라이즈에 필요한 모듈패키지설치

npm i express morgan nunjucks sequelize sequelize-cli mysql2

npm i -D nodemon

 

 

설치 완료 후

 

npx sequelize init

 

하면 

config, models, migrations, seeders폴더가 생성됨

models폴더 안에는 index.js가 있음

 

몇개는 지금 블록체인중이라서 넣은거라 안따라해도 됨

index.js 를 이렇게 수정고우

const Sequelize = require("sequelize");
const env = process.env.NODE_ENV || "development";
const config = require(__dirname + "/../config/config.json")[env];
const db = {};

//기존
const sequelize = new Sequelize(
  config.database,
  config.username,
  config.password,
  config
);

db.sequelize = sequelize;

module.exports = db;

 

이제 시퀄라이즈 통해 express앱과 mysql연결할거임

app.js 파일 만들고 안에 코드 넣기

const express = require("express");
const path = require("path");
const morgan = require("morgan");
const nunjucks = require("nunjucks");

const { sequelize } = require("./models");

const app = express();
app.set("port", process.env.PORT || 3307);
app.set("view engine", "html");
nunjucks.configure("views", {
  express: app,
  watch: true,
});

sequelize
  .sync({ force: false })
  .then(() => {
    console.log("데이터베이스 연결성공");
  })
  .catch((err) => {
    console.log(err);
  });

app.use(morgan("dev"));
app.use(express.static(path.join(__dirname, "public")));
app.use(express.json());

app.use(express.urlencoded({ extended: false }));

app.use((req, res, next) => {
  const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
  error.status = 404;
  next(error);
});

app.use((err, req, res, next) => {
  res.locals.message = err.message;
  res.locals.error = process.env.NODE_ENV !== "production" ? err : {};
  res.status(err.status || 500);
  res.render("error");
});

app.listen(app.get("port"), () => {
  console.log(app.get("port"), "번 포트에서 대기중");
});

마리아db포트가 3307로 되어 있어서 이렇게 했는데

사실 상관없는거같긴함 (서버는 계속 헷갈리는데 db서버가 3307포트인건 알겠고 여기서 넣는 포트는 아마 localhost:로 화면에 띄워주는 포트라고 이해함요..네.. )

여기서 3001로 바꿔도 잘됨 config에 port주소가 3307로 잘들어가 있기만 하면 문제없는듯 그거는 마리아db연결용 이건 보여주는포트용으로이해햇음

 

 

이제 중요한거.

config 내용임!!!

 

나는 마리아 디비쓰니까 그 유저네임이랑 그 비번 그 host로 적어줘야 한다

마리아 디비의 port랑

그래서 이렇게 했다

{
  "development": {
    "username": "mariatest",
    "password": "1234",
    "database": "mariadb",
    "host": "localhost",
    "dialect": "mysql",
    "port": "3307"
  },
  "test": {
    "username": "mariatest",
    "password": "1234",
    "database": "database_test",
    "host": "0.0.0.0",
    "dialect": "mysql"
  },
  "production": {
    "username": "mariatest",
    "password": "1234",
    "database": "database_production",
    "host": "0.0.0.0",
    "dialect": "mysql"
  }
}

아마 두세번째 애들은 상관없을듯 혹시나 해서 저렇게 넣어둔거임

 

 

 

이제 npm start하면 끝!!!

 


 

 

사실 위에도 말했지만 나는 연결안된다고 에러떴었음

ConnectionError [SequelizeConnectionError]: connect ETIMEDOUT
    at ConnectionManager.connect

 

암튼 이거 몰라서 mariadb들어가서 확인해보려는데 접근이 안됨

 

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

 

 

sudo service mysql restart

이것도 해보고 계속 안되다가 

+아냐 이게 실행이야 이거 하니까 된거임!!

+

 

 

sudo mysql -u root -p

로 하니까 들어가짐

 

들어가서 문제 있나 확인함

 

흠... config랑 비교해봄 

user mariatest 인지 host localhost로 되어있나..

 

npm start했을 때 db없다길래 

npx sequelize db:create

로 생성하고

실행

npm start

 

 

안에도 잘있군

 

얏호

 

 

 

https://velog.io/@pier101/Linux-wsl-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EB%A7%8C%EB%93%A4%EA%B8%B0nginx

 

[Linux] wsl 환경에서 로그인,회원가입 만들기(+nginx)

wsl 환경에서 node.js+react(+nginx)를 활용해 웹페이지를 만들어 보면서 ubuntu 환경에서 서버를 어떻게 구축하는지에 대한 감을 익히고 서버 구성에 대해 알아볼 것이다.📢본 작업은 wsl상에서 서버를

velog.io

 

 

 

 

 

728x90
728x90

https://medium.com/@dongha.sohn/bitcoin-8-%ED%95%A9%EC%9D%98-consensus-90e879d80b16

 

Bitcoin#8: 합의(Consensus)

이전의 글들을 통해 거래의 구조와 검증방식, 블록의 구조를 살펴보았다.

medium.com

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=gitacademy01&logNo=222027103524 

 

P2P 네트워크를 이용한 블록체인

안녕하세요 여러분 :) 오늘은 블록체인에 대해서 알아보려고 합니다. 얼마 전 만난 AI 연구를 하시는 분...

blog.naver.com

(이거는 비교조건/p2p개념 링크)

 

주의!

 

나름의 이해라서 백퍼 맞지 않을 수 있음ㅎㅎ

(이해를 돕기 위해 설명해준 학우분 감사합니다.)

 

수업에서 블록생성하고 연결하고 검증하고 서버열어서 블록보여주고 이런 걸 해봤는데

 

드는 생각이 왜 p2p를 하지? 였음

노드간의 통신을 작동해야된다는데

 

 

local3001 서버 열고 여기서 블록생성하고 검증하면 여기가 중앙서버고

노드, 참여자용 서버로 만든 6001, 6002,6003은 블록 생성해서 중앙서버가 검증자해주는 건가? 

잉 그러면 탈중앙화가 아닌데? 혼돈;;;

 

내가 이해를 잘못하고 있었음

 

 

 

"먼저 블록 채굴에 성공한 채굴자는 해당 블록을 주변에 전파하게 된다. 중요한 사실은, 이때 전파하는 블록은 채굴에 성공했다고 확신할 수 없는 블록이며 검증을 거쳐 블록체인에 연결되어야 한다. 거래는 노드들에게 전파되며 검증의 과정을 거치듯, 블록 또한 검증을 거치게 되며 검증의 조건은 다음과 같다....."

 

 

우리 p2p하려고 웹소켓 써서 주고받는 통신하잖아

그 전에는 기본 포트는 컴퓨터 내에서 물리적으로 연결되어있으니까 접속이 가능한거고

 

웹소켓으로 내 컴퓨터를 3개의 컴퓨터로 분리하다고 치고 6001, 6002, 6003에 하나씩 컴퓨터가 있고

실제로는 하나니까 물리적으로 3001에 연결될 수 있는거라 서버는 3001만 열어도 될거같기도하고..

 

암튼 결론은!

내가 연 3001은 그냥 이 모든게 보이는 용도로만 쓰이는 보여주기용 서버이고 

실제로 블록 생성, 검증은 

노드들이 하는거임

노드들의 서버 6001, 6002, 6003

그니까 얘네끼리 통신하는 p2p서버로 하는거지.

 

처음에 시작했을 때 

제네시스 블록을 모든 노드들이 갖고 시작하고 

그 뒤에 줄줄이 블록을 생성하는 거임

 

이제 검증과정을 거쳐서 서로블록을 비교하고

황금블록 발견한 애꺼가 당첨되면 원본 블록체인에 연결되고

나머지는 허탕치고 다시 다음블록 만드는 거임

 

 

 

 

그 checkedValid 코드부분은 

//블록구조 유효한지
//현재 블록의 인덱스가 이전 블록의 인덱스보다 1만큼 큰지
//이전블록의 해시값과 현재 블록의 이전해시가 같은지
//데이터 필드로부터 계산한 머클루트와 블록헤더의 머클루트가 동일한지
//이 조건 다 맞으면 올바른 구조체이다

 

이 조건들로 블록이 진짜인지 증명하는거고

진짜 블록이어도 아직 당첨은 안된거지 원본에 연결안된거

 

이제 비교해서 당첨될라면

p2p끼리 통신..

누구꺼를 연결할걸로 할지...

 

chainedBlock에 

replaceChain 함수 주석처리 해놨는데 이거가 

p2pServer.js에서 이렇게 함수호출되어있음

이걸로 누구꺼 연결할지 찾는거 아닐까 짐작..

 

블록들 비교까지 한다면

 어떻게 하는지는 모르겠다 어렵다!!!

 

 

아무튼 해야되는거는 

 

블록마이닝(채굴)/ 지갑생성/ 현재 상황 시각화 / DB 채굴 저장

 

사실상 지갑생성까지 수업때 한거긴한데

 

위같은 비교검증은 없어서

완전한 블록체인은 아닌거같고..흠 

 

 

 

 

 

 

 

 

 

 

 

728x90
728x90

어제 학원사람들이라 하면서 깨달은 점이랑 정리못했던 서버구축 내용 써보자~

서버 구축 코드는 이거 보면서 함요 https://blckchainetc.tistory.com/m/332

 

[119일차] 블록체인 네트워크 웹소켓 http ws 웹서버 구축

P2P (Peer to Peer) 구현 방법 -> WebSocket -> socket.io = 웹소켓으로 웹으로 구성할 때 필수적인 구성을 미리 만들어 놓은 패키지  이전 node.js chatting을 만들 때 사용함  기본 기능 외 여러가지 기능이..

blckchainetc.tistory.com

 

p2p 구현하기 위해서 서버 구축을 해보자!

(p2p peer to peer 노드, 참여자들간의 통신을 의미한다고 생각하면 됨!)

ws, 웹소켓쓸거임

 

블록체인은 대략적으로 크게 두개의 포트가 빌요한데

1. 중앙서버! 

서버- 클라이언트 

걍 작업, 블록생성한거 보여주는 데라고 이해함

(블록생성하고 작업하는 서버라고 생각했는데 검증과 생성을 서버 나눠서 따로 할 수도 있겠네.. 아니다 서버 안나누고 실행만 분리해도 될거같고..)

2. 노드끼리 통신

 

웹서버 구축의 기초 작업! 셋팅 시작!!

1. server - client http 서버 만들기

npm i express로 설치

 

//HTTP Server 초기화,P2P Server 초기화, 지갑 초기화
//(사용자와 노드간의 통신)

//client http 서버 만들기 express 사용
const express = require("express");
const app= express();
const port = process.env.PORT ||3000
const bodyParser = require('body-parser')
//블럭 가져오기
//현재 있는 블록들 가져와주는 함수
const {getBlocks} = require('./chainedBlock.js');

//바디파서 써서 json으로 볼예정임
app.use(bodyParser.json());
//blocks버튼임
app.get('/blocks',(req,res)=>{
    res.send(getBlocks())
})
app.listen(port,()=>{
    console.log(`server start port ${port}`)
    
})

서버 3000 잘 열렸고 

 

포트 변경도 해봄

(여기서 PORT변수로 명령어로 변수 값 변경한다고 생각하면 됨)

다시 3000으로 돌아와서

 

서버에 블록 가져와 보자!!

 

chainedBlock.js 에 만들었던 (이전 글에 코드 있음요)

blocks를 가져올거야. 

(chainedBlock.js에서 module.export로 getBlocks내보내기 해서 가져올 수 있음)

//현재가지고 있는 블록가져오는 함수
const {getBlocks} = require('./chainedBlock.js');

이걸로 함수 호출해서 기존 블록가져옴

app.get('/blocks',(req,res)=>{
    res.send(getBlocks())
})

 

 

이제 get잘되는지 확인해보자!

curl -X GET http://localhost:3000/blocks
curl http://localhost:3000/blocks -X GET   (요것도 가능!)

아 근데 curl 자체 의미하는게

"curl은 사용자 상호 작용 없이 작동하도록 설계된 서버에서 또는 서버로 데이터를 전송하기 위한 명령줄 유틸리티입니다"

그니까 명령어로 포스트맨,ARC같은거 안써도 볼 수 있는건데

나는 썼다

advanced REST client 확장자 설치해서 사용했음

 

 

여기서도 확인할 수 있겠죠

 

 

아!! 참고로 get post 알다가도 모르게 계속 헷갈렸는데 

다시금 이해함

 

post는 데이터 전송하고

get은  정보를 요청하는거! 그니까 버튼같은 역할이라고 생각하면 쉬움

이거 누르면 원하는 정보들이 보여지는거임

 

 

/blocks로  get으로 요청했으니까 

기존 제네시스 블록이 보이지

 

 

 

그럼 데이터 전송, 즉 추가하는 거 해보자

 

블록을 추가하는 것을 웹서버에서 해보자고

 

post로 해야겠지

 

//HTTP Server 초기화,P2P Server 초기화, 지갑 초기화
//(사용자와 노드간의 통신)

//client http 서버 만들기 express 사용
const express = require("express");
const app= express();
const port = process.env.PORT ||3000
const bodyParser = require('body-parser')
//블럭 가져오기
const {getBlocks,nextBlock} = require('./chainedBlock.js');
const {addBlock}= require('./checkValidBlock')

//바디파서 써서 json으로 볼예정임
app.use(bodyParser.json());
//blocks버튼임
app.get('/blocks',(req,res)=>{
    res.send(getBlocks())
})
app.listen(port,()=>{
    console.log(`server start port ${port}`)
    
})
//블록추가하기
app.post('/mineBlock',(req,res)=>{
    const data = req.body.data || []
    console.log(data)
    const block = nextBlock(data)
   addBlock(block)
   res.send(getBlocks())
})

바디데이터(아니면 빈배열)를 데이터로 이름 짓고 

다음블록생성하는 함수에 데이터 넣음

 

데이터 넣은 걸 block으로 이름 짓고 

그 블록을 블록배열에 추가한다!

(참고로 addBlock함수는 checkedValied에서 가져왔음)

그리고 getBlock함수로 지금 블록배열다 보이게 한다.

 

data로 변수 설정해서 "data"라고 하는 거임

 

 

이제 get요청인 blocks버튼 눌러보면요

배열에 추가된거 볼 수 있다.

 

 

 

 

좀 헷갈리는 거는 

chainedBlock에도 addBlock함수가 있고

checkedValid에도 addBlock함수가 있음

 

그래서

그냥 연결하는 체인드블록에서걸로

addBlock가져와서 쓰니까 오류났었음

블록구조가 유효한지 확인해야돼서 그런것도 있는데

둘 차이가 넣는 값이 다른거같은데 

bodyDate여서 그런가...흠 여긴 잘 모르겠군!

728x90
728x90

https://gdtbgl93.tistory.com/63

 

Git push가 안되는 경우 (fatal: refusing to merge unrelated histories)

로컬 저장소에 있는 프로젝트를 깃허브 사이트를 통해 만든 저장소로 push 하는 경우에 이런 메세지가 뜨는 경우가 있다. 1 2 3 4 5 6 7 8 C:\Users\gitProject>git push origin master To https://github.co..

gdtbgl93.tistory.com

 

 

push 안되길래 

pull먼저 해보려는데 다 안되길래

 

1
git pull origin 브런치명 --allow-unrelated-histories
cs

이거 하니까 되더라 

 


 

깃 데스크탑써서 큰 걱정은 없지만

 

 

 

 

 

이것도 한번 읽어봤다..

origin/branch 랑

branch 뭐가 다른지..

 

흠 대충 이해해서는 

branch는 내 로컬이고 

origin/branch가 깃헙에 넘어가는 거같음요..

 

"여기에서 branch_name은 로컬 분기인 반면, origin/branch_name은 원격 추적 분기입니다. 원점에 있는 해당 분기의 상태를 반영합니다..."

 

하지만 다 이해하진 못했다!

 

https://stackoverflow.com/questions/26125162/difference-between-origin-branch-name-and-branch-name

 

difference between origin/branch_name and branch_name?

for pushing to bitbucket. If I do: git push origin origin/branch_name my commits are not pushed. Total 0 (delta 0), reused 0 (delta 0) If I do git push origin branch_name my commits are pushed:

stackoverflow.com

 

728x90
728x90

먼저 꿀팁을 나눠준 학우들에게 감사합니다..

 

박수 짝짝짝

 

평소하던데로 

깃허브에서 

레포지토리 만들고 

주소 복사한다음에

 

깃데스크탑에 들어와서 

요기서 깃클론하고

 

작업할 때 

오픈 비주얼 코드 누르고 

터미널 켜져 있으면 

그걸로 하고 아니면 새 터미널들어가서 켜고

 

 

아래 보면

터미널이 원래 cmd로 되어있을건데

 

이걸 우분투로 바꿔주면

 

짠 

다가려놔서 안보이겠지만

wsl 작업창이 됐고요

/mnt 경로가 cd

커밋도 잘되고요

 

 

 

 

 

 

/mnt 경로는 바로

mnt

mnt는 mount의 약자 뜻을 가진 경로. 리눅스 프로그램에 기기의 탈부착으로 임시로 연결된 경로가 생기면 해당 경로에서 확인할 수가 있습니다. 현재 프로젝트의 AWS에는 연결된 외부 장치나 시스템이 없으므로 아무런 경로가 나오지 않습니다. 하지만 시스템의 용량을 확장할 때나 WSL에서 윈도우 시스템을 인식할 때 해당 경로로 인식할 수가 있습니다.

//WSL에서의 윈도우 인식
///mnt 경로
c d wsl

여기서 C는 Windows에서의 C 드라이브를 의미합니다.

출처: https://tecoble.techcourse.co.kr/post/2021-10-18-linux-file-directory-system/

 

 

 

 

728x90
728x90

겨우됐다 지갑생성하기....

 

chainedBlock에 

return을 빼먹어서 였음...

 

chainedBlock.js 코드전체

더보기
const fs = require('fs')
const merkle = require('merkle')
const cryptojs =require('crypto-js') 
// const { randomBytes } = require('crypto')
const random = require('random')
const { get } = require('http')

//예상 채굴 시간과 난이도 조절 단위수를 변수로 설정한다
const BLOCK_GENERATION_INTERVAL = 10  //second
const DIFFICULT_ADJUSTMENT_INTERVAL = 10 //in blocks




//블럭 형태 (헤더, 바디)
class Block{
        constructor(header, body){
		this.header = header
		this.body = body
	}
}

class BlockHeader{
        constructor(version,index, previousHash, timestamp, merkleRoot,difficulty, nonce){
	     this.version = version
	     this.index = index
	     this.previousHash = previousHash
	     this.timetamp = timestamp //블럭만들어진 시간
	     this.merkleRoot = merkleRoot
	    //  this.bit = bit
		this.difficulty = difficulty //채굴난이도. 아직안씀
	     this.nonce = nonce //넌스(문제풀기위해 대입한 횟수) 아직 안씀

	}
}

//버전계산하는 함수 
function getVersion(){
	const package = fs.readFileSync("package.json")
	// console.log(JSON.parse(package).version)
	return JSON.parse(package).version

}


//getVersion()

function createGenesisBlock(){
	const version = getVersion()
	const index= 0 //맨처음이라 인덱스0
	const previousHash = '0'.repeat(64) //sha256암호가 64자리니까 0을 64자리로 바꿔줌
	// const timestamp = parseInt(Date.now()/1000)
	//비트코인 날짜로..비트코인최초탄생일 2009/01/03 6:15pm (UTC)
	const timestamp = 1231006505  
	const body = ['제네시스블록 바디임요']
	const tree = merkle('sha256').sync(body) //바디값불러와서 sha256으로 암호화
	const merkleRoot = tree.root() || '0'.repeat(64)
	//루트값없으면 || 뒤에값 출력
	const difficulty = 0 //헤더값에 난이도 아직 0임
	const nonce = 0

	// console.log("version : %s, timestamp: %d, body : %s",version,timestamp,body)
	// console.log("previousHash : %d", previousHash);
	// console.log("merkleRoot : %d", merkleRoot);

	const header = new BlockHeader(version,index, previousHash, timestamp, merkleRoot, difficulty,nonce)
	return new Block(header, body)

}

//const block = createGenesisBlock()
//console.log(block)


//블록저장할수있는애들, 여러개 들어갈 수 있는 배열을 만들어줌
let Blocks = [createGenesisBlock()]


//현재 있는 블록을 다 리턴해주는 함수, 블럭목록 부르는 함수
function getBlocks(){
	return Blocks
}
//제일 마지막에 만든 블록 가져오기
function getLastBlock(){
	//길이 1이니까 1-1 =0 즉 첫번째배열 불러와 
	return Blocks[Blocks.length - 1]

}

//data에는 블록이 들어오는거임, 이블록을 가지고 해시값을 만들어내는 것임
// function createHash(data){
// 	const {version, index,previousHash,timestamp,merkleRoot,difficulty,nonce}= data.header
// 	const blockString = version + index + previousHash + timestamp + merkleRoot + difficulty + nonce
// 	const hash = cryptojs.SHA256(blockString).toString()
// 	return hash
// }
function createHash(data){
	//인자로 받은 것중에 헤더를 뽑아내서
	const {version, index, previousHash, timestamp, merkleRoot, difficulty, nonce} = data.header
	const blockString = version + index + previousHash + timestamp + merkleRoot + difficulty + nonce
	//다 합쳐서 해시로 만들고 리턴
	const hash = cryptojs.SHA256(blockString).toString()
	return hash
}

function calculateHash(version, index, 
		previousHash,timestamp,merkleRoot,difficulty,nonce){
//헤더의 값에 nonce값을 추가해서 모두 더한 string을 가지고 암호화 
//한 결과 hash를 내보낸다
	const blockString = version + index + previousHash + timestamp + merkleRoot + difficulty + nonce
	const hash = cryptojs.SHA256(blockString).toString()
	return hash
}
// const genesisBlock =createGenesisBlock()
//const testHash = createHash(block)
// console.log(genesisBlock)


//다음블록 만들었을 때 기존 블록 정보 가져와
function nextBlock(bodyData){
	//마지막 블럭, 이전블록으로
	const prevBlock = getLastBlock()
	const version = getVersion()
	const index = prevBlock.header.index + 1
	//이전 블록의 해시값
	const previousHash = createHash(prevBlock)
	const timestamp = parseInt(Date.now()/1000)
	const tree = merkle('sha256').sync(bodyData)
	const merkleRoot = tree.root() || '0'.repeat(64)
	//난이도 조절하는 함수 추가 
	const difficulty = getDifficulty(getBlocks())
    // const nonce = 0
    // const header = new BlockHeader(version, index, previousHash,timestamp,merkleRoot,bit,nonce)
	const header = findBlock(version, index, 
		previousHash,timestamp,merkleRoot,difficulty)
	return new Block(header,bodyData)


}
// const block1 = nextBlock(["tranjaction1"])
// console.log(block1)


function addBlock(bodyData){
	const newBlock = nextBlock(bodyData)
	Blocks.push(newBlock)
}
// addBlock(['transaction1'])
// addBlock(['transaction2'])
// addBlock(['transaction3'])
// addBlock(['transaction4'])
// addBlock(['transaction5'])
// console.log(Blocks)

//0103
// function replaceChain(newBlocks){
// 	if (isValidChain(newBlocks)){
// if 	((newBlocks.length> Blocks.length) ||
// (newBlocks.length=== Blocks.length) && random.boolean()){
// 	Blocks = newBlocks
// 	broadcast(responseLatestMsg())
// 	} 
// }
// 	else {
// 		console.log("받은 원장에 문제가 있음")
// 	}
// }
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'
	}

	let ret = "";
	for(let i = 0; i < s.length; i++){
		if (lookupTable[s[i]]) {
			ret += lookupTable[s[i]];
		}
		else { return null; }
	}
	return ret;
}
function hashMatchesDifficulty(hash, difficulty) {
	//difficulty를 이용해 만든 조건을 만족하는지 hash값과 대조해
	//조건에 해당되면 블록 생성
	const hashBinary = hexToBinary(hash.toUpperCase())
	 //difficulty 난이도가 높아짐에 따라 0개수가 늘어남 
	const requirePrefix = '0'.repeat(difficulty)
	//높으면 높을수록 조건을 맞추기가 까다로워짐(nonce값과 time값이 바뀌면서 암호화값이 달라진다.)
	return hashBinary.startsWith(requirePrefix)

}

function findBlock(currentVersion, nextIndex, previousHash, nextTimestamp,
	merkleRoot, difficulty) {
		//calculateHash값이 조건이 맞을때까지 while문으로 반복
		//조건문 반복할때마다 nonce값 증가
		let nonce = 0;
		while (true) {
			var hash = calculateHash(currentVersion, nextIndex, previousHash, nextTimestamp,
				merkleRoot, difficulty,nonce)
				if (hashMatchesDifficulty(hash,difficulty)){
					return new BlockHeader(currentVersion, nextIndex, previousHash, nextTimestamp,
						merkleRoot, difficulty,nonce)

				}
				nonce++ ;
				
		}
	}

	function getDifficulty(blocks){
		const lastBlock = blocks[blocks.length -1]
		if (lastBlock.header.index !==0 && lastBlock.header.index
			% DIFFICULT_ADJUSTMENT_INTERVAL === 0){
				//마지막 블럭헤더인덱스가 0이 아니고
				//난이도 조절수만큼 나누고 나머지가 0이면

				//난이도 조정함수 실행
				return getAdjustDifficulty(lastBlock,blocks)
			}
			//난이도 리턴
			return lastBlock.header.difficulty
	}

	function getAdjustDifficulty(lastBlock, blocks){
     // 지금 블록에서 난이도 조절 단위 수만큼의 전 블록과의 time 
	 //즉, 생성시간을 비교해서 자신의 예상 시간보다 느리거나 빠르면 난이도를 조절한다.
    //적당하면 난이도가 유지되고 블럭의 생성시간이 느리면 난이도를 낮추고, 빠르면 난이도를 높인다.
		const preAdjustmentBlock = blocks[blocks.length - DIFFICULT_ADJUSTMENT_INTERVAL];
		//시간
		const elapsedTime = lastBlock.header.timestamp - preAdjustmentBlock.header.timestamp
		const expectedTime = BLOCK_GENERATION_INTERVAL * DIFFICULT_ADJUSTMENT_INTERVAL;

		if (elapsedTime/2 > expectedTime) {
			return preAdjustmentBlock.header.difficulty +1;
		}
		else if (elapsedTime * 2 < expectedTime){
			return preAdjustmentBlock.header.difficulty -1 
		}
		else {
			return preAdjustmentBlock.header.difficulty
		}
	}

	function getCurrentTimestamp(){
		//Math.round 반올림함수
		return Math.round(Date().getTime()/ 1000);
	}

	function isValidTimestamp(newBlcok, prevBlock){
		if (newBlock.header.timestamp - prevBlock.header.timestamp > 60)
			return false
		if (getCurrentTimestamp()- newBlock.header.timestamp > 60)
		    return false
		return true
		

	}




module.exports = { hashMatchesDifficulty, isValidTimestamp, getBlocks, createHash,
	 Blocks, getLastBlock, nextBlock, addBlock, getVersion, 
	 createGenesisBlock };    //내보내주는거

httpServer.js

더보기
const express = require("express")
const bodyParser = require("body-parser")
const {getBlocks, nextBlock,getVersion} = require('./chainedBlock.js')
const {addBlock}= require('./checkValidBlock')
const { connectToPeers, getSockets } = require("./p2pServer.js")
const {getPublicKeyFromWallet, initWallet} = require("./encryption")

const http_port = process.env.HTTP_PORT || 3001

function initHttpServer(){
	const app = express()
	app.use(bodyParser.json())
	//추가
	app.post("/addPeers", (req,res)=>{
		const data = req.body.data || []
		console.log(data);
		connectToPeers(data);
		res.send(data);

	})
	app.get("/peers", (req, res)=> {
		let sockInfo = []

		getSockets().forEach(
			(s)=>{
				sockInfo.push(s._socket.remoteAddress+":"+s._socket.remotePort)
			}
		)
		res.send(sockInfo)
	})

	app.get("/blocks",(req,res)=>{
 
		res.send(getBlocks())

	})
	app.get("/version",(req, res)=>{
		res.send(getVersion())
	})

	app.post("/mineBlock",(req,res)=>{
		const data = req.body.data || []
		console.log(data)
		const block = nextBlock(data)
		addBlock(block)
		// res.send(block)
		res.send(getBlocks())
	})

	app.post("/stop", (req,res)=>{
		res.send({"msg":"Stop Server!"})
		process.exit()
	})
	app.get("/address", (req,res)=>{
		initWallet()
		const address = getPublicKeyFromWallet().toString();
		console.log(getPublicKeyFromWallet())
		if(address != "") {
			res.send({"address" : address})
		}
		else {
			res.send("empty address!")
		}
	})
	app.listen(http_port,()=>{
		console.log("Listening Http Port : "+ http_port)
	})


}

initHttpServer()

 

encryption.js

더보기
const fs = require("fs")
//타원곡선개념을 사용한 디지털 터널알고르짐을 짤거임
//타원 곡선 디지털 서명 알고리즘
const ecdsa = require("elliptic")
const ec = new ecdsa.ec("secp256k1")

const privateKeyLocation = "wallet/"+
(process.env.PRIVATE_KEY || "default");
const privateKeyFile = privateKeyLocation + "/private_key"


function initWallet(){
    if (fs.existsSync(privateKeyFile)){
        console.log("기존 지갑 private key 경로 :" + privateKeyFile)
        return;
    }
    if (!fs.existsSync("wallet/")){
        fs.mkdirSync("wallet/")
    }
    if (!fs.existsSync(privateKeyLocation)){ //wallet/default 파일없다면
        fs.mkdirSync(privateKeyLocation)
        //만들어줘.
       
    }
    if (!fs.existsSync(privateKeyFile)){//프라이빗키없으면
        console.log('주소값 키값을 생성중')
        const newPrivatekey = generatePrivatekey()
        fs.writeFileSync(privateKeyFile, newPrivatekey)
        console.log('개인키 생성이 완료됐습니다')

    }
    const newPrivatekey = generatePrivatekey()
    fs.writeFileSync(privateKeyFile,newPrivatekey)
    console.log("새로운 지갑 생성 private key 경로 :" + privateKeyFile)

}

// initWallet()

//비밀키 생성
function generatePrivatekey(){
    const keyPair = ec.genKeyPair();
    const privateKey = keyPair.getPrivate();
    //16진수로 만들어서 리턴
    return privateKey.toString(16);
}
//비밀키(인증서) 출력하는 함수
function getPrivateKeyFromWallet(){
    //지갑에 만들어놓은 걸 읽을 수 있게 해줌
   const buffer = fs.readFileSync(privateKeyFile,"utf8")
   return buffer.toString();
}
//공개키(지갑주소) 만들기
function getPublicKeyFromWallet(){
    const privateKey = getPrivateKeyFromWallet();
    const key = ec.keyFromPrivate(privateKey, "hex");
    return key.getPublic().encode("hex")
}

module.exports= {getPublicKeyFromWallet,initWallet}

 

 

 

node node httpServer.js

로 실행.

 

 

 

 

 

 

 

1. 암호화 종류

단방향 암호화 : 오직 암호화만 가능

양방향 암호화 : 암호화하고 다시 복호화 가능

 

양방향에 경우 당사자 2명을 제외한 나머지 사람들이 몰라야하므로 

비밀키를 정한다. 하지만 이 비밀키를 알 수 있기 때문에 당사자 2명 각각 서로 다른 키를 가진다. 

비대칭키 암호(공개키,비밀키)

 

 

2. 공개키 알고리즘

 

공개키 알고리즘은 크게 두 가지 방식으로 분류됨

1. 소인수분해 문제를 이용해서 만들어지는 것

2. 이산 로그 문제를 이용해서 만드는 방법 입니다. (타원곡선 )

 

 

 

3. 디지털 서명

 

 

4. ECDSA 알고리즘

타원곡선 암호를 전자서명에 접목시킨 암호 알고르짐

비대칭키 암호에 해당

secp256k1 라는 기준에 맞춰진 방정식 사용

발신자에 의한 서명시 사용되는 개인키와 수신자가 발신자의 서명을 사용하는 공개키 한 쌍을 갖음

 

https://server-engineer.tistory.com/586

https://rosypark.tistory.com/108

 

 

 

 

코드 설명잘해놓음

 

https://velog.io/@nara7875/BlockChain%EC%9E%91%EC%97%85%EC%A6%9D%EB%AA%85%EB%A7%88%EC%9D%B4%EB%8B%9D

728x90
728x90

출처: node.js 교과서

 

웹소켓은 실시간 양방향 데이터 전송을 위한 기술이다. 

http와 다르게 ws라는 프로토콜을 사용한다.

노드에서는 ws나 Socket.IO 같은 패키지를 통해 웹소켓을 사용할 수 있다.

 

 

웹소켓이 나오기 이전에는 http기술을 사용하여 실시간 데이터 전송을 구현했다.

그중 한가지가 폴링polling이라는 방식인데 http가  클라이언트에서 서버로 향하는 단방향 통신이므로 주기적으로 서버에 새로운 업데이트가 있는지 확인하는 요청을 보낸 후 , 있으면 가져오는 단순방식!

 

그러다가 HTML5가 나오면서 웹브라우저와 웹서버가 지속적으로 연결된 라인을 통해 실시간으로 데이터를 주고 받을 수 있는 웹소켓이 등장!

웹소켓이 연결되면 계속 연결된 상태로 있음

따로 업데이트가 있는지 요청을 보낼 필요가 없음

 

업데이트할 내용이 생겼다면 서버에서 바로 클라이언트에 알린다. http프로토콜과 포트를 공유할 수 있어서 다른 포트에 연결할 필요도 없다

 

참고로 

서버센트 이벤트 Server Sent Event (SSE)라는 기술도 등장함

처음에 한번만 연결하면 서버가 클라이언트에 지속적으로 데이터 보냄

웹소켓과 다른점은 클라이언트에서 서버로는 데이터를 보낼 수 없다는 것. 즉 단방향통신

 

웹소켓만이 진정한 양방향 통신

 

 

 

 

 

728x90
728x90

다음과 같은 조건을 거쳐야함.

 

1. 블록구조가 유효한지

2. 현재 블록의 인덱스가 이전 블록의 인덱스보다 1만큼 큰지

3. 이전블록의 해시값과 현재 블록의 이전해시가 같은지

4. 데이터 필드로부터 계산한 머클루트와 블록헤더의 머클루트가 동일한지 

 

이 조건 다 맞으면 올바른 구조체이다.

 

 

1.블록구조가 유효한가

//블록의 각각 타입체크 블록구조 유효한지 체크하는 함수
function isValidBlockStructure(block) {
    return typeof(block.blockheader.version) === 'string'
    && typeof(block.header.index) === 'number'
    && typeof(block.header.previousHash) === 'string'
    && typeof(block.header.timestamp) === 'number'
    && typeof(block.header.merkleRoot) === 'string'
    && typeof(block.body) === 'object'
}
//위 함수를 통해 블록구조의 유효성 검사
function isValidNewBlock(newBlock, previousBlock) {
    if (isValidBlockStructure(newBlock) === false) {
        console.log('Invalid Block Structure')
        return false;
    }
    return true;
}

 

2. 현재 블록의 인덱스가 이전 블록의 인덱스보다 1만큼 큰지

 

//위 함수를 통해 블록구조의 유효성 검사
function isValidNewBlock(newBlock, previousBlock) {
    if (isValidBlockStructure(newBlock) === false) {
        console.log('Invalid Block Structure')
        return false;
        //현재블록이 이전 블록보다 1크지 않으면 false
    } else if (newBlock.header.index !== previousBlock.header.index +1 ){
        console.log('Invalid Index')
        return false;
    }
    return true;
}

3. 이전블록의 해시값과 현재 블록의 이전해시가 같은지

 

나는 새로 만들어서 하는중이라

blockcopy.js 에 블록생성block, 다음블록연결chainedBlock까지 해놨음

여기서 createHash 내보내주기 

 

//이전블록 해시값과 현재 블록해시값의 이전해시가 같은지
//보려고 내보내줘야함 
module.exports = { 
	createHash,
}

이제 현재 코드,

비교해볼 코드에 createHash 가져와야돼

 

(수업때는 checkValidBlock.js 라고 이름지은걸 걍 다시 하면서 checkedBlock.js라고 했닿 )

const {createHash} = require('./blockcopy');

 

//위 함수를 통해 블록구조의 유효성 검사
function isValidNewBlock(newBlock, previousBlock) {
    if (isValidBlockStructure(newBlock) === false) {
        console.log('Invalid Block Structure')
        return false;
    } else if (newBlock.header.index !== previousBlock.header.index +1 ){
        console.log('Invalid Index')
        return false;
        //이전블록해시랑 현재블록의 이전해시가 다르면 false
    } else if (createHash(previousBlock)!==newBlock.header.previousHash) {
        console.log('Invalid previousHash');
        return false;
    }
    return true;
}

4. 데이터 필드로부터 계산한 머클루트와 블록헤더의 머클루트가 동일한지 

 

merkle쓰려면 설치해야돼

 

npm i merkle
const merkle = require("merkle")

....

//위 함수를 통해 블록구조의 유효성 검사
function isValidNewBlock(newBlock, previousBlock) {
    if (isValidBlockStructure(newBlock) === false) {
        console.log('Invalid Block Structure')
        return false;
    } else if (newBlock.header.index !== previousBlock.header.index +1 ){
        console.log('Invalid Index')
        return false;
    } else if (createHash(previousBlock)!==newBlock.header.previousHash) {
        console.log('Invalid previousHash');
        return false;
    } else if (newBlock.body.length === 0 && ('0'.repeat(64) !== newBlock.header.merkleRoot) 
    || newBlock.body.length !== 0 && (merkle("sha256").sync(newBlock.body).root() !==newBlock.header.merkleRoot))
    {
        console.log('Invalid merkleRoot')
        return false
    }
    return true;
}

새블록 body의 길이가 0일 경우 && 머클루트는 '0'.repeat(64)이 아니면

또는 

 

새블록바디 길이가 0이 아닐경우 && 머클루트에있는 정보와  새블록 바디의 루트 정보들이 같지 않으면

false

 

 

5. 새블록추가

chainedBlock.js 하단에 추가

 

나는 blockcopy.js에 추가함요

module.exports = {
	Blocks,
	getLastBlock,
	createHash,
}

checkValidBlock.js 상단에 불러오기

 

나는 checkBlock.js에 넣음

 

const {createHash,Blocks, createHash} = require('./blockcopy');

 

 

 

 

 

 

 

 

검증과정을 마친 블록

 

//검증마친 블록들은 chainedBlock.js의 Blocks배열에 추가한다
function addBlock(newBlock){
    if(isValidNewBlock(newBlock,getLastBlock())){
        Blocks.push(newBlock)
        return true;
    }
    return false;
}

const block = nextBlock(['transaction1'])
addBlock(block)

console.log(block)

 

 

 

blockcopy.js에서의 nextBlock함수 이용해서 

블록생성하고 출력해보기 

 

 

근데 결과에 

 

계속 이게 나와서 

이 콘솔임

 

코드비교해보니까 

blockcopy.js (chainedBlock.js) 에 

index순서 바꾸니까 안뜨네 뭐냐...

blockcopy.js (chained.js)

결과

 

 

 

코드 전체

blockcopy.js

(chainedBlock.js)

더보기
const cryptojs = require('crypto-js')
const fs = require('fs')
const merkle = require('merkle')


class Block{
        constructor(header, body){
		this.header = header
		this.body = body
	}
}

//블록헤더 구조체 선언 : 헤더 구성 요소 나열
class BlockHeader {
	constructor(index, version, previousHash, timestamp, merkleRoot, bit, nonce) {
	  this.index = index
	  this.version = version
	  this.previousHash = previousHash
	  this.timestamp = timestamp
	  this.merkleRoot = merkleRoot
	  this.bit = bit
	  this.nonce = nonce
	}
  }
  

//버전계산하는 함수 
function getVersion(){
	const package = fs.readFileSync("package.json")
	// console.log(JSON.parse(package).version)
	return JSON.parse(package).version

}


//getVersion()

function createGenesisBlock() {
  const index = 0
  const version = getVersion()
  const previousHash = '0'.repeat(64) // 0을 64번 반복
  const timestamp = parseInt(Date.now() / 1000) // 1000 나눈 이유 : 초 단위로 환산하기 위해
  const body = ['Hello block!']
  const tree = merkle('sha256').sync(body)
  const merkleRoot = tree.root() || '0'.repeat(64)
  const bit = 0
  const nonce = 0

  // console.log("version : %s, timestamp : %d, body: %s", version, timestamp, body);
  // console.log("previousHash : %d", previousHash);
  // console.log(tree);
  // console.log("merkleRoot: %s", merkleRoot);

  const header = new BlockHeader(index, version, previousHash, timestamp, merkleRoot, bit, nonce)
  return new Block(header, body)
}

//값넣어서 블록생성

let Blocks = [createGenesisBlock()]

function getBlocks() {
  return Blocks
}
function getLastBlock() {
  return Blocks[Blocks.length - 1]
}



//블록해시 값 구하기
function createHash (data){
	const {version,previousHash,timestamp, merkleRoot, bit, nonce}= data.header
	const blockString = version +previousHash +timestamp+ merkleRoot+ bit+ nonce
	const hash = cryptojs.SHA256(blockString).toString()
	return hash
}



// const block = createGenesisBlock()
// const testHash = createHash(block)
// console.log(testHash)

//다음블록 생성하기 
function nextBlock(bodyData) {
	const prevBlock = getLastBlock()
	const version = getVersion()
	//다음순서니까 하나 추가됨
	const index = prevBlock.header.index + 1
	//createHash함수에 이전블록 정보를 넣어 블록해시값을 구해넣는다.
	const previousHash = createHash(prevBlock)
	const timestamp = parseInt(Date.now() / 1000)
	//블록body부분에 저장될 트랜잭션(거래정보)인 bodyData를
	//merkle몯ㄹ을 사용하여 트랜잭션들의 해시트리를 만들어 tree에 넣어
	const tree = merkle('sha256').sync(bodyData)
	//여기서 최종적으로 구해진 해시값인 머클루트 해시값을 merkleRoot변수에 넣어
	const merkleRoot = tree.root() || '0'.repeat(64)
	const bit = 0
	const nonce = 0

	const header = new BlockHeader
	(index,version,previousHash,timestamp, 
		merkleRoot, bit, nonce)
    return new Block(header,bodyData)

}
//다음블록생성 출력하기
// const block1 = nextBlock(["tranjaction1"])
// console.log(block1)

//addblock함수를 통해 순차적으로 트랜잭션값 전달해 블록생성하고
function addBlock(bodyData) {
	const newBlock = nextBlock([bodyData])
	Blocks.push(newBlock)
  
  }

// addBlock(['transaction1'])
// addBlock(['transaction2'])
// addBlock(['transaction3'])
// addBlock(['transaction4'])
// addBlock(['transaction5'])

// console.log(Blocks);

//이전블록 해시값과 현재 블록해시값의 이전해시가 같은지
//보려고 내보내줘야함 
module.exports = { nextBlock, getLastBlock, createHash, Blocks, getVersion, getBlocks }


// function addBlock(newBlock){

// 	if(isValidNewBlock(newBlock,getLastBlock())){

// 		Blocks.push(newBlock)
// 		return true;
// 	} return false;
// }

 

checkedBlock.js

더보기
const merkle = require("merkle")
const {Blocks, createHash, getLastBlock,nextBlock} = require('./blockcopy');
// const { nextBlock } = require("./chainedBlock");


//블록구조 유효한지
//blockcopy.js에서 만든 블록체인

//블록의 각각 타입체크 블록구조 유효한지 체크하는 함수
function isValidBlockStructure(block) {
  return typeof (block.header.version) === 'string'
    && typeof (block.header.index) === 'number'
    && typeof (block.header.previousHash) === 'string'
    && typeof (block.header.timestamp) === 'number'
    && typeof (block.header.merkleRoot) === 'string'
    && typeof (block.body) === 'object'
}
//위 함수를 통해 블록구조의 유효성 검사
function isValidNewBlock(newBlock, previousBlock) {
    if (isValidBlockStructure(newBlock)===false){
		console.log('Invalid Block Structure');
		return false;
	} else if (newBlock.header.index !== previousBlock.header.index +1 ){
        console.log('Invalid Index')
        return false;
    } else if (createHash(previousBlock)!==newBlock.header.previousHash) {
        console.log('Invalid previousHash');
        return false;
        //블록길이가 0일경우
    } else if (newBlock.body.length === 0 
        //머클루트는길이가 
        && ('0'.repeat(64) !== newBlock.header.merkleRoot) 
    || newBlock.body.length !== 0 && (merkle("sha256").sync(newBlock.body).root() !==newBlock.header.merkleRoot))
    {
        console.log('Invalid merkleRoot')
        return false
    }
    return true;
}

//검증마친 블록들은 chainedBlock.js의 Blocks배열에 추가한다
function addBlock(newBlock){
    if(isValidNewBlock(newBlock,getLastBlock())){
        Blocks.push(newBlock)
        return true;
    }
    return false;
}

const block = nextBlock(['transaction1'])
addBlock(block)

console.log(block)
728x90
728x90

우분투 터미널 환경에서 깃허브 커밋하고 푸시할 때

1. 

git init

현재 폴더를 로컬 저장소로 지정

 

2.

.gitignore 파일 생성

모듈들이 커밋안되게 방지 

안에 (node_modules)

 

3.

git status

로컬 저장소의 현재상태확인

빨간 글씨체로 보여지는 파일목록은 추적되지 않은 파일들(Untracked files)

추적되지 않은 파일이란, 준비 영역이나 로컬 저장소에 한번이라도 add되거나 commit되지 않은 파일을 말한다.

반대로 추적 상태인 파일들(Tracked files)은 최소한 한번은 git add 명령을 통해 준비 영역에 포함되거나 commit을 통해 로컬 저장소에 저장된 파일이다.

4.

git add .

git add는 특정 파일만 업로드하고자 할 때 사용한다.

 

git add "파일명"



 

5. 로컬 저장소에 최종저장하는 단계

 git commit -m "메세지"

 

6. 로컬 저장소와 원격저장소를 연결

git remote add origin Git Repository 주소

*origin은 별칭이다.

 

7. 별칭내역을 확인

origin의 별칭으로 등록된 원격 저장소를 확인할 수 있다.

 

 git remote -v

 

 

8. 로컬 저장소의 파일들을 원격 저장소로 올린다.

 

git push origin master




 

 

이렇게 나옴

remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: Authentication failed for 'https://github.com/jsoyun/blockchain.git/'

 

그래서 찾아보니까 

 

Settings / Developer settings/Personal access tokens / Generate new token / Token 설정

으로 들어가서

 

토큰 비번을 만들어서 

비번에 넣어주니까 되더라 

 

 

 

 

 

출처:

 

에러시 이거

https://hyeo-noo.tistory.com/184

 

[Mac] GitHub push token 오류 해결

Github 오류 7.29일 새벽 갑자기 git push가 안 되는 현상을 겪었다. 오류의 첫 줄이 무슨 말이냐면 Password 인증방식이 일시적으로 brownout(shutdown?)되었다. Password 대신에 personal access token을 사용..

hyeo-noo.tistory.com

https://shxrecord.tistory.com/179 [두-번째저장소]

https://codecoco.tistory.com/74?category=512233 

728x90
728x90

chainedBlock.js  이라 이름지음요

 

이전 글에서 블록하나를 생성했으니 여러 블록을 이어서 블록체인 만들어보기

코드는 수업때 했지만 이번에도 설명을 보면서 고대로 정리했음 https://berrypjh.tistory.com/50

설명은 똑같고 내가 헤맨거 좀좀따리 추가됨

 

(리눅스 우분투에서 작업중)

 

블록해시 값 구하기

....이전과 같음

class BlockHeader{
        constructor(version,index, previousHash, timestamp, merkleRoot, bit, nonce){
	     this.version = version
		//index 값 넣기
     	 this.index = index
	     this.previousHash = previousHash
	     this.timetamp = timestamp
	     this.merkleRoot = merkleRoot
	     this.bit = bit
	     this.nonce = nonce
		

	}
}

//버전계산하는 함수 
function getVersion(){
...

}

function createGenesisBlock(){
	const version = getVersion()
	//index 값 넣기
	const index = 0
	const previousHash = '0'.repeat(64)
	const timestamp = parseInt(Date.now()/1000)
	const body = ['hello block']
	const tree = merkle('sha256').sync(body)
	const merkleRoot = tree.root() || '0'.repeat(64)
	const bit = 0
	const nonce = 0

...     
	//헤더에 대입
	const header = new BlockHeader(version, index, previousHash, timestamp, merkleRoot, bit,nonce)
    return new Block(header, body)

}
//값넣어서 블록생성
const block = createGenesisBlock()
console.log(block)

 

1) index 값 넣어서 이후에 만들 블록체인 순서를 확인할 수 있게 한다.

(아까 index를 선언만 하고 BlockHeader constructor 구조에 안넣고 헤더에 대입에도 안넣어서 안됐었음

게다가 뒤에 선언하는거랑 넣어준 순서다르면 안되더라...)

 

 

2) 블록체인 불러오기 

//여러개 들어갈 수 있는 배열로 Blocks를 만들어줌 
let Blocks = [createGenesisBlock()]


//현재 있는 블록들을 다 리턴해주는 함수
function getBlocks(){
	return Blocks
}

function getLastBlock(){
	//길이 1이니까 1-1 =0 즉 첫번째배열 불러와 
	return Blocks[Blocks.length - 1]

}

블록체인의 블록들을 넣어줄 Blocks배열을 만들고 첫번째 블록을 생성하는 함수인 createGenesisBlock함수를 해당 배열에 미리 넣어둔다.

 

3)블록해시값 구하기

 

//블록해시 값 구하기
//data에는 블록이 들어오는거임, 이블록을 가지고 해시값을 만들어내는 것임
function createHash (data){
	const {version,previousHash,timestamp, merkleRoot, bit, nonce}= data.header
	const blockString = version +previousHash +timestamp+ merkleRoot+ bit+ nonce
	const hash = crypto.js.SHA256(blockString).toString()
	return hash
}

블록헤더 안에 든 정보들 (version 등)의 합산 정보를 구한후 SHA256으로 변환하면 블록의 블록해시값을 구할 수 있다.

 

crypto.js 쓰려면 

const cryptojs =require('crypto-js')

이거 추가해야될거야

 

node 스크립트이름

 

 

다음블록 생성하기

//다음블록 생성하기 
function nextBlock(bodyData) {
	const prevBlock = getLastBlock()
}

이전 블록의 정보(가장 최신 블록 정보)를 getLastBlock함수를 이용해서 가져오고 prevBlock에 넣어둔다.

//다음블록 생성하기 
function nextBlock(bodyData) {
	const prevBlock = getLastBlock()
	const version = getVersion()
	//다음순서니까 하나 추가됨
	const index = prevBlock.header.index +1
	//createHash함수에 이전블록 정보를 넣어 블록해시값을 구해넣는다.
	const previousHash = createHash(prevBlock)
	const timestamp = parseInt(Date.now()/1000)
	//블록body부분에 저장될 트랜잭션(거래정보)인 bodyData를
	//merkle몯ㄹ을 사용하여 트랜잭션들의 해시트리를 만들어 tree에 넣어
	const tree = merkle('sha256').sync(bodyData)
	//여기서 최종적으로 구해진 해시값인 머클루트 해시값을 merkleRoot변수에 넣어
	const merkleRoot = tree.root() || '0'.repeat(64)
	const bit = 0
	const nonce = 0

const header = new BlockHeader(index,version,previousHash,timestamp, merkleRoot, bit, nonce)
     return new Block(header,bodyData)

}

머클루트 값을 통해 단일 블록내에 존재하는 트랜잭션의 무결성을 검증할 수 있고,

머클루트 값을 이용해 블록의 해시값을 생성했기 때문에 블록의 해시의 무결성도 함께 검증할 수 있다. 

 

 

두번째 블록 생성하고 출력해보기 

//다음블록 생성하기 
function nextBlock(bodyData) {
	const prevBlock = getLastBlock()
	const version = getVersion()
	//다음순서니까 하나 추가됨
	const index = prevBlock.header.index +1
	//createHash함수에 이전블록 정보를 넣어 블록해시값을 구해넣는다.
	const previousHash = createHash(prevBlock)
	const timestamp = parseInt(Date.now()/1000)
	//블록body부분에 저장될 트랜잭션(거래정보)인 bodyData를
	//merkle몯ㄹ을 사용하여 트랜잭션들의 해시트리를 만들어 tree에 넣어
	const tree = merkle('sha256').sync(bodyData)
	//여기서 최종적으로 구해진 해시값인 머클루트 해시값을 merkleRoot변수에 넣어
	const merkleRoot = tree.root() || '0'.repeat(64)
	const bit = 0
	const nonce = 0

	const header = new BlockHeader(version,index,previousHash,timestamp, merkleRoot, bit, nonce)
    return new Block(header,bodyData)

}
//다음블록생성 출력하기
const block1 = nextBlock(["tranjaction1"])
console.log(block1)

인덱스1 이면 배열은 0부터 시작하니까 두번째 맞겠지..?

트랜잭션 값이 'tranjaction1'인 두번째 블록 생성됨 확인 

 

 

블록체인에 넣기

 

function addBlock(bodyData) {
	const newBlock = nextBlock(bodyData)
	Blocks.push(newBlock)
}

addBlock(['transaction1'])
addBlock(['transaction2'])
addBlock(['transaction3'])
addBlock(['transaction4'])
addBlock(['transaction5'])

위처럼 여러 트랜잭션을 순차적으로 addBlock함수를 통해 트랜잭션 값을 전달하여 블록을 생성하고 Blocks배열에 넣어보기

 

최종결과 출력하기

console.log(Blocks);

 

결과

 

전체 코드

더보기
const fs = require('fs')
const merkle = require('merkle')
const cryptojs = require('crypto-js')

class Block{
        constructor(header, body){
		this.header = header
		this.body = body
	}
}

class BlockHeader{
        constructor(version,index, previousHash, timestamp, merkleRoot, bit, nonce){
	     this.version = version
		//index 값 넣기
     	 this.index = index
	     this.previousHash = previousHash
	     this.timetamp = timestamp
	     this.merkleRoot = merkleRoot
	     this.bit = bit
	     this.nonce = nonce
		

	}
}

//버전계산하는 함수 
function getVersion(){
	const package = fs.readFileSync("package.json")
	console.log(JSON.parse(package).version)
	return JSON.parse(package).version

}


//getVersion()

function createGenesisBlock(){
	const version = getVersion()
	//index 값 넣기
	const index = 0
	const previousHash = '0'.repeat(64)
	const timestamp = parseInt(Date.now()/1000)
	const body = ['hello block']
	const tree = merkle('sha256').sync(body)
	const merkleRoot = tree.root() || '0'.repeat(64)
	const bit = 0
	const nonce = 0

	// console.log("version : %s, timestamp: %d, body : %s",version,timestamp,body)
	// console.log("previousHash : %d", previousHash);
	// console.log("tree :")
	// // console.log(tree)
	// console.log("merkleRoot : %s", merkleRoot);
	// console.log("merkleRoot : %s", merkleRoot.toString('hex'));
     
	//헤더에 대입
	const header = new BlockHeader(version, index, previousHash, timestamp, merkleRoot, bit,nonce)
    return new Block(header, body)

}
//값넣어서 블록생성
let Blocks = [createGenesisBlock()]
function getBlocks(){
	return Blocks
}
function getLastBlock(){
	return Blocks[Blocks.length -1]
}


//블록해시 값 구하기
function createHash (data){
	const {version,previousHash,timestamp, merkleRoot, bit, nonce}= data.header
	const blockString = version +previousHash +timestamp+ merkleRoot+ bit+ nonce
	const hash = cryptojs.SHA256(blockString).toString()
	return hash
}

// const block = createGenesisBlock()
// const testHash = createHash(block)
// console.log(testHash)

//다음블록 생성하기 
function nextBlock(bodyData) {
	const prevBlock = getLastBlock()
	const version = getVersion()
	//다음순서니까 하나 추가됨
	const index = prevBlock.header.index +1
	//createHash함수에 이전블록 정보를 넣어 블록해시값을 구해넣는다.
	const previousHash = createHash(prevBlock)
	const timestamp = parseInt(Date.now()/1000)
	//블록body부분에 저장될 트랜잭션(거래정보)인 bodyData를
	//merkle몯ㄹ을 사용하여 트랜잭션들의 해시트리를 만들어 tree에 넣어
	const tree = merkle('sha256').sync(bodyData)
	//여기서 최종적으로 구해진 해시값인 머클루트 해시값을 merkleRoot변수에 넣어
	const merkleRoot = tree.root() || '0'.repeat(64)
	const bit = 0
	const nonce = 0

	const header = new BlockHeader(version,index,previousHash,timestamp, merkleRoot, bit, nonce)
    return new Block(header,bodyData)

}
//다음블록생성 출력하기
const block1 = nextBlock(["tranjaction1"])
// console.log(block1)

//addblock함수를 통해 순차적으로 트랜잭션값전달해 블록생성하고
function addBlock(bodyData) {
	const newBlock = nextBlock(bodyData)
	Blocks.push(newBlock)
}

addBlock(['transaction1'])
addBlock(['transaction2'])
addBlock(['transaction3'])
addBlock(['transaction4'])
addBlock(['transaction5'])

console.log(Blocks);
728x90

+ Recent posts