Implementation of SHA3-256 FIPS 202 hash precompile and EcRecover Uncompressed public key precompile to integrate with ICON blockchain (#3801)

* SHA3-256 FIPS-202 precompiled contract implementation

* go fmt

* Added precompiles address under VRF

* added sha3 prcompiles to Istanbul

* fix goimports error

* Updated the sha3 & ecreceover pk contract address to 253 & 254 respectively

* Added a new PrecompiledContractsSHA3FIPS and add  forking epoch & Changed the gas prices for sha3

Co-authored-by: Ganesha Upadhyaya <ganeshrvce@gmail.com>
pull/3882/head
Simsonraj Easvarasakthi 3 years ago committed by GitHub
parent 0e4563f7e1
commit 70d958370f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 79
      core/vm/contracts.go
  2. 64
      core/vm/contracts_test.go
  3. 6
      core/vm/evm.go
  4. 26
      internal/params/config.go
  5. 5
      internal/params/protocol_params.go

@ -30,6 +30,11 @@ import (
"github.com/ethereum/go-ethereum/crypto/bn256" "github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/params"
"golang.org/x/crypto/ripemd160" "golang.org/x/crypto/ripemd160"
//Needed for SHA3-256 FIPS202
"encoding/hex"
"golang.org/x/crypto/sha3"
) )
// PrecompiledContract is the basic interface for native Go contracts. The implementation // PrecompiledContract is the basic interface for native Go contracts. The implementation
@ -91,6 +96,24 @@ var PrecompiledContractsVRF = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{255}): &vrf{}, common.BytesToAddress([]byte{255}): &vrf{},
} }
// PrecompiledContractsSHA3FIPS contains the default set of pre-compiled Ethereum
// contracts used in the Istanbul release. plus VRF and SHA3FIPS-202 standard
var PrecompiledContractsSHA3FIPS = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{255}): &vrf{},
common.BytesToAddress([]byte{253}): &sha3fip{},
common.BytesToAddress([]byte{254}): &ecrecoverPublicKey{},
}
// RunPrecompiledContract runs and evaluates the output of a precompiled contract. // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(input) gas := p.RequiredGas(input)
@ -533,3 +556,59 @@ func (c *vrf) Run(input []byte) ([]byte, error) {
// So here we simply return it // So here we simply return it
return append([]byte{}, input...), nil return append([]byte{}, input...), nil
} }
// SHA3-256 FIPS 202 standard implemented as a native contract.
type sha3fip struct{}
// TODO Check if the gas price calculation needs modification
// RequiredGas returns the gas required to execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *sha3fip) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha3FipsWordGas + params.Sha3FipsGas
}
func (c *sha3fip) Run(input []byte) ([]byte, error) {
hexStr := common.Bytes2Hex(input)
pub, _ := hex.DecodeString(hexStr)
h := sha3.Sum256(pub[:])
return h[:], nil
}
// ECRECOVER implemented as a native contract.
type ecrecoverPublicKey struct{}
func (c *ecrecoverPublicKey) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}
func (c *ecrecoverPublicKey) Run(input []byte) ([]byte, error) {
const ecrecoverPublicKeyInputLength = 128
input = common.RightPadBytes(input, ecrecoverPublicKeyInputLength)
// "input" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)
r := new(big.Int).SetBytes(input[64:96])
s := new(big.Int).SetBytes(input[96:128])
v := input[63]
// tighter sig s values input homestead only apply to tx sigs
if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
return nil, nil
}
// We must make sure not to modify the 'input', so placing the 'v' along with
// the signature needs to be done on a new allocation
sig := make([]byte, 65)
copy(sig, input[64:128])
sig[64] = v
// v needs to be at the end for libsecp256k1
pubKey, err := crypto.Ecrecover(input[:32], sig)
// make sure the public key is a valid one
if err != nil {
return nil, nil
}
return pubKey, nil
}

