-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
eth/chains: implement eip 155 for replay protection (#20)
* eth/chains: implement eip 155 for replay protection * spec: test for legacy (pre 155) v values * eth/chains: fix yard docs
- Loading branch information
Showing
3 changed files
with
204 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# Copyright (c) 2016-2022 The Ruby-Eth Contributors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
# Provides the `Eth` module. | ||
module Eth | ||
|
||
# Encapsulates `Chains` IDs and utilities for EIP-155 compatibility. | ||
# ref: https://eips.ethereum.org/EIPS/eip-155 | ||
module Chains | ||
extend self | ||
|
||
# Chain ID for Ethereum mainnet | ||
ETHEREUM = 1 | ||
|
||
# Chain ID for Expanse mainnet | ||
EXPANSE = 2 | ||
|
||
# Chain ID for Optimistic Ethereum mainnet | ||
OPTIMISM = 10 | ||
|
||
# Chain ID for Ethereum Classic mainnet | ||
CLASSIC = 61 | ||
|
||
# Chain ID for POA Network mainnet | ||
POA_NET = 99 | ||
|
||
# Chain ID for xDAI mainnet | ||
XDAI = 100 | ||
|
||
# Chain ID for Arbitrum mainnet | ||
ARBITRUM = 42161 | ||
|
||
# Chain ID for Morden (Ethereum) testnet | ||
MORDEN = 2 | ||
|
||
# Chain ID for Ropsten testnet | ||
ROPSTEN = 3 | ||
|
||
# Chain ID for Rinkeby testnet | ||
RINKEBY = 4 | ||
|
||
# Chain ID for Goerli testnet | ||
GOERLI = 5 | ||
|
||
# Chain ID for Kotti testnet | ||
KOTTI = 6 | ||
|
||
# Chain ID for Kovan testnet | ||
KOVAN = 42 | ||
|
||
# Chain ID for Morden (Classic) testnet | ||
MORDEN_CLASSIC = 62 | ||
|
||
# Chain ID for Mordor testnet | ||
MORDOR = 63 | ||
|
||
# Chain ID for Optimistik Kovan testnet | ||
KOVAN_OPTIMISM = 69 | ||
|
||
# Chain ID for Arbitrum xDAI testnet | ||
XDAI_ARBITRUM = 200 | ||
|
||
# Chain ID for Optimistic Goerli testnet | ||
GOERLI_OPTIMISM = 420 | ||
|
||
# Chain ID for Arbitrum Rinkeby testnet | ||
RINKEBY_ARBITRUM = 421611 | ||
|
||
# Chain ID for the geth private network preset | ||
PRIVATE_GETH = 1337 | ||
|
||
# Indicates wether the given `v` indicates a legacy chain value | ||
# without EIP-155 replay protection. | ||
# | ||
# @param v [Integer] the signature's `v` value | ||
# @return [Boolean] true if legacy value | ||
def is_legacy? v | ||
[27, 28].include? v | ||
end | ||
|
||
# Convert a given `v` value to an ECDSA recovery id for the given | ||
# EIP-155 chain ID. | ||
# | ||
# @param v [Integer] the signature's `v` value | ||
# @param chain [Integer] the chain id the signature was generated on. | ||
# @return [Integer] the recovery id corresponding to `v`. | ||
# @raise [ArgumentError] if the given `v` is invalid. | ||
def to_recov v, chain = ETHEREUM | ||
x = 0 + 2 * chain + 35 | ||
y = 1 + 2 * chain + 35 | ||
if is_legacy? v | ||
return v - 27 | ||
elsif [x, y].include? v | ||
return v - 35 - 2 * chain | ||
else | ||
raise ArgumentError, "Invalid v value for chain #{chain}. Invalid chain ID?" | ||
end | ||
end | ||
|
||
# Converts a recovery ID into the expected `v` on a given chain. | ||
# | ||
# @param recov [Integer] signature recovery id. | ||
# @param chain [Integer] the chain id the signature was generated on. | ||
# @return [Integer] the signature's `v` value. | ||
def to_v recov, chain = ETHEREUM | ||
v = 2 * chain + 35 + recov | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
require 'spec_helper' | ||
|
||
describe Eth::Chains do | ||
describe "#CHAIN_ID" do | ||
it "has EIP155 chain ids for mainnets, testnets, and devnets" do | ||
# Chain IDs for selected mainnets | ||
expect(Eth::Chains::ETHEREUM).to eq 1 | ||
expect(Eth::Chains::EXPANSE).to eq 2 | ||
expect(Eth::Chains::OPTIMISM).to eq 10 | ||
expect(Eth::Chains::CLASSIC).to eq 61 | ||
expect(Eth::Chains::POA_NET).to eq 99 | ||
expect(Eth::Chains::XDAI).to eq 100 | ||
expect(Eth::Chains::ARBITRUM).to eq 42161 | ||
|
||
# Chain IDs for selected testnets | ||
expect(Eth::Chains::MORDEN).to eq 2 | ||
expect(Eth::Chains::ROPSTEN).to eq 3 | ||
expect(Eth::Chains::RINKEBY).to eq 4 | ||
expect(Eth::Chains::GOERLI).to eq 5 | ||
expect(Eth::Chains::KOTTI).to eq 6 | ||
expect(Eth::Chains::KOVAN).to eq 42 | ||
expect(Eth::Chains::MORDEN_CLASSIC).to eq 62 | ||
expect(Eth::Chains::MORDOR).to eq 63 | ||
expect(Eth::Chains::KOVAN_OPTIMISM).to eq 69 | ||
expect(Eth::Chains::XDAI_ARBITRUM).to eq 200 | ||
expect(Eth::Chains::GOERLI_OPTIMISM).to eq 420 | ||
expect(Eth::Chains::RINKEBY_ARBITRUM).to eq 421611 | ||
|
||
# Chain IDs for selected private networks | ||
expect(Eth::Chains::PRIVATE_GETH).to eq 1337 | ||
end | ||
end | ||
|
||
describe ".is_legacy" do | ||
it "can detect legacy values for v" do | ||
expect(Eth::Chains.is_legacy? 0).not_to be_truthy | ||
expect(Eth::Chains.is_legacy? 1).not_to be_truthy | ||
expect(Eth::Chains.is_legacy? 27).to be_truthy | ||
expect(Eth::Chains.is_legacy? 28).to be_truthy | ||
expect(Eth::Chains.is_legacy? 37).not_to be_truthy | ||
expect(Eth::Chains.is_legacy? 38).not_to be_truthy | ||
end | ||
end | ||
|
||
describe ".to_v .to_recovery_id" do | ||
it "can convert ethereum recovery ids to v" do | ||
expect(Eth::Chains.to_v 0).to be 37 | ||
expect(Eth::Chains.to_v 1).to be 38 | ||
end | ||
it "can convert other chain's recovery ids to v" do | ||
expect(Eth::Chains.to_v 0, Eth::Chains::CLASSIC).to be 157 | ||
expect(Eth::Chains.to_v 1, Eth::Chains::XDAI).to be 236 | ||
expect(Eth::Chains.to_v 0, Eth::Chains::ARBITRUM).to be 84357 | ||
expect(Eth::Chains.to_v 1, Eth::Chains::MORDEN_CLASSIC).to be 160 | ||
expect(Eth::Chains.to_v 0, Eth::Chains::GOERLI_OPTIMISM).to be 875 | ||
expect(Eth::Chains.to_v 1, Eth::Chains::RINKEBY_ARBITRUM).to be 843258 | ||
expect(Eth::Chains.to_v 0, Eth::Chains::PRIVATE_GETH).to be 2709 | ||
end | ||
it "can recover v from ethereum recovery id" do | ||
expect(Eth::Chains.to_recov 37).to be 0 | ||
expect(Eth::Chains.to_recov 38).to be 1 | ||
|
||
# legacy v | ||
expect(Eth::Chains.to_recov 27).to be 0 | ||
expect(Eth::Chains.to_recov 28).to be 1 | ||
end | ||
it "can recover v from other chain's recovery id" do | ||
expect(Eth::Chains.to_recov 157, Eth::Chains::CLASSIC).to be 0 | ||
expect(Eth::Chains.to_recov 236, Eth::Chains::XDAI).to be 1 | ||
expect(Eth::Chains.to_recov 84357, Eth::Chains::ARBITRUM).to be 0 | ||
expect(Eth::Chains.to_recov 160, Eth::Chains::MORDEN_CLASSIC).to be 1 | ||
expect(Eth::Chains.to_recov 875, Eth::Chains::GOERLI_OPTIMISM).to be 0 | ||
expect(Eth::Chains.to_recov 843258, Eth::Chains::RINKEBY_ARBITRUM).to be 1 | ||
expect(Eth::Chains.to_recov 2709, Eth::Chains::PRIVATE_GETH).to be 0 | ||
end | ||
it "raises an error for invalid v on chain ids" do | ||
expect {Eth::Chains.to_recov 0}.to raise_error ArgumentError | ||
expect {Eth::Chains.to_recov 36}.to raise_error ArgumentError | ||
expect {Eth::Chains.to_recov 843258, Eth::Chains::PRIVATE_GETH}.to raise_error ArgumentError | ||
end | ||
end | ||
end |