BlockChain>Truffle& react서명방식 3가지

YU YU·2021년 10월 28일
0

경일_BlockChain

목록 보기
24/24

좋아좋아!!!!!!
맑은 정신으로 수업을 듣는거야!!!!!!


솔리디티 코드를 배포하면 하나의 주소를 반환해준다.
'서명'이라는 단계가 존재함

1.서명

서명: 블록체인 네트워크의 컨트랙트를 사용하기 직전에 나오는 확인받는 절차임

1-1. 방식

  1. 메타마스크를 통해 사용자가 '확인'버튼을 눌러서 직접 서명하는 방식
    (지갑을 통해 서명하는 방식)(저번 시간에 했던 방식)
  2. 사용자가 직접 서명하지만 Server를 거쳤다가 서명하는 방식
    서명하기 전에 백엔드 서버에서 DB를 거쳐서 정보를 저장하고 프론트에 '확인'버튼을 누르도록 한다.
  3. 서버에서 서명까지 완료하는 작업
    사용자는 메타마스크가 필요가 없다. 서명할 필요가 없어서.


서버가 바로 블록체인 네트워크에 뿌리고 그리고 사용자에게 서버가 정보르 전달해준다.
흔히 거래소임.

필요한 데이터(?어다가 넣어야되지)

비밀키가 필요함.
트랜잭션을 일으킬 때 필요함.

라이트코인에서 getnewaddress는 비밀키와 암호키를 함축하고 있는 API였다. 그래서 그 주소로 sendtoaddress로 보내면 로컬과 서버의 작업이 이루어진 것이었다.

2. 사용자 직접 서명 방식

2-1. 작업

2-1-1. truffle-config.js 수정

const path = require("path");

module.exports = {
 // See <http://truffleframework.com/docs/advanced/configuration>
 // to customize your Truffle configuration!
 contracts_build_directory: path.join(__dirname, "client/src/contracts"),
 networks: {
   develop: {
     host:"127.0.0.1",
     port: 7545,
     network_id:"*"
   }
 }
};

2-1-2. set 이벤트 추가

  • contracts>SimpleStorage.sol

event Change(string message, uint newVal);를 추가해줌으로써 이벤트를 선언한다.
그리고

 function set(uint x) public {
    storedData = x;
    emit Change("set",x);
  }

를 추가해줌으로써 이벤트를 정의한다.

전체코드

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;

contract SimpleStorage {
  uint storedData;


  //이벤트를 만든다. 웹소켓 코드를 써준다고 생각하면 편하다.
  event Change(string message, uint newVal); //이벤트의 선언

  function set(uint x) public {
    storedData = x;
    emit Change("set",x);
  }

  function get() public view returns (uint) {
    return storedData;
  }
}

npx truffle compile
npx truffle migrate
작업을 하면 된다.

2-1-3. App.js 기본 세팅하기

  • client>App.js
import React, { useState,useEffect } from "react";
import SimpleStorageContract from "./contracts/SimpleStorage.json";
import getWeb3 from "./getWeb3";

import "./App.css";

const App=()=>{
  const [value,setValue]=useState(0);
  const [storage,setStorage] = useState(0);
  const [loadding,setLoadding] = useState(false);
  
  const handleResult =()=>{
    setStorage(value);
    setLoadding(prev=>!prev)
  }


  const handleChange = (e) =>{
    let val = e.target.value;
    setValue(val);
  }

  //button 함수
  const send =()=>{
    if(value > 0){
      setLoadding(prev=>!prev);
      //비동기적 처리
      handleResult()
    }

  }

  const sendAPI = ()=> {
    if(value > 0){
      setLoadding(prev=>!prev);
      //비동기적 처리
      handleResult()
    }
  }

  const sendTx = () => {
    if(value > 0){
      setLoadding(prev=>!prev);
      handleResult()
    }

  }

  const init = async() => {
    const contract=require('@truffle/contract');
    const web3 = await getWeb3();

    const [account] = await web3.eth.getAccounts();//하나인거 알고 있을 때는 이렇ㄹ게 쓰자
    // const networkId= await web3.eth.net.getId();//네트워크 아이들 가져올 수 있다.
    let simpleStorage = contract(SimpleStorageContract);
    simpleStorage.setProvider(web3.currentProvider);

    const Instance = await simpleStorage.deployed();
    console.log(Instance);
  }

  useEffect(()=>{
    init();
  },[])



  return (
    <div>
      <input type = "text" value={value} onChange={handleChange}/>
      <div>
        <button onClick={send}>일반 서명</button>
        <button onClick={sendAPI}>DB거치고 서명</button>
        <button onClick={sendTx}>DB 서명</button>
      </div>
      <div>
        {loadding? `loading`:storage}
      </div>
    </div>
  )
}

