From 1be60070429a31f48e67c4064d630c24138dcd80 Mon Sep 17 00:00:00 2001 From: fnaticwang Date: Wed, 25 Sep 2019 10:48:04 +0800 Subject: [PATCH] precompiled contract for privacy contract --- accounts/abi/abi.go | 18 + core/vm/contracts.go | 709 +++++++++++++++++++++++++++++++++++++- core/vm/evm.go | 9 +- crypto/crypto.go | 107 ++++++ params/protocol_params.go | 3 + 5 files changed, 828 insertions(+), 18 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 7831a5ed33..e089376178 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -92,6 +92,24 @@ func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) { return fmt.Errorf("abi: could not locate named method or event") } +func (abi ABI) UnpackTmp(v interface{}, name string, output []byte) (err error) { + if len(output) == 0 { + return fmt.Errorf("abi: unmarshalling empty output") + } + // since there can't be naming collisions with contracts and events, + // we need to decide whether we're calling a method or an event + if method, ok := abi.Methods[name]; ok { + // old stamp tx can't run in testnet. + //if len(output)%32 != 0 { + // return fmt.Errorf("abi: improperly formatted output") + //} + return method.Outputs.Unpack(v, output) + } else if event, ok := abi.Events[name]; ok { + return event.Inputs.Unpack(v, output) + } + return fmt.Errorf("abi: could not locate named method or event") +} + // UnpackIntoMap unpacks a log into the provided map[string]interface{} func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) { if len(data) == 0 { diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 875054f89a..8ed32ae205 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -17,26 +17,69 @@ package vm import ( + "crypto/ecdsa" "crypto/sha256" "encoding/binary" "errors" + "fmt" "math/big" + "strings" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/blake2b" "github.com/ethereum/go-ethereum/crypto/bn256" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "golang.org/x/crypto/ripemd160" ) +var ( + useCoinPrecompileAddr = common.BytesToAddress([]byte{100}) + useStampPrecompileAddr = common.BytesToAddress([]byte{200}) + + otaBalanceStorageAddr = common.BytesToAddress(big.NewInt(300).Bytes()) + otaImageStorageAddr = common.BytesToAddress(big.NewInt(301).Bytes()) + + // 0.01wei --> "0x0000000000000000000000010000000000000000" + otaBalancePercentdot001WStorageAddr = common.HexToAddress(UseStampdot001) + otaBalancePercentdot002WStorageAddr = common.HexToAddress(UseStampdot002) + otaBalancePercentdot005WStorageAddr = common.HexToAddress(UseStampdot005) + + otaBalancePercentdot003WStorageAddr = common.HexToAddress(UseStampdot003) + otaBalancePercentdot006WStorageAddr = common.HexToAddress(UseStampdot006) + otaBalancePercentdot009WStorageAddr = common.HexToAddress(UseStampdot009) + + otaBalancePercentdot03WStorageAddr = common.HexToAddress(UseStampdot03) + otaBalancePercentdot06WStorageAddr = common.HexToAddress(UseStampdot06) + otaBalancePercentdot09WStorageAddr = common.HexToAddress(UseStampdot09) + otaBalancePercentdot2WStorageAddr = common.HexToAddress(UseStampdot2) + otaBalancePercentdot5WStorageAddr = common.HexToAddress(UseStampdot5) + + otaBalance10WStorageAddr = common.HexToAddress(Usecoin10) + otaBalance20WStorageAddr = common.HexToAddress(Usecoin20) + otaBalance50WStorageAddr = common.HexToAddress(Usecoin50) + otaBalance100WStorageAddr = common.HexToAddress(Usecoin100) + + otaBalance200WStorageAddr = common.HexToAddress(Usecoin200) + otaBalance500WStorageAddr = common.HexToAddress(Usecoin500) + otaBalance1000WStorageAddr = common.HexToAddress(Usecoin1000) + otaBalance5000WStorageAddr = common.HexToAddress(Usecoin5000) + otaBalance50000WStorageAddr = common.HexToAddress(Usecoin50000) +) + // PrecompiledContract is the basic interface for native Go contracts. The implementation // requires a deterministic gas count based on the input size of the Run method of the // contract. type PrecompiledContract interface { - RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use - Run(input []byte) ([]byte, error) // Run runs the precompiled contract + RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) // Run runs the precompiled contract + ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error } // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum @@ -46,6 +89,9 @@ var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{2}): &sha256hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{}, common.BytesToAddress([]byte{4}): &dataCopy{}, + + useCoinPrecompileAddr: &useCoinSC{}, + useStampPrecompileAddr: &usechainStampSC{}, } // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum @@ -59,6 +105,9 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{6}): &bn256AddByzantium{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{}, common.BytesToAddress([]byte{8}): &bn256PairingByzantium{}, + + useCoinPrecompileAddr: &useCoinSC{}, + useStampPrecompileAddr: &usechainStampSC{}, } // PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum @@ -73,13 +122,16 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, common.BytesToAddress([]byte{9}): &blake2F{}, + + useCoinPrecompileAddr: &useCoinSC{}, + useStampPrecompileAddr: &usechainStampSC{}, } // 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, evm *EVM) (ret []byte, err error) { gas := p.RequiredGas(input) if contract.UseGas(gas) { - return p.Run(input) + return p.Run(input, contract, evm) } return nil, ErrOutOfGas } @@ -91,7 +143,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(input []byte) ([]byte, error) { +func (c *ecrecover) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { const ecRecoverInputLength = 128 input = common.RightPadBytes(input, ecRecoverInputLength) @@ -117,6 +169,10 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) { return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil } +func (c *ecrecover) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // SHA256 implemented as a native contract. type sha256hash struct{} @@ -127,11 +183,15 @@ type sha256hash struct{} func (c *sha256hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas } -func (c *sha256hash) Run(input []byte) ([]byte, error) { +func (c *sha256hash) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { h := sha256.Sum256(input) return h[:], nil } +func (c *sha256hash) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // RIPEMD160 implemented as a native contract. type ripemd160hash struct{} @@ -142,12 +202,16 @@ type ripemd160hash struct{} func (c *ripemd160hash) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas } -func (c *ripemd160hash) Run(input []byte) ([]byte, error) { +func (c *ripemd160hash) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { ripemd := ripemd160.New() ripemd.Write(input) return common.LeftPadBytes(ripemd.Sum(nil), 32), nil } +func (c *ripemd160hash) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // data copy implemented as a native contract. type dataCopy struct{} @@ -158,10 +222,14 @@ type dataCopy struct{} func (c *dataCopy) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas } -func (c *dataCopy) Run(in []byte) ([]byte, error) { +func (c *dataCopy) Run(in []byte, contract *Contract, evm *EVM) ([]byte, error) { return in, nil } +func (c *dataCopy) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // bigModExp implements a native big integer exponential modular operation. type bigModExp struct{} @@ -239,7 +307,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(input []byte) ([]byte, error) { +func (c *bigModExp) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { var ( baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64() expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64() @@ -267,6 +335,10 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) { return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil } +func (c *bigModExp) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point, // returning it, or an error if the point is invalid. func newCurvePoint(blob []byte) (*bn256.G1, error) { @@ -312,10 +384,14 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256AddIstanbul) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { return runBn256Add(input) } +func (c *bn256AddIstanbul) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // bn256AddByzantium implements a native elliptic curve point addition // conforming to Byzantium consensus rules. type bn256AddByzantium struct{} @@ -325,10 +401,14 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256AddByzantium) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { return runBn256Add(input) } +func (c *bn256AddByzantium) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by // both Byzantium and Istanbul operations. func runBn256ScalarMul(input []byte) ([]byte, error) { @@ -350,10 +430,14 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulIstanbul) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { return runBn256ScalarMul(input) } +func (c *bn256ScalarMulIstanbul) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // bn256ScalarMulByzantium implements a native elliptic curve scalar // multiplication conforming to Byzantium consensus rules. type bn256ScalarMulByzantium struct{} @@ -363,10 +447,14 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulByzantium) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { return runBn256ScalarMul(input) } +func (c *bn256ScalarMulByzantium) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + var ( // true32Byte is returned if the bn256 pairing check succeeds. true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} @@ -418,10 +506,14 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul } -func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256PairingIstanbul) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { return runBn256Pairing(input) } +func (c *bn256PairingIstanbul) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + // bn256PairingByzantium implements a pairing pre-compile for the bn256 curve // conforming to Byzantium consensus rules. type bn256PairingByzantium struct{} @@ -431,10 +523,14 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium } -func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256PairingByzantium) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { return runBn256Pairing(input) } +func (c *bn256PairingByzantium) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + type blake2F struct{} func (c *blake2F) RequiredGas(input []byte) uint64 { @@ -457,7 +553,7 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(input []byte) ([]byte, error) { +func (c *blake2F) Run(input []byte, contract *Contract, evm *EVM) ([]byte, error) { // Make sure the input is valid (correct lenth and final flag) if len(input) != blake2FInputLength { return nil, errBlake2FInvalidInputLength @@ -495,3 +591,584 @@ func (c *blake2F) Run(input []byte) ([]byte, error) { } return output, nil } + +func (c *blake2F) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + return nil +} + +var ( + // errNotOnCurve is returned if a point being unmarshalled as a bn256 elliptic + // curve point is not on the curve. + errNotOnCurve = errors.New("point not on elliptic curve") + + // errInvalidCurvePoint is returned if a point being unmarshalled as a bn256 + // elliptic curve point is invalid. + errInvalidCurvePoint = errors.New("invalid elliptic curve point") + + // invalid ring signed info + ErrInvalidRingSigned = errors.New("invalid ring signed info") +) + +var ( + coinSCDefinition = ` + [{"constant": false,"type": "function","stateMutability": "nonpayable","inputs": [{"name": "OtaAddr","type":"string"},{"name": "Value","type": "uint256"}],"name": "buyCoinNote","outputs": [{"name": "OtaAddr","type":"string"},{"name": "Value","type": "uint256"}]},{"constant": false,"type": "function","inputs": [{"name":"RingSignedData","type": "string"},{"name": "Value","type": "uint256"}],"name": "refundCoin","outputs": [{"name": "RingSignedData","type": "string"},{"name": "Value","type": "uint256"}]},{"constant": false,"type": "function","stateMutability": "nonpayable","inputs": [],"name": "getCoins","outputs": [{"name":"Value","type": "uint256"}]}]` + + stampSCDefinition = `[{"constant": false,"type": "function","stateMutability": "nonpayable","inputs": [{"name":"OtaAddr","type": "string"},{"name": "Value","type": "uint256"}],"name": "buyStamp","outputs": [{"name": "OtaAddr","type": "string"},{"name": "Value","type": "uint256"}]},{"constant": false,"type": "function","inputs": [{"name": "RingSignedData","type": "string"},{"name": "Value","type": "uint256"}],"name": "refundCoin","outputs": [{"name": "RingSignedData","type": "string"},{"name": "Value","type": "uint256"}]},{"constant": false,"type": "function","stateMutability": "nonpayable","inputs": [],"name": "getCoins","outputs": [{"name": "Value","type": "uint256"}]}]` + + coinAbi, errCoinSCInit = abi.JSON(strings.NewReader(coinSCDefinition)) + buyIdArr, refundIdArr, getCoinsIdArr [4]byte + + stampAbi, errStampSCInit = abi.JSON(strings.NewReader(stampSCDefinition)) + stBuyId [4]byte + + errBuyCoin = errors.New("error in buy coin") + errRefundCoin = errors.New("error in refund coin") + + errBuyStamp = errors.New("error in buy stamp") + + errParameters = errors.New("error parameters") + errMethodId = errors.New("error method id") + + errBalance = errors.New("balance is insufficient") + + errStampValue = errors.New("stamp value is not support") + + errCoinValue = errors.New("usecoin value is not support") + + ErrMismatchedValue = errors.New("mismatched usecoin value") + + ErrInvalidOTASet = errors.New("invalid OTA mix set") + + ErrOTAReused = errors.New("OTA is reused") + + StampValueSet = make(map[string]string, 5) + UseCoinValueSet = make(map[string]string, 10) +) + +const ( + Usecoin10 = "10000000000000000000" //10 + Usecoin20 = "20000000000000000000" //20 + Usecoin50 = "50000000000000000000" //50 + Usecoin100 = "100000000000000000000" //100 + + Usecoin200 = "200000000000000000000" //200 + Usecoin500 = "500000000000000000000" //500 + Usecoin1000 = "1000000000000000000000" //1000 + Usecoin5000 = "5000000000000000000000" //5000 + Usecoin50000 = "50000000000000000000000" //50000 + + UseStampdot001 = "1000000000000000" //0.001 + UseStampdot002 = "2000000000000000" //0.002 + UseStampdot005 = "5000000000000000" //0.005 + + UseStampdot003 = "3000000000000000" //0.003 + UseStampdot006 = "6000000000000000" //0.006 + UseStampdot009 = "9000000000000000" //0.009 + + UseStampdot03 = "30000000000000000" //0.03 + UseStampdot06 = "60000000000000000" //0.06 + UseStampdot09 = "90000000000000000" //0.09 + UseStampdot2 = "200000000000000000" //0.2 + UseStampdot3 = "300000000000000000" //0.3 + UseStampdot5 = "500000000000000000" //0.5 +) + +func init() { + if errCoinSCInit != nil || errStampSCInit != nil { + panic("err in coin sc initialize or stamp error initialize ") + } + + copy(buyIdArr[:], coinAbi.Methods["buyCoinNote"].ID()) + copy(refundIdArr[:], coinAbi.Methods["refundCoin"].ID()) + copy(getCoinsIdArr[:], coinAbi.Methods["getCoins"].ID()) + + copy(stBuyId[:], stampAbi.Methods["buyStamp"].ID()) + + svaldot001, _ := new(big.Int).SetString(UseStampdot001, 10) + StampValueSet[svaldot001.Text(16)] = UseStampdot001 + + svaldot002, _ := new(big.Int).SetString(UseStampdot002, 10) + StampValueSet[svaldot002.Text(16)] = UseStampdot002 + + svaldot005, _ := new(big.Int).SetString(UseStampdot005, 10) + StampValueSet[svaldot005.Text(16)] = UseStampdot005 + + svaldot003, _ := new(big.Int).SetString(UseStampdot003, 10) + StampValueSet[svaldot003.Text(16)] = UseStampdot003 + + svaldot006, _ := new(big.Int).SetString(UseStampdot006, 10) + StampValueSet[svaldot006.Text(16)] = UseStampdot006 + + svaldot009, _ := new(big.Int).SetString(UseStampdot009, 10) + StampValueSet[svaldot009.Text(16)] = UseStampdot009 + + svaldot03, _ := new(big.Int).SetString(UseStampdot03, 10) + StampValueSet[svaldot03.Text(16)] = UseStampdot03 + + svaldot06, _ := new(big.Int).SetString(UseStampdot06, 10) + StampValueSet[svaldot06.Text(16)] = UseStampdot06 + + svaldot09, _ := new(big.Int).SetString(UseStampdot09, 10) + StampValueSet[svaldot09.Text(16)] = UseStampdot09 + + svaldot2, _ := new(big.Int).SetString(UseStampdot2, 10) + StampValueSet[svaldot2.Text(16)] = UseStampdot2 + + svaldot3, _ := new(big.Int).SetString(UseStampdot3, 10) + StampValueSet[svaldot3.Text(16)] = UseStampdot3 + + svaldot5, _ := new(big.Int).SetString(UseStampdot5, 10) + StampValueSet[svaldot5.Text(16)] = UseStampdot5 + + cval10, _ := new(big.Int).SetString(Usecoin10, 10) + UseCoinValueSet[cval10.Text(16)] = Usecoin10 + + cval20, _ := new(big.Int).SetString(Usecoin20, 10) + UseCoinValueSet[cval20.Text(16)] = Usecoin20 + + cval50, _ := new(big.Int).SetString(Usecoin50, 10) + UseCoinValueSet[cval50.Text(16)] = Usecoin50 + + cval100, _ := new(big.Int).SetString(Usecoin100, 10) + UseCoinValueSet[cval100.Text(16)] = Usecoin100 + + cval200, _ := new(big.Int).SetString(Usecoin200, 10) + UseCoinValueSet[cval200.Text(16)] = Usecoin200 + + cval500, _ := new(big.Int).SetString(Usecoin500, 10) + UseCoinValueSet[cval500.Text(16)] = Usecoin500 + + cval1000, _ := new(big.Int).SetString(Usecoin1000, 10) + UseCoinValueSet[cval1000.Text(16)] = Usecoin1000 + + cval5000, _ := new(big.Int).SetString(Usecoin5000, 10) + UseCoinValueSet[cval5000.Text(16)] = Usecoin5000 + + cval50000, _ := new(big.Int).SetString(Usecoin50000, 10) + UseCoinValueSet[cval50000.Text(16)] = Usecoin50000 + +} + +type useCoinSC struct{} + +func (c *useCoinSC) RequiredGas(input []byte) uint64 { + if len(input) < 4 { + return 0 + } + + var methodIdArr [4]byte + copy(methodIdArr[:], input[:4]) + + if methodIdArr == refundIdArr { + + var RefundStruct struct { + RingSignedData string + Value *big.Int + } + + err := coinAbi.Unpack(&RefundStruct, "refundCoin", input[4:]) + if err != nil { + return params.RequiredGasPerMixPub + } + + err, publickeys, _, _, _ := DecodeRingSignOut(RefundStruct.RingSignedData) + if err != nil { + return params.RequiredGasPerMixPub + } + + mixLen := len(publickeys) + ringSigDiffRequiredGas := params.RequiredGasPerMixPub * (uint64(mixLen)) + + // ringsign compute gas + ota image key store setting gas + return ringSigDiffRequiredGas + params.SstoreSetGas + + } else { + // ota balance store gas + ota useaddr store gas + return params.SstoreSetGas * 2 + } + +} + +func (c *useCoinSC) Run(in []byte, contract *Contract, evm *EVM) ([]byte, error) { + if len(in) < 4 { + return nil, errParameters + } + + var methodIdArr [4]byte + copy(methodIdArr[:], in[:4]) + + if methodIdArr == buyIdArr { + return c.buyCoin(in[4:], contract, evm) + } else if methodIdArr == refundIdArr { + return c.refund(in[4:], contract, evm) + } + + return nil, errMethodId +} + +func (c *useCoinSC) ValidBuyCoinReq(stateDB StateDB, payload []byte, txValue *big.Int) (otaAddr []byte, err error) { + if stateDB == nil || len(payload) == 0 || txValue == nil { + return nil, errors.New("unknown error") + } + + var outStruct struct { + OtaAddr string + Value *big.Int + } + + err = coinAbi.Unpack(&outStruct, "buyCoinNote", payload) + if err != nil || outStruct.Value == nil { + return nil, errBuyCoin + } + + if outStruct.Value.Cmp(txValue) != 0 { + return nil, ErrMismatchedValue + } + + _, ok := UseCoinValueSet[outStruct.Value.Text(16)] + if !ok { + return nil, errCoinValue + } + + useAddr, err := hexutil.Decode(outStruct.OtaAddr) + if err != nil { + return nil, err + } + + ax, err := GetAXFromUseAddr(useAddr) + if err != nil { + return nil, err + } + + exist, _, err := CheckOTAAXExist(stateDB, ax) + if err != nil { + return nil, err + } + + if exist { + return nil, ErrOTAReused + } + + return useAddr, nil +} + +func (c *useCoinSC) ValidRefundReq(stateDB StateDB, payload []byte, from []byte) (image []byte, value *big.Int, err error) { + if stateDB == nil || len(payload) == 0 || len(from) == 0 { + return nil, nil, errors.New("unknown error") + } + + var RefundStruct struct { + RingSignedData string + Value *big.Int + } + + err = coinAbi.Unpack(&RefundStruct, "refundCoin", payload) + if err != nil || RefundStruct.Value == nil { + return nil, nil, errRefundCoin + } + + ringSignInfo, err := FetchRingSignInfo(stateDB, from, RefundStruct.RingSignedData) + if err != nil { + return nil, nil, err + } + + if ringSignInfo.OTABalance.Cmp(RefundStruct.Value) != 0 { + return nil, nil, ErrMismatchedValue + } + + kix := crypto.FromECDSAPub(ringSignInfo.KeyImage) + exist, _, err := CheckOTAImageExist(stateDB, kix) + if err != nil { + return nil, nil, err + } + + if exist { + return nil, nil, ErrOTAReused + } + + return kix, RefundStruct.Value, nil + +} + +func (c *useCoinSC) refund(all []byte, contract *Contract, evm *EVM) ([]byte, error) { + kix, value, err := c.ValidRefundReq(evm.StateDB, all, contract.CallerAddress.Bytes()) + if err != nil { + fmt.Println("failed refund") + fmt.Println(evm.BlockNumber) + return nil, err + } + + err = AddOTAImage(evm.StateDB, kix, value.Bytes()) + if err != nil { + return nil, err + } + + addrSrc := contract.CallerAddress + + evm.StateDB.AddBalance(addrSrc, value) + return []byte{1}, nil + +} + +func (c *useCoinSC) buyCoin(in []byte, contract *Contract, evm *EVM) ([]byte, error) { + otaAddr, err := c.ValidBuyCoinReq(evm.StateDB, in, contract.value) + if err != nil { + return nil, err + } + + add, err := AddOTAIfNotExist(evm.StateDB, contract.value, otaAddr) + if err != nil || !add { + return nil, errBuyCoin + } + + addrSrc := contract.CallerAddress + balance := evm.StateDB.GetBalance(addrSrc) + + if balance.Cmp(contract.value) >= 0 { + // Need check contract value in build in value sets + evm.StateDB.SubBalance(addrSrc, contract.value) + return []byte{1}, nil + } else { + return nil, errBalance + } +} + +func DecodeRingSignOut(s string) (error, []*ecdsa.PublicKey, *ecdsa.PublicKey, []*big.Int, []*big.Int) { + ss := strings.Split(s, "+") + if len(ss) < 4 { + return ErrInvalidRingSigned, nil, nil, nil, nil + } + + ps := ss[0] + k := ss[1] + ws := ss[2] + qs := ss[3] + + pa := strings.Split(ps, "&") + publickeys := make([]*ecdsa.PublicKey, 0) + for _, pi := range pa { + + publickey := crypto.ToECDSAPub(common.FromHex(pi)) + if publickey == nil || publickey.X == nil || publickey.Y == nil { + return ErrInvalidRingSigned, nil, nil, nil, nil + } + + publickeys = append(publickeys, publickey) + } + + keyimgae := crypto.ToECDSAPub(common.FromHex(k)) + if keyimgae == nil || keyimgae.X == nil || keyimgae.Y == nil { + return ErrInvalidRingSigned, nil, nil, nil, nil + } + + wa := strings.Split(ws, "&") + w := make([]*big.Int, 0) + for _, wi := range wa { + bi, err := hexutil.DecodeBig(wi) + if bi == nil || err != nil { + return ErrInvalidRingSigned, nil, nil, nil, nil + } + + w = append(w, bi) + } + + qa := strings.Split(qs, "&") + q := make([]*big.Int, 0) + for _, qi := range qa { + bi, err := hexutil.DecodeBig(qi) + if bi == nil || err != nil { + return ErrInvalidRingSigned, nil, nil, nil, nil + } + + q = append(q, bi) + } + + if len(publickeys) != len(w) || len(publickeys) != len(q) { + return ErrInvalidRingSigned, nil, nil, nil, nil + } + + return nil, publickeys, keyimgae, w, q +} + +func (c *useCoinSC) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + if stateDB == nil || signer == nil || tx == nil { + return errParameters + } + + payload := tx.Data() + if len(payload) < 4 { + return errParameters + } + + var methodIdArr [4]byte + copy(methodIdArr[:], payload[:4]) + + if methodIdArr == buyIdArr { + _, err := c.ValidBuyCoinReq(stateDB, payload[4:], tx.Value()) + return err + + } else if methodIdArr == refundIdArr { + from, err := types.Sender(signer, tx) + if err != nil { + return err + } + + _, _, err = c.ValidRefundReq(stateDB, payload[4:], from.Bytes()) + return err + } + + return errParameters +} + +type usechainStampSC struct{} + +func (c *usechainStampSC) RequiredGas(input []byte) uint64 { + // ota balance store gas + ota useaddr store gas + return params.SstoreSetGas * 2 +} + +func (c *usechainStampSC) Run(in []byte, contract *Contract, env *EVM) ([]byte, error) { + if len(in) < 4 { + return nil, errParameters + } + + var methodId [4]byte + copy(methodId[:], in[:4]) + + if methodId == stBuyId { + return c.buyStamp(in[4:], contract, env) + } + + return nil, errMethodId +} + +func (c *usechainStampSC) ValidBuyStampReq(stateDB StateDB, payload []byte, value *big.Int) (otaAddr []byte, err error) { + if stateDB == nil || len(payload) == 0 || value == nil { + return nil, errors.New("unknown error") + } + + var StampInput struct { + OtaAddr string + Value *big.Int + } + + err = stampAbi.UnpackTmp(&StampInput, "buyStamp", payload) + if err != nil || StampInput.Value == nil { + return nil, errBuyStamp + } + + if StampInput.Value.Cmp(value) != 0 { + return nil, ErrMismatchedValue + } + + _, ok := StampValueSet[StampInput.Value.Text(16)] + if !ok { + return nil, errStampValue + } + + useAddr, err := hexutil.Decode(StampInput.OtaAddr) + if err != nil { + return nil, err + } + + ax, err := GetAXFromUseAddr(useAddr) + exist, _, err := CheckOTAAXExist(stateDB, ax) + if err != nil { + return nil, err + } + + if exist { + return nil, ErrOTAReused + } + + return useAddr, nil +} + +func (c *usechainStampSC) ValidTx(stateDB StateDB, signer types.Signer, tx *types.Transaction) error { + if stateDB == nil || signer == nil || tx == nil { + return errParameters + } + + payload := tx.Data() + if len(payload) < 4 { + return errParameters + } + + var methodId [4]byte + copy(methodId[:], payload[:4]) + if methodId == stBuyId { + _, err := c.ValidBuyStampReq(stateDB, payload[4:], tx.Value()) + return err + } + + return errParameters +} + +func (c *usechainStampSC) buyStamp(in []byte, contract *Contract, evm *EVM) ([]byte, error) { + useAddr, err := c.ValidBuyStampReq(evm.StateDB, in, contract.value) + if err != nil { + return nil, err + } + + add, err := AddOTAIfNotExist(evm.StateDB, contract.value, useAddr) + if err != nil || !add { + return nil, errBuyStamp + } + + addrSrc := contract.CallerAddress + balance := evm.StateDB.GetBalance(addrSrc) + + if balance.Cmp(contract.value) >= 0 { + // Need check contract value in build in value sets + evm.StateDB.SubBalance(addrSrc, contract.value) + return []byte{1}, nil + } else { + return nil, errBalance + } +} + +type RingSignInfo struct { + PublicKeys []*ecdsa.PublicKey + KeyImage *ecdsa.PublicKey + W_Random []*big.Int + Q_Random []*big.Int + OTABalance *big.Int +} + +func FetchRingSignInfo(stateDB StateDB, hashInput []byte, ringSignedStr string) (info *RingSignInfo, err error) { + if stateDB == nil || hashInput == nil { + return nil, errParameters + } + + infoTmp := new(RingSignInfo) + + err, infoTmp.PublicKeys, infoTmp.KeyImage, infoTmp.W_Random, infoTmp.Q_Random = DecodeRingSignOut(ringSignedStr) + if err != nil { + return nil, err + } + + otaLongs := make([][]byte, 0, len(infoTmp.PublicKeys)) + for i := 0; i < len(infoTmp.PublicKeys); i++ { + otaLongs = append(otaLongs, keystore.ECDSAPKCompression(infoTmp.PublicKeys[i])) + } + + exist, balanceGet, _, err := BatCheckOTAExist(stateDB, otaLongs) + if err != nil { + + log.Error("verify mix ota fail", "err", err.Error()) + return nil, err + } + + if !exist { + return nil, ErrInvalidOTASet + } + + infoTmp.OTABalance = balanceGet + + valid := crypto.VerifyRingSign(hashInput, infoTmp.PublicKeys, infoTmp.KeyImage, infoTmp.W_Random, infoTmp.Q_Random) + if !valid { + return nil, ErrInvalidRingSigned + } + + return infoTmp, nil +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 751c1fdc1f..f878281eb7 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -17,6 +17,7 @@ package vm import ( + "bytes" "math/big" "sync/atomic" "time" @@ -51,7 +52,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err precompiles = PrecompiledContractsIstanbul } if p := precompiles[*contract.CodeAddr]; p != nil { - return RunPrecompiledContract(p, input, contract) + return RunPrecompiledContract(p, input, contract, evm) } } for _, interpreter := range evm.interpreters { @@ -222,7 +223,11 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } - evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + + if !bytes.Equal(to.Address().Bytes(), useCoinPrecompileAddr.Bytes()) && !bytes.Equal(to.Address().Bytes(), useStampPrecompileAddr.Bytes()) { + evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + } + // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, to, value, gas) diff --git a/crypto/crypto.go b/crypto/crypto.go index b97b8d9a02..4b9614cdab 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -31,8 +31,18 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + + //usha3 "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/crypto/sha3" + + "github.com/ethereum/go-ethereum/log" +) + +var ( + secp256k1_N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) + secp256k1_halfN = new(big.Int).Div(secp256k1_N, big.NewInt(2)) ) //SignatureLength indicates the byte length required to carry a signature with recovery id. @@ -235,6 +245,103 @@ func zeroBytes(bytes []byte) { } } +// calc [x]Hash(P) +func xScalarHashP(x []byte, pub *ecdsa.PublicKey) (I *ecdsa.PublicKey) { + KeyImg := new(ecdsa.PublicKey) + I = new(ecdsa.PublicKey) + KeyImg.X, KeyImg.Y = S256().ScalarMult(pub.X, pub.Y, Keccak256(FromECDSAPub(pub))) //Hash(P) + I.X, I.Y = S256().ScalarMult(KeyImg.X, KeyImg.Y, x) + I.Curve = S256() + return +} + +// VerifyRingSign verifies the validity of ring signature +// Pengbo added, Shi,TeemoGuo revised +func VerifyRingSign(M []byte, PublicKeys []*ecdsa.PublicKey, I *ecdsa.PublicKey, c []*big.Int, r []*big.Int) bool { + if M == nil || PublicKeys == nil || I == nil || c == nil || r == nil { + return false + } + + if len(PublicKeys) == 0 || len(PublicKeys) != len(c) || len(PublicKeys) != len(r) { + return false + } + + n := len(PublicKeys) + for i := 0; i < n; i++ { + if PublicKeys[i] == nil || PublicKeys[i].X == nil || PublicKeys[i].Y == nil || + c[i] == nil || r[i] == nil { + return false + } + } + + log.Debug("M info", "R", 0, "M", common.ToHex(M)) + for i := 0; i < n; i++ { + log.Debug("publicKeys", "i", i, "publickey", common.ToHex(FromECDSAPub(PublicKeys[i]))) + } + + log.Debug("image info", "I", common.ToHex(FromECDSAPub(I))) + for i := 0; i < n; i++ { + log.Debug("c info", "i", i, "c", common.ToHex(c[i].Bytes())) + } + + for i := 0; i < n; i++ { + log.Debug("r info", "i", i, "r", common.ToHex(r[i].Bytes())) + } + + SumC := new(big.Int).SetInt64(0) + Lpub := new(ecdsa.PublicKey) + d := sha3.NewLegacyKeccak256() + d.Write(M) + + //hash(M,Li,Ri) + for i := 0; i < n; i++ { + Lpub.X, Lpub.Y = S256().ScalarBaseMult(r[i].Bytes()) //[ri]G + if Lpub.X == nil || Lpub.Y == nil { + return false + } + + Ppub := new(ecdsa.PublicKey) + Ppub.X, Ppub.Y = S256().ScalarMult(PublicKeys[i].X, PublicKeys[i].Y, c[i].Bytes()) //[ci]Pi + if Ppub.X == nil || Ppub.Y == nil { + return false + } + + Lpub.X, Lpub.Y = S256().Add(Lpub.X, Lpub.Y, Ppub.X, Ppub.Y) //[ri]G+[ci]Pi + SumC.Add(SumC, c[i]) + SumC.Mod(SumC, secp256k1_N) + d.Write(FromECDSAPub(Lpub)) + log.Debug("LPublicKeys", "i", i, "Lpub", common.ToHex(FromECDSAPub(Lpub))) + } + + Rpub := new(ecdsa.PublicKey) + for i := 0; i < n; i++ { + Rpub = xScalarHashP(r[i].Bytes(), PublicKeys[i]) //[qi]HashPi + if Rpub == nil || Rpub.X == nil || Rpub.Y == nil { + return false + } + + Ppub := new(ecdsa.PublicKey) + Ppub.X, Ppub.Y = S256().ScalarMult(I.X, I.Y, c[i].Bytes()) //[wi]I + if Ppub.X == nil || Ppub.Y == nil { + return false + } + + Rpub.X, Rpub.Y = S256().Add(Rpub.X, Rpub.Y, Ppub.X, Ppub.Y) //[qi]HashPi+[wi]I + log.Debug("RPublicKeys", "i", i, "Rpub", common.ToHex(FromECDSAPub(Rpub))) + + d.Write(FromECDSAPub(Rpub)) + } + + hash := new(big.Int).SetBytes(d.Sum(nil)) //hash(m,Li,Ri) + log.Debug("hash info", "i", 0, "hash", common.ToHex(hash.Bytes())) + + hash.Mod(hash, secp256k1_N) + log.Debug("hash info", "i", 2, "hash", common.ToHex(hash.Bytes())) + log.Debug("SumC info", "i", 3, "SumC", common.ToHex(SumC.Bytes())) + + return hash.Cmp(SumC) == 0 +} + // A1=[hash([r]B)]G+A func generateA1(r []byte, A *ecdsa.PublicKey, B *ecdsa.PublicKey) ecdsa.PublicKey { A1 := new(ecdsa.PublicKey) diff --git a/params/protocol_params.go b/params/protocol_params.go index 11b858a61c..61cf3422ac 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -129,6 +129,9 @@ const ( Bn256PairingBaseGasIstanbul uint64 = 45000 // Base 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 + + RequiredGasPerMixPub uint64 = 4000 // ring signature mix difficulty gas + GetOTAMixSetMaxSize uint64 = 20 // Max number of mix ota set size from once getting ) var (