공개키를 사용해 암호화한 파일을 개인키를 보유한 사람에게 보내고, 개인키를 사용해 복호화한다. 즉, 상대방에게 공개키를 전달 후 암호화된 내용을 전달 받아 그 내용은 암호화한 사람과 복호화한 사람만 알 수 있다. 비대칭키 암/복호화는 데이터의 보안성과 기밀성을 유지할 수 있다.
이것을 반대로 사용하면 암호화 전자서명으로 활용할 수 있다.
특정 내용을 개인키를 사용해 서명하고 그 내용을 필요한 사람에게 보낸다. 서명된 데이터를 받은 사람은 공개키를 사용해 서명을 검증한다.
서명이 검증되면 서명한 사람이 보낸것임을 알 수 있다.
전자서명은 데이터의 무결성과 발신자의 인증을 보장할 수 있다.
블록체인에서는 이런 전자서명을 트랜잭션 보낼때 보내는 주체(어드레스)가 트랜잭션을 생성한 것인지 증명하기 위해 서명을 한다. 이는 트랜잭션의 무결성과 발신자의 인증을 보장하기 위해 사용된다.
콘솔에서 eth.signTransaction() 통해 서명 가능
서명시 gas, gasPrice, nonce 매개변수 필요.
논스값 확인
> eth.getTransactionCount(eth.accounts[0])
4
서명
> eth.signTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(111, "ether"), gas:21000, gasPrice: 1000000000, nonce:5})
{
raw: "0xf86e05843b9aca0082520894c51efd9613dac64386a8f6d26dd1b7c95effe74f8906046f37e5945c000080826096a08b98717aaea0257f100540c728b7327509c136fb972429d306998eac6421d43aa041545be6c1550bfc5e53977244fc47941dedfc0b5bdb94fce918171d1c4d8a50",
tx: {
gas: "0x5208",
gasPrice: "0x3b9aca00",
hash: "0x44061c062223e0c338c40692b97476a27eef61c4ed18b5998f0841b30b545346",
input: "0x",
maxFeePerGas: null,
maxPriorityFeePerGas: null,
nonce: "0x5",
r: "0x8b98717aaea0257f100540c728b7327509c136fb972429d306998eac6421d43a",
s: "0x41545be6c1550bfc5e53977244fc47941dedfc0b5bdb94fce918171d1c4d8a50",
to: "0xc51efd9613dac64386a8f6d26dd1b7c95effe74f",
type: "0x0",
v: "0x6096",
value: "0x6046f37e5945c0000"
}
}
gasPrice는 1GWEI임
raw 필드는 트랜잭션 전체 데이터의 직렬화된 형태를 나타낸다. 이 데이터는 트랜잭션의 모든 구성 요소(예: nonce, gasPrice, gasLimit, to, value, data 등)와 서명 정보(서명자의 개인키로 서명된 정보)를 포함한다. 이는 트랜잭션을 네트워크에 전송하는 데 필요한 모든 정보를 담고 있다.
R과 S (서명 값): ECDSA 서명은 크게 두 부분, r과 s로 구성된다. 이들은 서명 과정에서 생성된 두 개의 숫자 값이며, 각각 32바이트의 크기를 가진다. 이 값들은 원래의 데이터와 개인키를 사용하여 계산되며, 트랜잭션의 무결성과 발신자의 인증을 증명하는 데 핵심적인 역할을 한다.
V (복구 식별자): v는 서명에서 공개키를 복구하는 데 사용되는 식별자이다. 이더리움과 같은 일부 블록체인 시스템에서는 v 값을 사용하여 서명을 한 사람의 공개키를 유추할 수 있다. 이는 트랜잭션의 발신자를 확인하는 데 중요한 역할을 한다.
공개키 복구: r, s 값과 함께 v 값을 사용하면, 공개키를 복구할 수 있다. 이 과정을 통해 트랜잭션이 특정 개인키에 의해 서명되었음을 확인하고, 해당 트랜잭션의 발신자가 누구인지 파악할 수 있다.
/go-ethereum/core/types/transaction.go
// WithSignature returns a new transaction with the given signature.
// This signature needs to be in the [R || S || V] format where V is 0 or 1.
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
r, s, v, err := signer.SignatureValues(tx, sig)
if err != nil {
return nil, err
}
cpy := tx.inner.copy()
cpy.setSignatureValues(signer.ChainID(), v, r, s)
return &Transaction{inner: cpy, time: tx.time}, nil
}
서명 함수는 타입에 따라 차이가 있다.
/go-ethereum/core/types/transaction_signing.go
func (s cancunSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
txdata, ok := tx.inner.(*BlobTx)
if !ok {
return s.londonSigner.SignatureValues(tx, sig)
}
// Check that chain ID of tx matches the signer. We also accept ID zero here,
// because it indicates that the chain ID was not specified in the tx.
if txdata.ChainID.Sign() != 0 && txdata.ChainID.ToBig().Cmp(s.chainId) != 0 {
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
}
R, S, _ = decodeSignature(sig)
V = big.NewInt(int64(sig[64]))
return R, S, V, nil
}
func (s londonSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
txdata, ok := tx.inner.(*DynamicFeeTx)
if !ok {
return s.eip2930Signer.SignatureValues(tx, sig)
}
// Check that chain ID of tx matches the signer. We also accept ID zero here,
// because it indicates that the chain ID was not specified in the tx.
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
}
R, S, _ = decodeSignature(sig)
V = big.NewInt(int64(sig[64]))
return R, S, V, nil
}
func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
switch txdata := tx.inner.(type) {
case *LegacyTx:
return s.EIP155Signer.SignatureValues(tx, sig)
case *AccessListTx:
// Check that chain ID of tx matches the signer. We also accept ID zero here,
// because it indicates that the chain ID was not specified in the tx.
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
}
R, S, _ = decodeSignature(sig)
V = big.NewInt(int64(sig[64]))
default:
return nil, nil, nil, ErrTxTypeNotSupported
}
return R, S, V, nil
}
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
if tx.Type() != LegacyTxType {
return nil, nil, nil, ErrTxTypeNotSupported
}
R, S, V = decodeSignature(sig)
if s.chainId.Sign() != 0 {
V = big.NewInt(int64(sig[64] + 35))
V.Add(V, s.chainIdMul)
}
return R, S, V, nil
}
raw값 즉, 직렬화된 전체 트랜잭션 데이터를 매개변수로 eth.sendRawTransaction()로 트랜잭션을 전송한다. 트랜잭션 해시가 리턴된다.
> eth.sendRawTransaction("0xf86e05843b9aca0082520894c51efd9613dac64386a8f6d26dd1b7c95effe74f8906046f37e5945c000080826096a08b98717aaea0257f100540c728b7327509c136fb972429d306998eac6421d43aa041545be6c1550bfc5e53977244fc47941dedfc0b5bdb94fce918171d1c4d8a50")
"0x44061c062223e0c338c40692b97476a27eef61c4ed18b5998f0841b30b545346"
트랜잭션 해시로 확인
> eth.getTransaction("0x44061c062223e0c338c40692b97476a27eef61c4ed18b5998f0841b30b545346")
{
blockHash: null,
blockNumber: null,
chainId: "0x3039",
from: "0x6730ae899d1b135f13544543c88f354b5d79a715",
gas: 21000,
gasPrice: 1000000000,
hash: "0x44061c062223e0c338c40692b97476a27eef61c4ed18b5998f0841b30b545346",
input: "0x",
nonce: 5,
r: "0x8b98717aaea0257f100540c728b7327509c136fb972429d306998eac6421d43a",
s: "0x41545be6c1550bfc5e53977244fc47941dedfc0b5bdb94fce918171d1c4d8a50",
to: "0xc51efd9613dac64386a8f6d26dd1b7c95effe74f",
transactionIndex: null,
type: "0x0",
v: "0x6096",
value: 111000000000000000000
}
노드가 마이닝 중인데 블록 정보가 없는것으로보아 블록에 포함되지 못함.
가스, 가스 프라이스를 수정해서 실행해보았으나 트랜잭션이 블록에 포함되지 못함
eth.mining 이 true인데 eth.hashrate는 0인 상황, 노드를 껐다 켜도 마이너를 멈추고 다시 재실행해도 결과는 같음, 노드 실행은 다음 명령어로 실행함
./geth --datadir ./data --http --http.api "admin, debug, web3, eth, txpool, personal, ethash, miner, net" --mine --miner.threads "1" --allow-insecure-unlock --unlock 0 console
트랜잭션에서 사용한 from 계정은 코인베이스 였는데 이 계정에 이더도 충분히 보유하고 있음.
todo: 일단 보류하고 학습 진도부터 나간후 처리
eth.sendTransaction()을 사용하였을 경우 정상적으로 블록에 담김 확인
sendRawTransaction() 사용 시 파라메터 적용에 오류가 있어 발생한 이슈가 아닐까 싶음, 디버깅에 도움이될 로그 기능 여부와 활성화 옵션 확인 필요.eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(10, "ether")}) "0x9972b64a91009dc91c010d1b672b358c6534886713d84fc6fb870d94c8e37ffc" eth.getTransaction("0x9972b64a91009dc91c010d1b672b358c6534886713d84fc6fb870d94c8e37ffc") { blockHash: "0xe62838f4090185e39630797e14742cfdeac01e910ca98bc4a9963a204803eb19", blockNumber: 1225, chainId: "0x3039", from: "0x2da28e108530892c660dca151762ec3304b63127", gas: 21000, gasPrice: 1000000000, hash: "0x9972b64a91009dc91c010d1b672b358c6534886713d84fc6fb870d94c8e37ffc", input: "0x", nonce: 0, r: "0xa117c3841cc778e38588a9815fb29e6ff66cfb5298fad6aa69d3857ce76bd28f", s: "0x550d67a4bfdf5f4fbf82aa47ac31405aa6bc7bdac2976e309d20069c887ce442", to: "0x7a37056b9fe5bf9d3ee613529e3db76ab5e46e1d", transactionIndex: 0, type: "0x0", v: "0x6096", value: 10000000000000000000 }