export default App;

하면 콘솔창에 이렇게 address에 대해서도 뜬다.
계좌가 하나일때는 이렇게 써주도록 하자.
const [account] = await web3.eth.getAccounts();

2-1-4. Ruducer 세팅

import React, { useState,useEffect,useReducer } from "react";
import SimpleStorageContract from "./contracts/SimpleStorage.json";
import getWeb3 from "./getWeb3";

import "./App.css";

const reducer = (state,action)=>{
  switch(action.type){
    case "INIT":{
      let {web3,Instance,account} = action; 
      return {
        ...state,web3,Instance,account
      }
    }
  }
}

const INIT_ACTIONS=(web3,Instance,account)=>{
  return{ type:'INIT',web3,Instance,account}
}

const App=()=>{

  const initialState={
    web3:null,
    Instance:null,
    account:null,
  }
  const [state,dispatch]=useReducer(reducer,initialState);
  const [value,setValue]=useState(0);
  const [storage,setStorage] = useState(0);
  const [loadding,setLoadding] = useState(false);
  
  const handleResult =()=>{
    setStorage(value);
    setLoadding(prev=>!prev)
  }


  const handleChange = (e) =>{
    let val = e.target.value;
    setValue(val);
  }

  //button 함수
  const send =()=>{
    if(value > 0){
      setLoadding(prev=>!prev);
      //비동기적 처리
      handleResult()
    }

  }

  const sendAPI = ()=> {
    if(value > 0){
      setLoadding(prev=>!prev);
      //비동기적 처리
      handleResult()
    }
  }

  const sendTx = () => {
    if(value > 0){
      setLoadding(prev=>!prev);
      handleResult()
    }

  }

  const init = async() => {
    const contract=require('@truffle/contract');
    const web3 = await getWeb3();

    const [account] = await web3.eth.getAccounts();//하나인거 알고 있을 때는 이렇ㄹ게 쓰자
    // const networkId= await web3.eth.net.getId();//네트워크 아이들 가져올 수 있다.
    let simpleStorage = contract(SimpleStorageContract);
    simpleStorage.setProvider(web3.currentProvider);

    const Instance = await simpleStorage.deployed();
    console.log(Instance);
    
  }

  useEffect(()=>{
    init();
  },[])



  return (
    <div>
      <input type = "text" value={value} onChange={handleChange}/>
      <div>
        <button onClick={send}>일반 서명</button>
        <button onClick={sendAPI}>DB거치고 서명</button>
        <button onClick={sendTx}>DB 서명</button>
      </div>
      <div>
        {loadding? `loading`:storage}
      </div>
    </div>
  )
}

export default App;

React dev-tool로 확인할 수 있다.

넣기

import React, { useState,useEffect,useReducer } from "react";
import SimpleStorageContract from "./contracts/SimpleStorage.json";
import getWeb3 from "./getWeb3";

import "./App.css";

const reducer = (state,action)=>{
  switch(action.type){
    case "INIT":{
      let {web3,Instance,account} = action; 
      return {
        ...state,web3,Instance,account
      }
    }
  }
}

const INIT_ACTIONS=(web3,Instance,account)=>{
  return{ type:'INIT',web3,Instance,account}
}

const App=()=>{

  const initialState={
    web3:null,
    Instance:null,
    account:null,
  }
  const [state,dispatch]=useReducer(reducer,initialState);
  const [value,setValue]=useState(0);
  const [storage,setStorage] = useState(0);
  const [loadding,setLoadding] = useState(false);
  
  const handleResult =()=>{
    setStorage(value);
    setLoadding(prev=>!prev)
  }


  const handleChange = (e) =>{
    let val = e.target.value;
    setValue(val);
  }

  //button 함수
  const send =()=>{
    if(value > 0){
      setLoadding(prev=>!prev);
      //비동기적 처리
      handleResult()
    }

  }

  const sendAPI = ()=> {
    if(value > 0){
      setLoadding(prev=>!prev);
      //비동기적 처리
      handleResult()
    }
  }

  const sendTx = () => {
    if(value > 0){
      setLoadding(prev=>!prev);
      handleResult()
    }

  }

  const init = async() => {
    const contract=require('@truffle/contract');
    const web3 = await getWeb3();

    const [account] = await web3.eth.getAccounts();//하나인거 알고 있을 때는 이렇ㄹ게 쓰자
    // const networkId= await web3.eth.net.getId();//네트워크 아이들 가져올 수 있다.
    let simpleStorage = contract(SimpleStorageContract);
    simpleStorage.setProvider(web3.currentProvider);

    const Instance = await simpleStorage.deployed();
    console.log(Instance);
    dispatch(INIT_ACTIONS(web3,Instance,account))
  }

  useEffect(()=>{
    init();
  },[])



  return (
    <div>
      <input type = "text" value={value} onChange={handleChange}/>
      <div>
        <button onClick={send}>일반 서명</button>
        <button onClick={sendAPI}>DB거치고 서명</button>
        <button onClick={sendTx}>DB 서명</button>
      </div>
      <div>
        {loadding? `loading`:storage}
      </div>
    </div>
  )
}

