Transaction

Luniversity에 오신 여러분을 환영합니다!😆 앞서 진행했던 Block은 재밌게 보고 오셨나요? 이번 시간에는 Transaction이란 무엇인지, 어떤 구조로 구성되어 있는지 확인해보고 실제 Transaction을 실행해 보며 Transaction에 대한 이해도를 높이는 시간을 갖도록 하겠습니다!


Transaction이란?

Blockchain에서는 Transaction은 유저가 발생시키는 일종의 작업으로 상태(State)의 변화를 의미합니다. 보편적으로 발생하는 Transaction은 디지털 자산을 전송하거나 받는 작업으로 Sender의 자산이 Receiver의 주소로 전송되는 것 입니다. 아래의 그림을 보면 더욱 쉽게 이해할 수 있습니다.

Transaction이 발생하면서, Sender(User A)가 보유하고 있는 ETH의 잔고(State)와 Receiver(User B)가 보유 중인 ETH의 잔고(State)가 변경됩니다. Node는 이러한 상태 변화를 네트워크에 전파하게 되고 벨리데이터(채굴자)들이 전파된 Transaction을 모아 Block을 채굴하게 됩니다. Transaction의 종류는 ETH를 전송하는 Transaction외에 블록체인에 데이터를 입력하기 위한 Transaction, 스마트 컨트랙트의 Method를 실행하기 위한 Transaction 등 다양한 Transaction이 있습니다.


Transaction과 Gas

이더리움 네트워크에서는 트랜잭션을 실행할 때 Gas라는 이름의 수수료를 지불하게 됩니다. 왜 P2P 네트워크인 이더리움에서 수수료를 지불하게 구성이 되어 있을까요? 이유는 바로 안전한 네트워크 환경을 유지하기 위함입니다. 한 번 상상해 볼까요? 악의적인 공격자가 Gas가 없는 이더리움 네트워크에서 무한으로 로직이 동작하는 Transaction을 실행했다고 가정해 보겠습니다. 이더리움 특성 상, 해당 Transaction이 실행될 때 다른 Transaction은 실행 중인 Transaction이 끝날 때까지 대기하게 됩니다. 하지만 악의적인 Transaction이 무한으로 동작하고 있으므로 다른 Transaction들이 실행되지 못해 네트워크에 심각한 문제가 발생할 수 있습니다.

이러한 공격을 막기 위해 이더리움 네트워크는 Transaction을 실행할 때 각 작업마다 Gas라는 이름의 수수료를 소비하도록 구성하였습니다. 만약 악의적인 공격자가 이더리움 네트워크에서 무한루프 트랜잭션을 실행했다고 가정을 해 보겠습니다. 공격자의 Transacntion이 무한루프를 돌 때 마다 Gas가 소모되면서 공격자가 보유한 ETH의 잔고가 줄어들게 됩니다. 그러다 공격자가 보유한 모든 ETH를 소모하면 Transaction은 실패하고 State는 악의적인 공격자의 Transaction 실행 이전 State로 돌아가게 됩니다. 악의적인 공격자는 자신의 ETH만 소모하고 이더리움 네트워크는 아무런 타격을 받지 않게 되는 것입니다. (Transaction이 실패하게 될 경우, 실패한 부분 까지 소모된 Gas는 지불하게 되고 State는 원상복구 됩니다.)

또한 이더리움 네트워크에서는 Transaction에 대한 최대 Gas 소모량을 제한할 수 있는 기능을 제공하고 있습니다. 이를 제한하는 이유는 예상되는 Gas 소모량 이상 사용되는 트랜잭션이 발생할 경우, 이를 차단하여 유저의 자산을 보호하기 위함입니다. 유저는 Transaction을 실행할 때 사용할 최대 Gas의 Limit을 지정하고 Node는 Transaction을 실행하다가 지정된 Gas의 Limit보다 많은 Gas를 소모하게 될 경우, Transaction을 Fail로 처리하고 State를 Transaction 실행 이전으로 되돌리게 됩니다. 이렇듯 이더리움 네트워크는 Gas와 GasLimit을 통해 네트워크를 보다 안정적으로 유지하고 유저의 자산을 보호할 수 있습니다.

그렇다면 과연 이러한 Transaction은 어떠한 구조로 구성되어 있을까요? 같이 확인해 보도록 하겠습니다.😀