@ -399,7 +399,7 @@ var blake2FTests = []precompiledTest{
} }
func testPrecompiled(addr string, test precompiledTest, t *testing.T) { func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] p := PrecompiledContractsSHA3FIPS[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input) in := common.Hex2Bytes(test.input)
contract := NewContract(AccountRef(common.HexToAddress("1337")), contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), p.RequiredGas(in)) nil, new(big.Int), p.RequiredGas(in))
@ -418,7 +418,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
} }
func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] p := PrecompiledContractsSHA3FIPS[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input) in := common.Hex2Bytes(test.input)
contract := NewContract(AccountRef(common.HexToAddress("1337")), contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), p.RequiredGas(in)-1) nil, new(big.Int), p.RequiredGas(in)-1)
@ -436,7 +436,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
} }
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) { func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] p := PrecompiledContractsSHA3FIPS[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input) in := common.Hex2Bytes(test.input)
contract := NewContract(AccountRef(common.HexToAddress("31337")), contract := NewContract(AccountRef(common.HexToAddress("31337")),
nil, new(big.Int), p.RequiredGas(in)) nil, new(big.Int), p.RequiredGas(in))
@ -458,7 +458,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
if test.noBenchmark { if test.noBenchmark {
return return
} }
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] p := PrecompiledContractsSHA3FIPS[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input) in := common.Hex2Bytes(test.input)
reqGas := p.RequiredGas(in) reqGas := p.RequiredGas(in)
contract := NewContract(AccountRef(common.HexToAddress("1337")), contract := NewContract(AccountRef(common.HexToAddress("1337")),
@ -661,3 +661,59 @@ func TestPrecompiledEcrecover(t *testing.T) {
} }
} }
// sha3fip test vectors
var sha3fipTests = []precompiledTest{
{
input: "0448250ebe88d77e0a12bcf530fe6a2cf1ac176945638d309b840d631940c93b78c2bd6d16f227a8877e3f1604cd75b9c5a8ab0cac95174a8a0a0f8ea9e4c10bca",
expected: "c7647f7e251bf1bd70863c8693e93a4e77dd0c9a689073e987d51254317dc704",
name: "sha3fip",
},
{
input: "1234",
expected: "19becdc0e8d6dd4aa2c9c2983dbb9c61956a8ade69b360d3e6019f0bcd5557a9",
name: "sha3fip",
},
}
func TestPrecompiledSHA3fip(t *testing.T) {
for _, test := range sha3fipTests {
testPrecompiled("FD", test, t)
}
}
// EcRecover test vectors
var ecRecoverPublicKeyTests = []precompiledTest{
{
input: "c5d6c454e4d7a8e8a654f5ef96e8efe41d21a65b171b298925414aa3dc061e37" +
"0000000000000000000000000000000000000000000000000000000000000000" +
"4011de30c04302a2352400df3d1459d6d8799580dceb259f45db1d99243a8d0c" +
"64f548b7776cb93e37579b830fc3efce41e12e0958cda9f8c5fcad682c610795",
expected: "0448250ebe88d77e0a12bcf530fe6a2cf1ac176945638d309b840d631940c93b78c2bd6d16f227a8877e3f1604cd75b9c5a8ab0cac95174a8a0a0f8ea9e4c10bca",
name: "CallEcrecoverrecoverable Key",
},
{
input: "c5d6c454e4d7a8e8a654f5ef96e8efe41d21a65b171b298925414aa3dc061e37" +
"000000000000000000000000000000000000000000000000000000000000001b" +
"4011de30c04302a2352400df3d1459d6d8799580dceb259f45db1d99243a8d0c" +
"64f548b7776cb93e37579b830fc3efce41e12e0958cda9f8c5fcad682c610795",
expected: "",
name: "InvalidLowV-bits-1",
}, {
input: "c5d6c454e4d7a8e8a654f5ef96e8efe41d21a65b171b298925414aa3dc061e37" +
"000000000000000000000000000000000000000000000000000000000000001c" +
"4011de30c04302a2352400df3d1459d6d8799580dceb259f45db1d99243a8d0c" +
"64f548b7776cb93e37579b830fc3efce41e12e0958cda9f8c5fcad682c610795",
expected: "",
name: "InvalidLowV-bits-1",
},
}
func TestPrecompiledEcrecoverPublicKey(t *testing.T) {
for _, test := range ecRecoverPublicKeyTests {
testPrecompiled("FE", test, t)
}
}