export default App;

init 부분 하기

  const init = async() => {
    const contract=require('@truffle/contract');
    const web3 = await getWeb3();

    const [account] = await web3.eth.getAccounts();//하나인거 알고 있을 때는 이렇ㄹ게 쓰자
    // const networkId= await web3.eth.net.getId();//네트워크 아이들 가져올 수 있다.
    let simpleStorage = contract(SimpleStorageContract);
    simpleStorage.setProvider(web3.currentProvider);

    const Instance = await simpleStorage.deployed();
    console.log(Instance);
    dispatch(INIT_ACTIONS(web3,Instance,account))
  }

일반 서명

(event받아서 처리)


이벤트를 받아서 처리하기

init함수에 더 추가한다.

 const init = async() => {
    const contract=require('@truffle/contract');
    const web3 = await getWeb3();

    const [account] = await web3.eth.getAccounts();//하나인거 알고 있을 때는 이렇ㄹ게 쓰자
    // const networkId= await web3.eth.net.getId();//네트워크 아이들 가져올 수 있다.
    let simpleStorage = contract(SimpleStorageContract);
    simpleStorage.setProvider(web3.currentProvider);

    const Instance = await simpleStorage.deployed();
    console.log(Instance);
    dispatch(INIT_ACTIONS(web3,Instance,account))
    web3.eth.subscribe("logs",{address:Instance.address})//사용자의 어드레스가 아니라 contract계정의 address임
    .on('data',log=>{
      console.log(log)
    })
    .on('error',err=>console.log(err))
  }



데이터를 이상하게 보내준다. 컴퓨터가 사용하기 편한 값으로 보내준다. 그래서 우리는 변경해서 보내주어야 한다.

그리고 이제 send, sendAPI, sendTx의 handleresult()를 삭제해준다.
hadleResult 메소드를 다음과 같이 수정한다.


  const handleResult =(log,web3)=>{
    const params=[
      {type:'string',name:'message'},
      {type:'uint256',name:'newVal'}
      //uint만 적는 것이 아니라 uint256으로 적어준다.
    ]
    const returnValues = web3.eth.abi.decodeLog(params,log.data)//dat:0x00000.....을 우리가 원하는 형식으로 변경해준다.
    //decode는 첫번째는 데이터 형식, 두번째는 log.data 
    
    console.log(`returnValues:`,returnValues);//서명까지 해야 보인다. 
    setStorage(value);
    setLoadding(prev=>!prev)
  }

init 함수를 이렇게 바꾸어준다.

const init = async() => {
    const contract=require('@truffle/contract');
    const web3 = await getWeb3();

    const [account] = await web3.eth.getAccounts();//하나인거 알고 있을 때는 이렇ㄹ게 쓰자
    // const networkId= await web3.eth.net.getId();//네트워크 아이들 가져올 수 있다.
    let simpleStorage = contract(SimpleStorageContract);
    simpleStorage.setProvider(web3.currentProvider);

    const Instance = await simpleStorage.deployed();
    console.log(Instance);
    dispatch(INIT_ACTIONS(web3,Instance,account))
    web3.eth.subscribe("logs",{address:Instance.address})//사용자의 어드레스가 아니라 contract계정의 address임
    .on('data',log=>{
      // console.log(log)
      handleResult(log,web3)
    })
    .on('error',err=>console.log(err))
  }

서명을 해주면 다음곽 같은 것이 뜬다.

아까와는 다르게 우리가 해석할 수 있는 언어로 던져주는 것을 알 수 있다. 그리고 이것은 Simplestorage.js의 event 의 설정값이라는 것을 알 수 있다.

다음 사이트에 가면 web.js의 메소드에 관해 자세히 알 수 있다.
https://web3js.readthedocs.io/en/v1.5.2/

완성된 코드

  • App.js 전체 코드
  import React, { useState,useEffect,useReducer } from "react";