Transaction의 Structure 및 분석

이더리움 Client인 Go-Ethereum(Geth)에서 확인할 수 있는 Transaction의 구조는 다음과 같습니다.

type Transaction struct {
    data txdata   
    hash common.Hash   
    size int      
    from common.Address  
    nonce uint64      
    gasPrice *big.Int    
    gasLimit uint64   
    to *common.Address 
    value *big.Int  
    // EIP155-specific fields
    chainID *big.Int  
    accessList types.AccessList  
    type uint8 
     
    **// caches**
    fromCache atomic.Value
    encodedCache atomic.Value
    sizeCache atomic.Value
    hashCache atomic.Value
}

참조 : https://github.com/ethereum/go-ethereum/blob/master/core/types/transaction.go


Transaction

Transaction에는 거래 데이터와 Transaction 시 사용되는 가스의 정보 등이 기록됩니다. Transaction에 기록되는 값은 다음과 같습니다.

  • data : Transaction 거래 데이터 입니다. txdata라는 구조체에서 값을 불러옵니다.
  • hash : Transaction의 해시 입니다.
  • size : Transaction의 크기 입니다.
  • from : Transaction을 실행하는 Sender의 주소 입니다.
  • nonce : from 주소의 nonce 입니다. Account의 nonce는 일종의 카운터로 0부터 시작하여 Transaction을 발생시킬 때 마다 1씩 증가합니다.
  • gasPrice : Transaction 시 사용되는 가스의 가격으로 Sender가 입력할 수 있습니다. gasPrice가 높을수록 소모되는 gas의 가격이 비싸지지만 Transaction이 빨리 실행될 확률이 높아집니다.
  • gasLimit : Transaction 실행에 필요한 최대 가스량을 나타냅니다. 트랜잭션 실행 시 필요한 가스의 양은 컨트랙트의 복잡성에 따라 달라지는데 gasLimit을 지정하여 최대 사용될 가스의 양을 제한할 수 있습니다. 만약 Transaction 실행에 필요한 값보다 낮게 설정되어 있거나 Transaction이 gasLimit보다 많은 gas를 소모하게 될 경우, Transaction은 실패하게 됩니다.
  • to : Transaction을 받는 주소 입니다. EOA로 설정이 되어 있을 경우, 아래 입력하는 Value에 따라 ETH를 전송받게 되며 CA로 설정되어 있을 경우, data에 입력한 값에 따라 Contract의 Method를 실행할 수 있습니다. 컨트랙트를 생성하는 Transaction일 경우, to의 값은 null 입니다.
  • value : From 주소에서 To 주소로 전송할 ETH의 양 입니다.
  • chainID : EIP-155 규격에서 지정된 chain ID 입니다. 이더리움 계열의 체인은 ID를 보유하고 있으며 해당 체인에서 실행하는 Transaction임을 명시합니다. 예를 들어 이더리움 메인넷의 chainID는 1, Sepolia 테스트넷의 chainID는 11155111 입니다.
  • accessList : EIP-2930 규격에서 지정된 값으로 트랜잭션에서 접근해야하는 스토리지의 위치를 명시적으로 지정하여 해당 위치만 업데이트, 조회하는 등 필요한 작업만 수행하여 gas 비용을 줄이고 속도를 향상시킬 수 있습니다. accessList의 데이터 타입은 array이며 accessList에 입력되는 데이터의 포맷은 address와 storageKeys라는 이름의 32byte 배열입니다. address에 대한 접근 패턴 중 변경될 수 있는 스토리지의 위치를 storageKeys로 명시합니다.

TxData

Transaction 구조체의 data 필드는 TxData라는 구조체에서 값을 받아옵니다. TxData에 기록되는 값은 다음과 같습니다.

