스마트컨트랙트 - 서명

오미희·2021년 10월 16일
0

blockchain

목록 보기
5/13

스마트컨트랙트 기본세팅
// 위의 작업 후 이어진 작업

스마트 컨트랙트에서 서명

1) 일반서명

2) SERVER를 거친 서명

3) SERVER 자체 서명


진행 과정

** 최상위 폴더 work

1. SimpleStorage.sol파일 내용 작성

// 현 경로 /work/contracts

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() publie view returns (uint) {
       returns storedData;
    }
}

2. 기존에 client폴더내 contract폴더에 존재하는 파일 삭제

// 현 경로 /work/client/src/contracts
// rm Migrations.json && rm testStorage.json ==> 기존에 존재하던 두 파일 삭제
// rm => 파일이나 폴더 삭제해주는 명령어

3. 컴파일이랑 배포 다시함

truffle compile ->솔리디티 파일 컴파일
truffle migrate -> 배포해줌

4. App.js 파일 수정

// 하려는 작업에 따라 App.js 내용 달라짐
// 나는 서명에 대한 작업을 할 것임.

사전에 설치
-- axios

// 우선 전체 코드
// 함수형으로 코드 작성

import React,{useEffect, useState, useReducer} from 'react';
import TestStorageContract from './contracts/testStorage.json';
import getWeb3 from './getWeb3';
import axios from 'axios';

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 [value,setValue] = useState(0);
  const [loadding,setLoadding] = useState(false);
  const [storage,setStorage] = useState(0);

  const [state,dispatch] = useReducer(reducer,initialState);
  
  const handleResult = (log,web3) => {
    const params = [
      {type:'string',name:'message'},
      {type:'uint256',name:'newVal'}
    ]
    //decodeLog() --> 2개의 인자값
    // 첫번째  --> 데이터 형식
    // 두번째  --> log.data 
    const returnValues = web3.eth.abi.decodeLog(params,log.data);
    console.log('returnValue',returnValues)

    setStorage(returnValues.newVal);
    setLoadding(prev=>!prev)

  }

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

  // 일반서명-메타마스크를 통해 이루어지는 것.
  const send = async () => {
    const {account, Instance} = state;
    if(value > 0){
      setLoadding(prev=>!prev)
      await Instance.set(value,{from:account})
      // 비동기적 처리
    }else if(value < 0) {
      setStorage('양심 있냐?')
    }
  }
  
   /*
   1. backend에 요청
   2. backend에서 rawTx 객체를 반환
   3. 반환받은 값으로 sendTransaction()을 실행 (실질적 서명이 이루어짐)
   */

  // SERVER거친 서명  //http://localhost:3001/rpc/set
  const sendAPI = async () => {
    const {web3,account} = state;
    if(value > 0 ){
      setLoadding(prev=>!prev);
      let result = await axios.post('http://localhost:3001/rpc/set',{from:account,val:value});
      console.log(result.data.rawTx,'왜 안나와?');
      if(result.data !== undefined && 
         result.data.rawTx !== undefined && 
         result.data.success == true ){
      await web3.eth.sendTransaction(result.data.rawTx); 
        /*
        rawTx = {
           "from":"address",
           "to":...,
           "data":"실질적 데이터 부분 들어감",
           "gasLimit":"",
           "gasPrice":""
        }
        */
      }
    }
  }

  // SERVER서명
  const sendTx = async () => {
    const {account} = state;
    if(value > 0){
      setLoadding(prev=>!prev);
      const result = await axios.post('http://localhost:3001/rpc/setTx',{from:account,val:value})
      console.log(result)
    }
  }
  // instance  중요 ============================================
  const init = async () => {
    const contract = require('@truffle/contract');
    const web3 = await getWeb3();
    // 위의 반환값은 프로미스 객체이므로 await로 받음 // 함수 앞에 async명시해줌
    const [account] = await web3.eth.getAccounts();
    // 배열이므로 구조분해 할당문으로 해서 받음.
    
    //networkID도 가져올 수 있다.
    //const netwrokID = await web3.eth.net.getId();

    let testStorage = contract(TestStorageContract);
    testStorage.setProvider(web3.currentProvider);

    const Instance = await testStorage.deployed();
    // instance생성하는 부분 ** 매우 중요 **
    // 배포한 코드까지 접근할 수 있도록, Instance 코드로 만든 것.  
    console.log(Instance,"Instance");

    dispatch(INIT_ACTIONS(web3,Instance,account))
    
    // sol파일에서 emit과 관련한 부분.
    web3.eth.subscribe('logs',{address:Instance.address})
    // 두 번째 인자값에는 배포가 된 계정의 address
    .on("data",log=>{
      console.log(log,'log');
      // transaction이 발생할 때마다 로그가 찍힘.
      handleResult(log,web3);   // handleResult(log,web3)부분 잘 이해안감... 밑에 확인
      /*
        const handleResult = (log,web3) => {
        const params = [
          {type:'string',name:'message'},
          {type:'uint256',name:'newVal'}
        ]
        //decodeLog() --> 2개의 인자값
        // 첫번째  --> 데이터 형식
        // 두번째  --> log.data 
        const returnValues = web3.eth.abi.decodeLog(params,log.data);
        console.log('returnValue',returnValues)

        setStorage(returnValues.newVal);
        setLoadding(prev=>!prev)
      }
      
      */
    })
    .on('error',err=>console.log(err,'err'));
  }

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

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