import SimpleStorageContract from "./contracts/SimpleStorage.json";
import getWeb3 from "./getWeb3";
// import axios from 'axios'

import "./App.css";

const reducer = (state,action)=>{
  switch(action.type){
    case "INIT":{
      let {web3,Instance,account} = action; 
      return {
        ...state,web3,Instance,account
      }
    }
  }
}

const INIT_ACTIONS=(web3,Instance,account)=>{
  return{ type:'INIT',web3,Instance,account}
}

const App=()=>{

  const initialState={
    web3:null,
    Instance:null,
    account:null,
  }
  const [state,dispatch]=useReducer(reducer,initialState);
  const [value,setValue]=useState(0);
  const [storage,setStorage] = useState(0);
  const [loadding,setLoadding] = useState(false);
  
  const handleResult =(log,web3)=>{
    const params=[
      {type:'string',name:'message'},
      {type:'uint256',name:'newVal'}
      //uint만 적는 것이 아니라 uint256으로 적어준다.
    ]
    const returnValues = web3.eth.abi.decodeLog(params,log.data)//dat:0x00000.....을 우리가 원하는 형식으로 변경해준다.
    //decode는 첫번째는 데이터 형식, 두번째는 log.data 

    console.log(`returnValues:`,returnValues);//서명까지 해야 보인다. 
    //returnValues.message=="set" 이렇게 다양한 이벤트 ㅊㅓ리가 가능하다.스위치문으로 가능
    setStorage(returnValues.newVal);
    setLoadding(prev=>!prev);
  }


  const handleChange = (e) =>{
    let val = e.target.value;
    setValue(val);
  }

  //button 함수
  const send = async() => {
    const {account,Instance} = state;
    if(value > 0){
      setLoadding(prev=>!prev);
      //비동기적 처리
      await Instance.set(value,{from:account})
    }
  }

  const sendAPI = ()=> {

    //역할
    /*1. 백엔드에 요청한다.(비동기axios)
    백엔드에서 reqTx객체를 반환해준다.
    반환받은 값을 sendTransaction()을 실행한다.(실질적으로 서명하는 과정) */
    if(value > 0){
      setLoadding(prev=>!prev);
      //비동기적 처리
      //web3.eth.sendTransaction() 서명만 처리하는 방법이 있다. 
      //인자값으로 객체가 들어간다.
      /*rawTx={
        "from":"adress,,,",
        "to":"",
        "data":"실질적인 데이터부분 이제는 우리가 컴퓨터가 이해 편하도록인코딩을 해야한다. 이걸 백엔드에서 처리해야함 여기서 set함수로 넣는다."
        "gasLimit":"...",
        "gasPrice":",,,"
      }*/
    }
  }

  const sendTx = () => {
    if(value > 0){
      setLoadding(prev=>!prev);

    }

  }

  const init = async() => {
    const contract=require('@truffle/contract');
    const web3 = await getWeb3();

    const [account] = await web3.eth.getAccounts();//하나인거 알고 있을 때는 이렇ㄹ게 쓰자
    // const networkId= await web3.eth.net.getId();//네트워크 아이들 가져올 수 있다.
    let simpleStorage = contract(SimpleStorageContract);
    simpleStorage.setProvider(web3.currentProvider);

    const Instance = await simpleStorage.deployed();
    console.log(Instance);
    dispatch(INIT_ACTIONS(web3,Instance,account))
    web3.eth.subscribe("logs",{address:Instance.address})//사용자의 어드레스가 아니라 contract계정의 address임
    .on('data',log=>{
      // console.log(log)
      handleResult(log,web3)
    })
    .on('error',err=>console.log(err))
  }

  useEffect(()=>{
    init();
  },[])



  return (
    <div>
      <input type = "text" value={value} onChange={handleChange}/>
      <div>
        <button onClick={send}>일반 서명</button>
        <button onClick={sendAPI}>DB거치고 서명</button>
        <button onClick={sendTx}>DB 서명</button>
      </div>
      <div>
        {loadding? `loading`:storage}
      </div>
    </div>
  )
}

export default App;

여기서 setStoarage(value)가 아니라 setStorage(returnValues.newVal)으로 처리함으로 블록체인에서 변경된 값을 넣어주었다.


숫자가 잘 바뀌는 것을 확인할 수 있다. 여기서의 값은 화면에서만 처리되는 것이 아니라 블록체인의 값이 바뀐 것임을 명심하자!

npm i web3 express body-parser ethereumjs-tx cors

  • server.js
profile
코딩 재밌어요!

0개의 댓글