type txdata struct {
    AccountNonce uint64          `json:"nonce"       gencodec:"required"`
    Price        *big.Int        `json:"gasPrice"    gencodec:"required"`
    GasLimit     uint64          `json:"gas"         gencodec:"required"`
    Recipient    *common.Address `json:"to"          rlp:"nil"`
    Amount       *big.Int        `json:"value"       gencodec:"required"`
    Payload      []byte          `json:"input"       gencodec:"required"`
    V            *big.Int        `json:"v"           gencodec:"required"`
    R            *big.Int        `json:"r"           gencodec:"required"`
    S            *big.Int        `json:"s"           gencodec:"required"`
    Type         uint8           `json:"type"        gencodec:"required"`
}
  • AccountNonce : Transaction 송신자의 nonce 값 입니다. nonce는 각 트랜잭션이 한 번만 처리되게 하기 위한 카운터 입니다. Transaction을 실행할 때마다 이 값을 1씩 증가시켜야 합니다.
  • Price : Transaction 시 사용되는 가스의 가격으로 Sender가 입력할 수 있습니다.
  • GasLimit : Transaction 실행에 필요한 최대 가스량을 나타냅니다. 트랜잭션 실행 시 필요한 가스의 양은 컨트랙트의 복잡성에 따라 달라지는데 gasLimit을 지정하여 최대 사용될 가스의 양을 제한할 수 있습니다. 만약 Transaction 실행에 필요한 값보다 낮게 설정되어 있는 경우, 해당 Transaction은 실패하게 됩니다.
  • Recipient : Transaction의 발신자로 To에 해당하는 Account Address 입니다.
  • Amount :From 주소에서 To 주소로 전송할 ETH의 양 입니다.
  • Payload : Transaction 시 함께 전송할 데이터 입니다. 컨트랙트의 Method 실행 시 Payload에 작성된 값을 바탕으로 컨트랙트의 Method를 실행하게 됩니다.
  • V, R, S : 서명 알고리즘에 의해 생성된 값으로 서명을 검증하기 위한 값 입니다.
  • Type : 거래 유형을 나타내며 0은 Legacy, 1은 AccessList, 2는 EIP-1559에 대한 Transaction 유형임을 나타냅니다.

TxData은 User가 실행하고자 입력한 Transaction의 정보와 서명데이터가 포함된 구조체이고 Transaction은 해당 거래에 대한 전체적인 정보를 포함하고 있어 거래의 검증 및 상태 변화를 하는 구조체 입니다.


ethers.js를 이용하여 Transaction 실행

ether.js는 Ethereum 환경에서 개발을 편리하게 할 수 있도록 다양한 기능을 제공하는 JavaScript 라이브러리로 Ethereum 표준 인터페이스를 준수하여 다른 이더리움 라이브러리와 상호 운용성이 높습니다. 이번 시간에는 ethers.js를 이용하여 Transaction을 실행해 보도록 하겠습니다.

(트랜잭션 실행 시 gas비용으로 ETH가 소모되므로 무료로 ETH를 받아 진행하기 위해 메인넷이 아닌 Sepolia 테스트넷에서 트랜잭션을 실행해 보도록 하겠습니다.)

📘

Node를 이용하기 위한 RPC-Endpoint 구하기

Node를 이용하기 위해서는 해당 Node를 특정하는 RPC-Endpoint가 필요합니다. 아래 링크를 통해 Nova에서 제공하는 RPC-Endpoint를 얻을 수 있습니다. RPC-Endpont는 네트워크 별로 달라질 수 있으며 현재 Luniverse에서 제공하는 Ethereum의 네트워크는 Mainnet, Goerli, Sepolia 입니다.

노드 연동하여 RPC-Endpoint 구하는 방법(클릭)

const { ethers } = require('ethers');

const nodeId = "{Your Node ID}"
const rpcEndpoint = 'https://ethereum-sepolia.luniverse.io/${nodeId}';
const provider = new ethers.providers.JsonRpcProvider(rpcEndpoint);
const privateKey = "{Your Private Key}";
const wallet = new ethers.Wallet(privateKey, provider);
const myNonce = await wallet.getTransactionCount('latest');
const gasPrice = await provider.getGasPrice();

// Raw transaction data
const txData = {
  to: "0x6063B58BED10C5b4d40660bD68b1a0317C3E883c",
  value: ethers.utils.parseEther("0.1"),
  gasLimit : 21000,
  gasPrice : gasPrice._hex,
  nonce: myNonce,
  chainId : 11155111
};

// Sign raw Transaction data
const signedTx = await wallet.signTransaction(txData);
console.log("signed Transcation Data :", signedTx);

// send signed raw transaction data
const txResponse = await provider.sendTransaction(signedTx);
console.log("Transaction Hash : ", txResponse);