export default App;

5. backend 관련 서명을 위한 폴더생성

// 최상위 폴더인 work에서 server폴더 생성

//server.js 파일 내용
const express = require('express');
const app = express();
const router = require('./routes');
const cors = require('cors');
const bodyParser = require('body-parser');
const web3 = require('web3')

app.use(cors());
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());

app.use('/',router);

app.listen(3001,()=>{
  // 프론트인 리엑트에서 3000번 포트를 사용 중이므로 3000번 포트 사용할 수 없음.
    console.log('server start port 3001')
})

// 나머지 index.js파일은 단순히 라우터 기능을 하는 부분이므로 코드 생량
// 실질적인 기능을 하는 파일은 rpc.controller.js파일

// rpc.controller.js 파일 내용
const Web3 = require('web3');
const web3 = new Web3('http://localhost:7545');
const abi = require('../../../client/src/contracts/testStorage.json').abi;
const {address} = require('../../../client/src/contracts/testStorage.json').networks["5777"];
const ethTx = require('ethereumjs-tx').Transaction;

// SERVER를 거친 서명    //http://localhost:3001/rpc/set
const set = async (req,res) => {
    const {from,val} = req.body;
    const instance = await new web3.eth.Contract(abi,address);
    console.log(instance,'이건 instance ');

    const data = await instance.methods.set(val).encodeABI();
    console.log(data,'이건 data')

    let txObject = {
        nonce:null,
        from,
        to:address,
        data,
        gasLimit:web3.utils.toHex(30000),
        gasPrice:web3.utils.toHex(web3.utils.toWei('20','gwei'))
    }
    
    res.json({
        success:true,
        rawTx:txObject
    })
}

// SERVER자체 서명   //http://localhost:3001/rpc/setTx
const setTx = async (req,res) => {
    const {from,val} = req.body;
    const instance = await new web3.eth.Contract(abi,address);
    const data = await instance.methods.set(val).encodeABI();
    const txCount = await web3.eth.getTransactionCount(from);
    const privateKey = Buffer.from('/* 비밀키 */','hex');

    let txObject = {
        nonce:web3.utils.toHex(txCount),
        from,
        to:address,
        data,
        gasLimit:web3.utils.toHex(3000000),
        gasPrice:web3.utils.toHex(web3.utils.toWei('20','gwei'))
    }

    const tx = new ethTx(txObject);
    tx.sign(privateKey);

    const serializedTx = tx.serialize();
    const txhash = await web3.eth.sendSignedTransaction('0x'+serializedTx.toString('hex'));
    res.json({
        success:true,
        rawTx:txObject,
        txhash
    })
}

module.exports = {
    set,
    setTx
}

참고

//   /work/contracts폴더내의 testStoraged.sol파일 내용
pragma solidity >=0.4.22 <0.9.0;

contract testStorage {
  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;
  }
}
profile
안녕하세요

0개의 댓글