@ -60,6 +60,9 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
if evm.chainRules.IsVRF { if evm.chainRules.IsVRF {
precompiles = PrecompiledContractsVRF precompiles = PrecompiledContractsVRF
} }
if evm.chainRules.IsSHA3 {
precompiles = PrecompiledContractsSHA3FIPS
}
if p := precompiles[*contract.CodeAddr]; p != nil { if p := precompiles[*contract.CodeAddr]; p != nil {
if _, ok := p.(*vrf); ok { if _, ok := p.(*vrf); ok {
if evm.chainRules.IsPrevVRF { if evm.chainRules.IsPrevVRF {
@ -262,6 +265,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if evm.chainRules.IsVRF { if evm.chainRules.IsVRF {
precompiles = PrecompiledContractsVRF precompiles = PrecompiledContractsVRF
} }
if evm.chainRules.IsSHA3 {
precompiles = PrecompiledContractsSHA3FIPS
}
if precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && value.Sign() == 0 { if precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer // Calling a non existing account, don't do anything, but ping the tracer

@ -62,6 +62,7 @@ var (
DataCopyFixEpoch: big.NewInt(689), // Around Wed Sept 15th 2021 with 3.5s block time DataCopyFixEpoch: big.NewInt(689), // Around Wed Sept 15th 2021 with 3.5s block time
IstanbulEpoch: big.NewInt(314), IstanbulEpoch: big.NewInt(314),
ReceiptLogEpoch: big.NewInt(101), ReceiptLogEpoch: big.NewInt(101),
SHA3Epoch: EpochTBD, //EpochTBD
} }
// TestnetChainConfig contains the chain parameters to run a node on the harmony test network. // TestnetChainConfig contains the chain parameters to run a node on the harmony test network.
@ -92,6 +93,7 @@ var (
DataCopyFixEpoch: big.NewInt(74412), DataCopyFixEpoch: big.NewInt(74412),
IstanbulEpoch: big.NewInt(43800), IstanbulEpoch: big.NewInt(43800),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: EpochTBD, //EpochTBD
} }
// PangaeaChainConfig contains the chain parameters for the Pangaea network. // PangaeaChainConfig contains the chain parameters for the Pangaea network.
@ -123,6 +125,7 @@ var (
DataCopyFixEpoch: big.NewInt(0), DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0), IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
} }
// PartnerChainConfig contains the chain parameters for the Partner network. // PartnerChainConfig contains the chain parameters for the Partner network.
@ -154,6 +157,7 @@ var (
DataCopyFixEpoch: big.NewInt(0), DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0), IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
} }
// StressnetChainConfig contains the chain parameters for the Stress test network. // StressnetChainConfig contains the chain parameters for the Stress test network.
@ -185,6 +189,7 @@ var (
DataCopyFixEpoch: big.NewInt(0), DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0), IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
} }
// LocalnetChainConfig contains the chain parameters to run for local development. // LocalnetChainConfig contains the chain parameters to run for local development.
@ -215,6 +220,7 @@ var (
DataCopyFixEpoch: big.NewInt(0), DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0), IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
} }
// AllProtocolChanges ... // AllProtocolChanges ...
@ -247,6 +253,7 @@ var (
big.NewInt(0), // DataCopyFixEpoch big.NewInt(0), // DataCopyFixEpoch
big.NewInt(0), // IstanbulEpoch big.NewInt(0), // IstanbulEpoch
big.NewInt(0), // ReceiptLogEpoch big.NewInt(0), // ReceiptLogEpoch
big.NewInt(0),
} }
// TestChainConfig ... // TestChainConfig ...
@ -279,6 +286,7 @@ var (
big.NewInt(0), // DataCopyFixEpoch big.NewInt(0), // DataCopyFixEpoch
big.NewInt(0), // IstanbulEpoch big.NewInt(0), // IstanbulEpoch
big.NewInt(0), // ReceiptLogEpoch big.NewInt(0), // ReceiptLogEpoch
big.NewInt(0),
} }
// TestRules ... // TestRules ...
@ -387,11 +395,14 @@ type ChainConfig struct {
// ReceiptLogEpoch is the first epoch support receiptlog // ReceiptLogEpoch is the first epoch support receiptlog
ReceiptLogEpoch *big.Int `json:"receipt-log-epoch,omitempty"` ReceiptLogEpoch *big.Int `json:"receipt-log-epoch,omitempty"`
// IsSHA3Epoch is the first epoch in supporting SHA3 FIPS-202 standard
SHA3Epoch *big.Int `json:"sha3-epoch,omitempty"`
} }
// String implements the fmt.Stringer interface. // String implements the fmt.Stringer interface.
func (c *ChainConfig) String() string { func (c *ChainConfig) String() string {
return fmt.Sprintf("{ChainID: %v EthCompatibleChainID: %v EIP155: %v CrossTx: %v Staking: %v CrossLink: %v ReceiptLog: %v}", return fmt.Sprintf("{ChainID: %v EthCompatibleChainID: %v EIP155: %v CrossTx: %v Staking: %v CrossLink: %v ReceiptLog: %v SHA3Epoch:%v}",
c.ChainID, c.ChainID,
c.EthCompatibleChainID, c.EthCompatibleChainID,
c.EIP155Epoch, c.EIP155Epoch,
@ -399,6 +410,7 @@ func (c *ChainConfig) String() string {
c.StakingEpoch, c.StakingEpoch,
c.CrossLinkEpoch, c.CrossLinkEpoch,
c.ReceiptLogEpoch, c.ReceiptLogEpoch,
c.SHA3Epoch,
) )
} }
@ -527,6 +539,11 @@ func (c *ChainConfig) IsReceiptLog(epoch *big.Int) bool {
return isForked(c.ReceiptLogEpoch, epoch) return isForked(c.ReceiptLogEpoch, epoch)
} }
// IsSHA3 returns whether epoch is either equal to the IsSHA3 fork epoch or greater.
func (c *ChainConfig) IsSHA3(epoch *big.Int) bool {
return isForked(c.SHA3Epoch, epoch)
}
// UpdateEthChainIDByShard update the ethChainID based on shard ID. // UpdateEthChainIDByShard update the ethChainID based on shard ID.
func UpdateEthChainIDByShard(shardID uint32) { func UpdateEthChainIDByShard(shardID uint32) {
once.Do(func() { once.Do(func() {
@ -575,9 +592,9 @@ func isForked(s, epoch *big.Int) bool {
// Rules is a one time interface meaning that it shouldn't be used in between transition // Rules is a one time interface meaning that it shouldn't be used in between transition
// phases. // phases.
type Rules struct { type Rules struct {
ChainID *big.Int ChainID *big.Int
EthChainID *big.Int EthChainID *big.Int
IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul, IsVRF, IsPrevVRF bool IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul, IsVRF, IsPrevVRF, IsSHA3 bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -600,5 +617,6 @@ func (c *ChainConfig) Rules(epoch *big.Int) Rules {
IsIstanbul: c.IsIstanbul(epoch), IsIstanbul: c.IsIstanbul(epoch),
IsVRF: c.IsVRF(epoch), IsVRF: c.IsVRF(epoch),
IsPrevVRF: c.IsPrevVRF(epoch), IsPrevVRF: c.IsPrevVRF(epoch),
IsSHA3: c.IsSHA3(epoch),
} }
} }

@ -170,6 +170,11 @@ const (
Bn256PairingBaseGasIstanbul uint64 = 45000 // Base price for an elliptic curve pairing check Bn256PairingBaseGasIstanbul uint64 = 45000 // Base price for an elliptic curve pairing check
Bn256PairingPerPointGasByzantium uint64 = 80000 // Byzantium per-point price for an elliptic curve pairing check Bn256PairingPerPointGasByzantium uint64 = 80000 // Byzantium per-point price for an elliptic curve pairing check
Bn256PairingPerPointGasIstanbul uint64 = 34000 // Per-point price for an elliptic curve pairing check Bn256PairingPerPointGasIstanbul uint64 = 34000 // Per-point price for an elliptic curve pairing check
//SHA3-FIPS Precompiled contracts gas price esstimation as per ethereum yellow paper appendix G
Sha3FipsGas uint64 = 30 // Once per SHA3-256 operation.
Sha3FipsWordGas uint64 = 6 // Once per word of the SHA3-256 operation's data.
) )
// nolint // nolint

Loading…
Cancel
Save