Provider 인스턴스와 PrivateKey를 이용하여 새로운 wallet 인스턴스를 생성합니다. 이후 해당 wallet의 nonce를 구하고 현재 이더리움 네트워크의 평균적인 gasPrice를 구한 뒤, 전송할 트랜잭션 데이터를 입력, 서명 후, 트랜잭션을 실행하는 코드 입니다.

eth_sendTransaction의 응답으로 반환받는 값은 다음과 같습니다. Transaction을 노드에 전송한 후, 블록에 포함되는 것을 기다리지 않고 즉시 반환합니다. 노드에 전송한 Transaction의 데이터와 Transaction Hash를 반환 받습니다.


👍

Response eth_sendTransaction

{
nonce: 0,
  gasPrice: BigNumber { _hex: '0x59682f07', _isBigNumber: true },
  gasLimit: BigNumber { _hex: '0x5208', _isBigNumber: true },
  to: '0x6063B58BED10C5b4d40660bD68b1a0317C3E883c',
  value: BigNumber { _hex: '0x016345785d8a0000', _isBigNumber: true },
  data: '0x',
  chainId: 11155111,
  v: 22310257,
  r: '0x850df3a3d8c6231bd7a07bc2f54bfff803dbe09a88ded4e21a40be19a1bcca0a',
  s: '0x09a88296814a63733043aa13281e6a0bf241b66e9b9436a2f209e65f4af7fe97',
  from: '{Sender Address}',
  hash: '0x605133d8dfe07579bfa28fffd72cd766e29ba15204ec5bbc48f121f639cda730',
  type: null,
  confirmations: 0,
  wait: [Function (anonymous)]
}

이후 Transaction이 블록에 포함되었다면 getTransactionReceipt Method를 이용하여 Transaction에 대한 결과를 확인할 수 있습니다. Transaction 실행 결과를 포함한 Receipt가 반환되며 앞서 실행한 Transaction에 대한 getTransactionReceipt Method의 Response는 다음과 같습니다.

{
  to: '0x6063B58BED10C5b4d40660bD68b1a0317C3E883c',
  from: '0x78D3D552841415FE367F08ACD869d0f0AA93e815',
  contractAddress: null,
  transactionIndex: 6,
  gasUsed: BigNumber { _hex: '0x5208', _isBigNumber: true },
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  blockHash: '0x645ed5b68379bfce8827c6797e5ae430d73370080454914eb739f44d6f5e0091',
  transactionHash: '0x605133d8dfe07579bfa28fffd72cd766e29ba15204ec5bbc48f121f639cda730',
  logs: [],
  blockNumber: 3317977,
  confirmations: 22,
  cumulativeGasUsed: BigNumber { _hex: '0x1d37ef', _isBigNumber: true },
  effectiveGasPrice: BigNumber { _hex: '0x59682f07', _isBigNumber: true },
  status: 1,
  type: 0,
  byzantium: true
}

실제 트랜잭션 실행 시 사용된 gas의 양과 Transaction이 포함되어 있는 block의 Hash와 number, 그리고 Confirm된 Block의 수 등, 해당 Transaction 실행 결과에 대한 정보를 확인할 수 있습니다.


Luniverse를 이용하여 Transaction 실행

Luniverse에서는 Transaction을 실행하기 위해 필요한 데이터를 불러오는 기능을 제공하고 있습니다. 이러한 기능을 이용해서 Transaction을 실행하기 위해 필요한 데이터를 불러오는 기능을 체험해 보도록 하겠습니다.

체험할 Multichain API

📘

How to get Access Token?

Access Token은 API를 호출하기 위해 필요한 Header의 Authorization 입니다. Access Token을 생성하는 방법은 아래 링크를 클릭하여 확인할 수 있습니다.

Access Token 생성하기(클릭)

🚧

INSTALLATION

예제를 실행하기 위해서는 라이브러리를 설치해야 합니다. 아래 명령어를 입력하여 필요한 라이브러리를 설치할 수 있습니다.

$ npm install ethers@5


Transaction을 실행하기 위해서는 gasPrice가 필요합니다. 현재 네트워크에서 사용되는 평균적인 gasPrice를 구할 수 있는 getGasPrice API를 제공하고 있습니다. Multichain API를 사용하기 전 JSON-RPC를 이용해 gasPrice를 구해보도록 하겠습니다.

  • JSON-RPC 호출
