diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 2e4922f55d..d77340abc0 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -17,6 +17,7 @@ package vm import ( + "crypto/sha1" "crypto/sha256" "encoding/binary" "errors" @@ -33,6 +34,8 @@ import ( "github.com/ethereum/go-ethereum/crypto/secp256r1" "github.com/ethereum/go-ethereum/libplanet" "github.com/ethereum/go-ethereum/params" + "github.com/sircoon4/bencodex-go" + "github.com/sircoon4/bencodex-go/bencodextype" "golang.org/x/crypto/ripemd160" ) @@ -83,15 +86,17 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ // PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum // contracts used in the Berlin release. var PrecompiledContractsBerlin = 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{eip2565: true}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, + 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{eip2565: true}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{0x02, 0x00}): &libplanetVerifyProof{}, + common.BytesToAddress([]byte{0x02, 0x01}): &libplanetWithdrawalTransactionHashing{}, } // PrecompiledContractsCancun contains the default set of pre-compiled Ethereum @@ -124,6 +129,7 @@ var PrecompiledContractsFjord = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{}, common.BytesToAddress([]byte{0x01, 0x00}): &p256Verify{}, common.BytesToAddress([]byte{0x02, 0x00}): &libplanetVerifyProof{}, + common.BytesToAddress([]byte{0x02, 0x01}): &libplanetWithdrawalTransactionHashing{}, } // PrecompiledContractsBLS contains the set of pre-compiled Ethereum @@ -1206,7 +1212,7 @@ func (c *libplanetVerifyProof) Run(input []byte) ([]byte, error) { proofMap := map[string]any{ "stateRootHash": nil, // sha256(bencoded) []byte "proof": nil, // bencoded(list) []byte - "key": nil, // keyBytes []byte + "key": nil, // keyBytes | address []byte "value": nil, // bencoded []byte } proofMap, err := libplanet.ParseMerkleTrieProofInput(input) @@ -1223,3 +1229,42 @@ func (c *libplanetVerifyProof) Run(input []byte) ([]byte, error) { return common.CopyBytes(libplanet.BoolAbi(valid)), nil } + +type libplanetWithdrawalTransactionHashing struct{} + +func (c *libplanetWithdrawalTransactionHashing) RequiredGas(input []byte) uint64 { + return params.LibplanetWithdrawalTransactionHashingGas +} + +func (c *libplanetWithdrawalTransactionHashing) Run(input []byte) ([]byte, error) { + withdrawalTransaction := map[string]any{ + "nonce": nil, // *big.Int + "from": nil, // common.Address + "to": nil, // common.Address + "amount": nil, // *big.Int + } + withdrawalTransaction, err := libplanet.ParseWithdrawalTransactionInput(input) + if err != nil { + return nil, err + } + + nonce := withdrawalTransaction["nonce"].(*big.Int) + from := withdrawalTransaction["from"].(common.Address).Bytes() + to := withdrawalTransaction["to"].(common.Address).Bytes() + amount := withdrawalTransaction["amount"].(*big.Int) + + dict := bencodextype.NewDictionary() + dict.Set("nonce", nonce) + dict.Set("from", from) + dict.Set("to", to) + dict.Set("amount", amount) + + encoded, err := bencodex.Encode(dict) + if err != nil { + return nil, err + } + + sum := sha1.Sum(encoded) + + return common.CopyBytes(libplanet.AddressAbi(sum)), nil +} diff --git a/libplanet/merkle_trie_proof.go b/libplanet/merkle_trie_proof.go index 1976f62428..7362c24d72 100644 --- a/libplanet/merkle_trie_proof.go +++ b/libplanet/merkle_trie_proof.go @@ -13,6 +13,10 @@ func ValidateProof( key []byte, // []byte value []byte, // bencoded ) (bool, error) { + key, err := validProofKey(key) + if err != nil { + return false, err + } targetHash := stateRootHash nibbles := keybytesToNibbles(key) decodedProofList, err := bencodex.Decode(proof) diff --git a/libplanet/merkle_trie_proof_utils.go b/libplanet/merkle_trie_proof_utils.go index b03aa6d85f..b92bf812a5 100644 --- a/libplanet/merkle_trie_proof_utils.go +++ b/libplanet/merkle_trie_proof_utils.go @@ -120,3 +120,29 @@ func resolveToNextCandidateNode( return nil, nil, fmt.Errorf("invalid proof node") } + +func validProofKey(key []byte) ([]byte, error) { + if len(key) == 20 { + return toStateKey(key), nil + } + + if len(key) == 40 { + return key, nil + } + + return nil, fmt.Errorf("invalid key length") +} + +func toStateKey(key []byte) []byte { + var _conversionTable = []byte{ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, + } + + l := len(key) + var stateKey = make([]byte, l*2) + for i, b := range key { + stateKey[i*2] = _conversionTable[b>>4] + stateKey[i*2+1] = _conversionTable[b&0xf] + } + return stateKey +} diff --git a/libplanet/withdrawal_transaction_utils.go b/libplanet/withdrawal_transaction_utils.go new file mode 100644 index 0000000000..ba598bfc66 --- /dev/null +++ b/libplanet/withdrawal_transaction_utils.go @@ -0,0 +1,46 @@ +package libplanet + +import ( + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" +) + +func ParseWithdrawalTransactionInput(input []byte) (map[string]any, error) { + Uint256, _ := abi.NewType("uint256", "", nil) + Address, _ := abi.NewType("address", "", nil) + + var arguments = abi.Arguments{ + abi.Argument{Name: "nonce", Type: Uint256, Indexed: false}, + abi.Argument{Name: "from", Type: Address, Indexed: false}, + abi.Argument{Name: "to", Type: Address, Indexed: false}, + abi.Argument{Name: "amount", Type: Uint256, Indexed: false}, + } + + decoded := map[string]any{ + "nonce": nil, // *big.Int + "from": nil, // common.Address + "to": nil, // common.Address + "amount": nil, // *big.Int + } + err := arguments.UnpackIntoMap(decoded, input) + if err != nil { + return nil, err + } + + return decoded, nil +} + +func AddressAbi(input [20]byte) []byte { + + Address, _ := abi.NewType("address", "", nil) + + var arguments = abi.Arguments{ + abi.Argument{Name: "address", Type: Address, Indexed: false}, + } + + encoded, err := arguments.Pack(common.BytesToAddress(input[:])) + if err != nil { + panic(err) + } + return encoded +} diff --git a/params/protocol_params.go b/params/protocol_params.go index 3c14f50d8f..f71762d139 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -166,8 +166,9 @@ const ( Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation Bls12381MapG2Gas uint64 = 110000 // Gas price for BLS12-381 mapping field element to G2 operation - P256VerifyGas uint64 = 3450 // secp256r1 elliptic curve signature verifier gas price - LibplanetVerifyProofGas uint64 = 3000 // Libplanet proof verifier gas price + P256VerifyGas uint64 = 3450 // secp256r1 elliptic curve signature verifier gas price + LibplanetVerifyProofGas uint64 = 3000 // Libplanet proof verifier gas price + LibplanetWithdrawalTransactionHashingGas uint64 = 3000 // Libplanet withdraw transaction hashing gas price // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529, // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529