curl -X POST "https://ethereum-sepolia.luniverse.io/{Your Node ID}" \
--header 'Content-Type: application/json' \
--data '{
    "jsonrpc":"2.0",
    "method":"eth_gasPrice",
    "params":[],
    "id":1
}'

해당 Method 호출의 결과로 현재 이더리움 네트워크에서 트랜잭션 실행을 위해 쓰이는 평균적인 gasPrice의 양을 반환받습니다.

{
"jsonrpc": "2.0",
"id": 1,
"result": "0x3b9aca07"
}

이와 같은 기능을 Nova getGasPrice API를 이용해서 gasPrice를 구해보도록 하겠습니다. Node.js를 이용하여 API를 호출하였으며 방법은 아래와 같습니다.

const axios = require('axios');

const options = {
  method: 'GET',
  url: 'https://web3.luniverse.io/v1/ethereum/sepolia/transactions/gas/price',
  headers: {
    accept: 'application/json',
    Authorization: 'Bearer {Your Access Token}'
  }
};

axios
  .request(options)
  .then(function (response) {
    console.log(response.data);
  })
  .catch(function (error) {
    console.error(error);
  });

getGasPrice의 응답은 다음과 같습니다. 평균적으로 사용되는 high, low, average gasPrice와 baseFee를 확인할 수 있습니다. Response로 받은 gasPrice를 이용하여 Raw Transaction의 gasPrice Field를 채울 수 있습니다.

{
  "code": "SUCCESS",
  "data": {
    "high": "1500000007",
    "average": "1500000007",
    "low": "1500000007",
    "lastBlock": "3318051",
    "baseFee": null
  }
}

이번에 진행할 API는 getNextNonce API입니다. Account가 트랜잭션을 실행하기 위해 입력해야하는 nonce값을 반환해주는 API로 Nova API 이용 전, JSON-RPC로 먼저 nonce를 구해보겠습니다.

curl -X POST "https://ethereum-sepolia.luniverse.io/{Your Node ID}" \
--header 'Content-Type: application/json' \
--data '{
    "jsonrpc":"2.0",
    "method":"eth_getTransactionCount",
    "params":["{Your EOA Address}","latest"],
    "id":1
    }'

JSON-RPC로 nonce를 구하기 위한 Method는 eth_getTransactionCount 입니다. 해당 명령어를 입력하면 해당 EOA가 트랜잭션을 실행하기 위해 필요한 nonce의 값을 반환받을 수 있습니다.

{"jsonrpc":"2.0","id":1,"result":"0x1"}

같은 응답을 반환하는 Nova getNextNonce API를 실행해 결과값을 확인해 보겠습니다. Node.js를 이용하여 API를 호출하는 방법은 다음과 같습니다.

const axios = require('axios');

const address = "{Your Address}";
const options = {
  method: 'GET',
  url: 'https://web3.luniverse.io/v1/ethereum/sepolia/transactions/nonce/${address}',
  headers: {
    accept: 'application/json',
    Authorization: 'Bearer {Your Access Token}'
  }
};

axios
  .request(options)
  .then(function (response) {
    console.log(response.data);
  })
  .catch(function (error) {
    console.error(error);
  });

getNextNonce API의 응답은 다음과 같습니다. 16진수의 값으로 나타납니다. 이 값을 Raw Transaction Data의 nonce Field에 입력하여 사용할 수 있습니다.

{
  "code": "SUCCESS",
  "data": {
    "nonce": "0x1"
  }
}

마지막으로 transactionHash를 이용하여 Transaction에 대한 데이터를 조회할 수 있는 eth_getTransactionHash Method 입니다. 먼저 JSON-RPC로 조회한 후, Nova API를 이용해 같은 값을 조회해 보도록 하겠습니다.

JSON-RPC를 이용한 방법 입니다.

curl -X POST "https://ethereum-sepolia.luniverse.io/{Your Node ID}" \
--header 'Content-Type: application/json' \
--data '{
        "jsonrpc":"2.0",
        "method":"eth_getTransactionByHash",
        "params": ["0x605133d8dfe07579bfa28fffd72cd766e29ba15204ec5bbc48f121f639cda730"],
        "id":1
    }'

eth_getTransactionByHash Method를 사용하였고 params에 txHash를 입력하여 조회합니다. 응답은 다음과 같습니다.

{
	"jsonrpc":"2.0",
	"id":1,
	"result":
	    {
	      "blockHash":"0x645ed5b68379bfce8827c6797e5ae430d73370080454914eb739f44d6f5e0091",
	      "blockNumber":"0x32a0d9",
	      "from":"0x78d3d552841415fe367f08acd869d0f0aa93e815",
	      "gas":"0x5208",
	      "gasPrice":"0x59682f07",
	      "hash":"0x605133d8dfe07579bfa28fffd72cd766e29ba15204ec5bbc48f121f639cda730",
	      "input":"0x",
	      "nonce":"0x0",
	      "to":"0x6063b58bed10c5b4d40660bd68b1a0317c3e883c",
	      "transactionIndex":"0x6",
	      "value":"0x16345785d8a0000",
	      "type":"0x0",
	      "chainId":"0xaa36a7",
	      "v":"0x1546d71",
	      "r":"0x850df3a3d8c6231bd7a07bc2f54bfff803dbe09a88ded4e21a40be19a1bcca0a",
	      "s":"0x9a88296814a63733043aa13281e6a0bf241b66e9b9436a2f209e65f4af7fe97"
	     }
}

Nova에서 제공하는 getTransactionByHash API를 이용해 실행된 트랜잭션에 대한 데이터를 확인할 수 있습니다.

const axios = require('axios');

const txHash = "{Your Tx Hash}"
const options = {
  method: 'GET',
  url: 'https://web3.luniverse.io/v1/ethereum/sepolia/transactions/${txHash}',
  headers: {
    accept: 'application/json',
    Authorization: 'Bearer {Your Access Token}'
  }
};

axios
  .request(options)
  .then(function (response) {
    console.log(response.data);
  })
  .catch(function (error) {
    console.error(error);
  });

getTransactionByHash 의 응답은 다음과 같습니다. getTransactionReceipt Method를 이용하였을 때 반환받는 응답값과 같은 것을 확인할 수 있습니다.

{
  "code": "SUCCESS",
  "data": {
    "path": "/ethereum/sepolia/transactions/0x605133d8dfe07579bfa28fffd72cd766e29ba15204ec5bbc48f121f639cda730",
    "hash": "0x605133d8dfe07579bfa28fffd72cd766e29ba15204ec5bbc48f121f639cda730",
    "from": "0x78d3d552841415fe367f08acd869d0f0aa93e815",
    "to": "0x6063b58bed10c5b4d40660bd68b1a0317c3e883c",
    "value": "0x16345785d8a0000",
    "data": "0x",
    "nonce": "0x0",
    "gas": "0x5208",
    "gasPrice": "0x59682f07",
    "maxFeePerGas": null,
    "maxPriorityFeePerGas": null,
    "timestamp": 1681878972,
    "block": "0x645ed5b68379bfce8827c6797e5ae430d73370080454914eb739f44d6f5e0091",
    "status": "success",
    "receipt": {
      "from": "0x78d3d552841415fe367f08acd869d0f0aa93e815",
      "to": "0x6063b58bed10c5b4d40660bd68b1a0317c3e883c",
      "gasUsed": "0x5208",
      "logs": [],
      "cumulativeGasUsed": "0x1d37ef",
      "effectiveGasPrice": "0x59682f07",
      "contractAddress": null,
      "transactionIndex": 6,
      "transactionHash": "0x605133d8dfe07579bfa28fffd72cd766e29ba15204ec5bbc48f121f639cda730",
      "status": "0x1"
    }
  }
}

지금까지 Ethereum Transaction이 무엇인지, 구조가 어떻게 되는지 확인해보고 실제 Transaction을 실행해 보았습니다. Transaction이 어떤 것인지 이해가 되시나요? Ethereum은 Transaction이 상태를 변화시키고 이러한 Transaction을 모아 Block을 생성하여 상태 변화를 확정하는 구조 입니다.

다음 시간에는 이러한 Transaction을 발생시킬 수 있는 Ethereum 네트워크의 핵심 개념 중 하나인 Account와 EVM에 대해 학습을 하도록 하겠습니다.

Luniverse는 Public Blockchain을 손 쉽게 이용할 수 있도록 다양한 API와 Node Provider Service를 제공하고 있습니다. 오늘 학습한 내용 외에도 다양한 API를 제공하고 있으며 여기(Luniverse Multichain API 확인하기)를 클릭하여 Nova가 제공하는 다양한 API를 확인해 볼 수 있습니다! 😄😄