From 06dc0ead656e6f2054a667ecfb1cc617096cb507 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 20 Feb 2024 23:20:12 +1100 Subject: [PATCH 01/59] update start script to right syntax (#439) --- cicd/devnet/start.sh | 6 +++--- cicd/mainnet/start.sh | 6 +++--- cicd/testnet/start.sh | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh index 8899d16fa2bf..4c3a81ee5b53 100755 --- a/cicd/devnet/start.sh +++ b/cicd/devnet/start.sh @@ -7,10 +7,10 @@ then exit 1 fi echo $PRIVATE_KEY >> /tmp/key - wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -v FS="({|})" '{print $2}') + wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -F '[{}]' '{print $2}') XDC --datadir /work/xdcchain init /work/genesis.json else - wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -v FS="({|})" '{print $2}') + wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -F '[{}]' '{print $2}') fi input="/work/bootnodes.list" @@ -80,6 +80,6 @@ XDC --ethstats ${netstats} --gcmode archive \ --rpcapi admin,db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ --rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ ---periodicprofile --debugdatadir /work/xdcchain \ +--debugdatadir /work/xdcchain \ --ws --wsaddr=0.0.0.0 --wsport $ws_port \ --wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cicd/mainnet/start.sh b/cicd/mainnet/start.sh index c9512a804ce2..86054c02c211 100755 --- a/cicd/mainnet/start.sh +++ b/cicd/mainnet/start.sh @@ -7,10 +7,10 @@ then exit 1 fi echo $PRIVATE_KEY >> /tmp/key - wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -v FS="({|})" '{print $2}') + wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -F '[{}]' '{print $2}') XDC --datadir /work/xdcchain init /work/genesis.json else - wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -v FS="({|})" '{print $2}') + wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -F '[{}]' '{print $2}') fi input="/work/bootnodes.list" @@ -79,6 +79,6 @@ XDC --ethstats ${netstats} --gcmode archive \ --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \ --rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ ---periodicprofile --debugdatadir /work/xdcchain \ +--debugdatadir /work/xdcchain \ --ws --wsaddr=0.0.0.0 --wsport $ws_port \ --wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cicd/testnet/start.sh b/cicd/testnet/start.sh index 6050ac990c34..665c0637fc9e 100755 --- a/cicd/testnet/start.sh +++ b/cicd/testnet/start.sh @@ -8,10 +8,10 @@ then exit 1 fi echo $PRIVATE_KEY >> /tmp/key - wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -v FS="({|})" '{print $2}') + wallet=$(XDC account import --password .pwd --datadir /work/xdcchain /tmp/key | awk -F '[{}]' '{print $2}') XDC --datadir /work/xdcchain init /work/genesis.json else - wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -v FS="({|})" '{print $2}') + wallet=$(XDC account list --datadir /work/xdcchain | head -n 1 | awk -F '[{}]' '{print $2}') fi input="/work/bootnodes.list" From f11bf739a2f7dbbfbfaf2424b6e07219d693cf92 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 23 Feb 2024 14:11:42 +0800 Subject: [PATCH 02/59] fix panic caused by skipKeyFile (#440) --- accounts/keystore/file_cache.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/accounts/keystore/file_cache.go b/accounts/keystore/file_cache.go index ce2d5fc91a6c..0ef6e28dc7d1 100644 --- a/accounts/keystore/file_cache.go +++ b/accounts/keystore/file_cache.go @@ -61,7 +61,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er if err != nil { log.Warn("scan get FileInfo", "err", err, "path", path) } - if skipKeyFile(fiInfo) { + if fiInfo == nil || skipKeyFile(fiInfo) { log.Trace("Ignoring file on account scan", "path", path) continue } @@ -94,7 +94,8 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er // skipKeyFile ignores editor backups, hidden files and folders/symlinks. func skipKeyFile(fi os.FileInfo) bool { // Skip editor backups and UNIX-style hidden files. - if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") { + name := fi.Name() + if strings.HasSuffix(name, "~") || strings.HasPrefix(name, ".") { return true } // Skip misc special files, directories (yes, symlinks too). From 4731eb2c6d8c283c120ad2d72ed70c11ce40db24 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 29 Feb 2024 17:11:46 +0800 Subject: [PATCH 03/59] params: lint ChainConfig --- params/config.go | 91 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/params/config.go b/params/config.go index 86864d9f8b88..a850bd54d063 100644 --- a/params/config.go +++ b/params/config.go @@ -241,31 +241,74 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil} + AllEthashProtocolChanges = &ChainConfig{ + ChainId: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: nil, + Ethash: new(EthashConfig), + Clique: nil, + XDPoS: nil, + } // AllXDPoSProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the XDPoS consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllXDPoSProtocolChanges = &ChainConfig{big.NewInt(89), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &XDPoSConfig{Period: 0, Epoch: 900}} - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 900}, nil} + AllXDPoSProtocolChanges = &ChainConfig{ + ChainId: big.NewInt(89), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: nil, + Ethash: nil, + Clique: nil, + XDPoS: &XDPoSConfig{Period: 0, Epoch: 900}, + } + + AllCliqueProtocolChanges = &ChainConfig{ + ChainId: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: nil, + Ethash: nil, + Clique: &CliqueConfig{Period: 0, Epoch: 900}, + XDPoS: nil, + } // XDPoS config with v2 engine after block 901 TestXDPoSMockChainConfig = &ChainConfig{ - big.NewInt(1337), - big.NewInt(0), - nil, - false, - big.NewInt(0), - common.Hash{}, - big.NewInt(0), - big.NewInt(0), - big.NewInt(0), - nil, - new(EthashConfig), - nil, - &XDPoSConfig{ + ChainId: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: nil, + Ethash: new(EthashConfig), + Clique: nil, + XDPoS: &XDPoSConfig{ Epoch: 900, Gap: 450, SkipV1Validation: true, @@ -279,7 +322,21 @@ var ( }, } - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil} + TestChainConfig = &ChainConfig{ + ChainId: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: nil, + Ethash: new(EthashConfig), + Clique: nil, + XDPoS: nil, + } TestRules = TestChainConfig.Rules(new(big.Int)) ) From 192edc0c63ccc62203abe9c40a38c2fe04ff6b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 8 Jun 2020 14:24:40 +0200 Subject: [PATCH 04/59] core/vm: use uint256 in EVM implementation (#20787) * core/vm: use fixed uint256 library instead of big * core/vm: remove intpools * core/vm: upgrade uint256, fixes uint256.NewFromBig * core/vm: use uint256.Int by value in Stack * core/vm: upgrade uint256 to v1.0.0 * core/vm: don't preallocate space for 1024 stack items (only 16) Co-authored-by: Martin Holst Swende --- cmd/evm/json_logger.go | 7 +- core/vm/common.go | 27 +- core/vm/contract.go | 7 +- core/vm/eips.go | 5 +- core/vm/evm.go | 2 +- core/vm/gas.go | 4 +- core/vm/gas_table.go | 26 +- core/vm/instructions.go | 579 ++++++++++++----------------- core/vm/instructions_test.go | 149 ++++---- core/vm/int_pool_verifier.go | 31 -- core/vm/int_pool_verifier_empty.go | 23 -- core/vm/interpreter.go | 17 - core/vm/intpool.go | 117 ------ core/vm/intpool_test.go | 55 --- core/vm/logger.go | 6 +- core/vm/logger_json.go | 7 +- core/vm/logger_test.go | 8 +- core/vm/memory.go | 7 +- core/vm/stack.go | 34 +- eth/tracers/tracer.go | 2 +- go.mod | 2 +- go.sum | 2 + 22 files changed, 386 insertions(+), 731 deletions(-) delete mode 100644 core/vm/int_pool_verifier.go delete mode 100644 core/vm/int_pool_verifier_empty.go delete mode 100644 core/vm/intpool.go delete mode 100644 core/vm/intpool_test.go diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go index 458d56494b30..90e44f9c4ae5 100644 --- a/cmd/evm/json_logger.go +++ b/cmd/evm/json_logger.go @@ -56,7 +56,12 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos log.Memory = memory.Data() } if !l.cfg.DisableStack { - log.Stack = stack.Data() + //TODO(@holiman) improve this + logstack := make([]*big.Int, len(stack.Data())) + for i, item := range stack.Data() { + logstack[i] = item.ToBig() + } + log.Stack = logstack } return l.encoder.Encode(log) } diff --git a/core/vm/common.go b/core/vm/common.go index 194e3897f8c3..bb11f4a393e5 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -17,15 +17,14 @@ package vm import ( - "math/big" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/holiman/uint256" ) // calcMemSize64 calculates the required memory size, and returns // the size and whether the result overflowed uint64 -func calcMemSize64(off, l *big.Int) (uint64, bool) { +func calcMemSize64(off, l *uint256.Int) (uint64, bool) { if !l.IsUint64() { return 0, true } @@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) { // calcMemSize64WithUint calculates the required memory size, and returns // the size and whether the result overflowed uint64 // Identical to calcMemSize64, but length is a uint64 -func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) { +func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) { // if length is zero, memsize is always zero, regardless of offset if length64 == 0 { return 0, false } // Check that offset doesn't overflow - if !off.IsUint64() { + offset64, overflow := off.Uint64WithOverflow() + if overflow { return 0, true } - offset64 := off.Uint64() val := offset64 + length64 // if value < either of it's parts, then it overflowed return val, val < offset64 @@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte { return common.RightPadBytes(data[start:end], int(size)) } -// getDataBig returns a slice from the data based on the start and size and pads -// up to size with zero's. This function is overflow safe. -func getDataBig(data []byte, start *big.Int, size *big.Int) []byte { - dlen := big.NewInt(int64(len(data))) - - s := math.BigMin(start, dlen) - e := math.BigMin(new(big.Int).Add(s, size), dlen) - return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) -} - -// bigUint64 returns the integer casted to a uint64 and returns whether it -// overflowed in the process. -func bigUint64(v *big.Int) (uint64, bool) { - return v.Uint64(), !v.IsUint64() -} - // toWordSize returns the ceiled word size required for memory expansion. func toWordSize(size uint64) uint64 { if size > math.MaxUint64-31 { diff --git a/core/vm/contract.go b/core/vm/contract.go index cc9d59cf2deb..5378267dad03 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/holiman/uint256" ) // ContractRef is a reference to the contract's backing object @@ -81,11 +82,11 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin return c } -func (c *Contract) validJumpdest(dest *big.Int) bool { - udest := dest.Uint64() +func (c *Contract) validJumpdest(dest *uint256.Int) bool { + udest, overflow := dest.Uint64WithOverflow() // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. // Don't bother checking for JUMPDEST in that case. - if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) { + if overflow || udest >= uint64(len(c.Code)) { return false } // Only JUMPDESTs allowed for destinations diff --git a/core/vm/eips.go b/core/vm/eips.go index 40da1f88fafa..65829f1d568a 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) // EnableEIP enables the given EIP on the config. @@ -61,7 +62,7 @@ func enable1884(jt *JumpTable) { } func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(callContext.contract.Address())) + balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address())) callContext.stack.push(balance) return nil, nil } @@ -81,7 +82,7 @@ func enable1344(jt *JumpTable) { // opChainID implements CHAINID opcode func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - chainId := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainId) + chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainId) callContext.stack.push(chainId) return nil, nil } diff --git a/core/vm/evm.go b/core/vm/evm.go index f6f6a29a87d0..e8e5c1ca9419 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -353,7 +353,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // but is the correct thing to do and matters on other networks, in tests, and potential // future scenarios - evm.StateDB.AddBalance(addr, bigZero) + evm.StateDB.AddBalance(addr, big.NewInt(0)) } // When an error was returned by the EVM or when setting the creation code diff --git a/core/vm/gas.go b/core/vm/gas.go index bda326cdc7ab..5cf1d852d24a 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -17,7 +17,7 @@ package vm import ( - "math/big" + "github.com/holiman/uint256" ) // Gas costs @@ -34,7 +34,7 @@ const ( // // The cost of gas was changed during the homestead price change HF. // As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64. -func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) { +func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) { if isEip150 { availableGas = availableGas - base gas := availableGas - availableGas/64 diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 987dca32384d..719613f91227 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -71,7 +71,7 @@ func memoryCopierGas(stackpos int) gasFunc { return 0, err } // And gas for copying data, charged per word at param.CopyGas - words, overflow := bigUint64(stack.Back(stackpos)) + words, overflow := stack.Back(stackpos).Uint64WithOverflow() if overflow { return 0, ErrGasUintOverflow } @@ -97,7 +97,7 @@ var ( func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( y, x = stack.Back(1), stack.Back(0) - current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) ) // The legacy gas metering only takes into consideration the current state // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283) @@ -132,11 +132,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi // 2.2.2. If original value equals new value (this storage slot is reset) // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. // 2.2.2.2. Otherwise, add 4800 gas to refund counter. - value := common.BigToHash(y) + value := common.Hash(y.Bytes32()) if current == value { // noop (1) return params.NetSstoreNoopGas, nil } - original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return params.NetSstoreInitGas, nil @@ -184,14 +184,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m // Gas sentry honoured, do the actual gas calculation based on the stored value var ( y, x = stack.Back(1), stack.Back(0) - current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) ) - value := common.BigToHash(y) + value := common.Hash(y.Bytes32()) if current == value { // noop (1) return params.SstoreNoopGasEIP2200, nil } - original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return params.SstoreInitGasEIP2200, nil @@ -220,7 +220,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m func makeGasLog(n uint64) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - requestedSize, overflow := bigUint64(stack.Back(1)) + requestedSize, overflow := stack.Back(1).Uint64WithOverflow() if overflow { return 0, ErrGasUintOverflow } @@ -253,7 +253,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if err != nil { return 0, err } - wordGas, overflow := bigUint64(stack.Back(1)) + wordGas, overflow := stack.Back(1).Uint64WithOverflow() if overflow { return 0, ErrGasUintOverflow } @@ -287,7 +287,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS if err != nil { return 0, err } - wordGas, overflow := bigUint64(stack.Back(2)) + wordGas, overflow := stack.Back(2).Uint64WithOverflow() if overflow { return 0, ErrGasUintOverflow } @@ -329,8 +329,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( gas uint64 - transfersValue = stack.Back(2).Sign() != 0 - address = common.BigToAddress(stack.Back(1)) + transfersValue = !stack.Back(2).IsZero() + address = common.Address(stack.Back(1).Bytes20()) ) if evm.chainRules.IsEIP158 { if transfersValue && evm.StateDB.Empty(address) { @@ -423,7 +423,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me // EIP150 homestead gas reprice fork: if evm.chainRules.IsEIP150 { gas = params.SelfdestructGasEIP150 - var address = common.BigToAddress(stack.Back(0)) + var address = common.Address(stack.Back(0).Bytes20()) if evm.chainRules.IsEIP158 { // if empty and transfers value diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 4f1d12291132..92b3b9ae88c0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -17,303 +17,170 @@ package vm import ( - "math/big" - - "github.com/XinFinOrg/XDPoSChain/params" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) -var ( - bigZero = new(big.Int) - tt255 = math.BigPow(2, 255) -) - func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - math.U256(y.Add(x, y)) - - interpreter.intPool.putOne(x) + y.Add(&x, y) return nil, nil } func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - math.U256(y.Sub(x, y)) - - interpreter.intPool.putOne(x) + y.Sub(&x, y) return nil, nil } func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.pop() - callContext.stack.push(math.U256(x.Mul(x, y))) - - interpreter.intPool.putOne(y) - + x, y := callContext.stack.pop(), callContext.stack.peek() + y.Mul(&x, y) return nil, nil } func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - if y.Sign() != 0 { - math.U256(y.Div(x, y)) - } else { - y.SetUint64(0) - } - interpreter.intPool.putOne(x) + y.Div(&x, y) return nil, nil } func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop()) - res := interpreter.intPool.getZero() - - if y.Sign() == 0 || x.Sign() == 0 { - callContext.stack.push(res) - } else { - if x.Sign() != y.Sign() { - res.Div(x.Abs(x), y.Abs(y)) - res.Neg(res) - } else { - res.Div(x.Abs(x), y.Abs(y)) - } - callContext.stack.push(math.U256(res)) - } - interpreter.intPool.put(x, y) + x, y := callContext.stack.pop(), callContext.stack.peek() + y.SDiv(&x, y) return nil, nil } func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.pop() - if y.Sign() == 0 { - callContext.stack.push(x.SetUint64(0)) - } else { - callContext.stack.push(math.U256(x.Mod(x, y))) - } - interpreter.intPool.putOne(y) + x, y := callContext.stack.pop(), callContext.stack.peek() + y.Mod(&x, y) return nil, nil } func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop()) - res := interpreter.intPool.getZero() - - if y.Sign() == 0 { - callContext.stack.push(res) - } else { - if x.Sign() < 0 { - res.Mod(x.Abs(x), y.Abs(y)) - res.Neg(res) - } else { - res.Mod(x.Abs(x), y.Abs(y)) - } - callContext.stack.push(math.U256(res)) - } - interpreter.intPool.put(x, y) + x, y := callContext.stack.pop(), callContext.stack.peek() + y.SMod(&x, y) return nil, nil } func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - base, exponent := callContext.stack.pop(), callContext.stack.pop() - // some shortcuts - cmpToOne := exponent.Cmp(big1) - if cmpToOne < 0 { // Exponent is zero - // x ^ 0 == 1 - callContext.stack.push(base.SetUint64(1)) - } else if base.Sign() == 0 { - // 0 ^ y, if y != 0, == 0 - callContext.stack.push(base.SetUint64(0)) - } else if cmpToOne == 0 { // Exponent is one - // x ^ 1 == x - callContext.stack.push(base) - } else { - callContext.stack.push(math.Exp(base, exponent)) - interpreter.intPool.putOne(base) - } - interpreter.intPool.putOne(exponent) + base, exponent := callContext.stack.pop(), callContext.stack.peek() + exponent.Exp(&base, exponent) return nil, nil } func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - back := callContext.stack.pop() - if back.Cmp(big.NewInt(31)) < 0 { - bit := uint(back.Uint64()*8 + 7) - num := callContext.stack.pop() - mask := back.Lsh(common.Big1, bit) - mask.Sub(mask, common.Big1) - if num.Bit(int(bit)) > 0 { - num.Or(num, mask.Not(mask)) - } else { - num.And(num, mask) - } - - callContext.stack.push(math.U256(num)) - } - - interpreter.intPool.putOne(back) + back, num := callContext.stack.pop(), callContext.stack.peek() + num.ExtendSign(num, &back) return nil, nil } func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x := callContext.stack.peek() - math.U256(x.Not(x)) + x.Not(x) return nil, nil } func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - if x.Cmp(y) < 0 { - y.SetUint64(1) + if x.Lt(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - if x.Cmp(y) > 0 { - y.SetUint64(1) + if x.Gt(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - - xSign := x.Cmp(tt255) - ySign := y.Cmp(tt255) - - switch { - case xSign >= 0 && ySign < 0: - y.SetUint64(1) - - case xSign < 0 && ySign >= 0: - y.SetUint64(0) - - default: - if x.Cmp(y) < 0 { - y.SetUint64(1) - } else { - y.SetUint64(0) - } + if x.Slt(y) { + y.SetOne() + } else { + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - - xSign := x.Cmp(tt255) - ySign := y.Cmp(tt255) - - switch { - case xSign >= 0 && ySign < 0: - y.SetUint64(0) - - case xSign < 0 && ySign >= 0: - y.SetUint64(1) - - default: - if x.Cmp(y) > 0 { - y.SetUint64(1) - } else { - y.SetUint64(0) - } + if x.Sgt(y) { + y.SetOne() + } else { + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - if x.Cmp(y) == 0 { - y.SetUint64(1) + if x.Eq(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x := callContext.stack.peek() - if x.Sign() > 0 { - x.SetUint64(0) + if x.IsZero() { + x.SetOne() } else { - x.SetUint64(1) + x.Clear() } return nil, nil } func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.pop() - callContext.stack.push(x.And(x, y)) - - interpreter.intPool.putOne(y) + x, y := callContext.stack.pop(), callContext.stack.peek() + y.And(&x, y) return nil, nil } func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - y.Or(x, y) - - interpreter.intPool.putOne(x) + y.Or(&x, y) return nil, nil } func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - y.Xor(x, y) - - interpreter.intPool.putOne(x) + y.Xor(&x, y) return nil, nil } func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { th, val := callContext.stack.pop(), callContext.stack.peek() - if th.Cmp(common.Big32) < 0 { - b := math.Byte(val, 32, int(th.Int64())) - val.SetUint64(uint64(b)) - } else { - val.SetUint64(0) - } - interpreter.intPool.putOne(th) + val.Byte(&th) return nil, nil } func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - if z.Cmp(bigZero) > 0 { - x.Add(x, y) - x.Mod(x, z) - callContext.stack.push(math.U256(x)) + x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek() + if z.IsZero() { + z.Clear() } else { - callContext.stack.push(x.SetUint64(0)) + z.AddMod(&x, &y, z) } - interpreter.intPool.put(y, z) return nil, nil } func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - if z.Cmp(bigZero) > 0 { - x.Mul(x, y) - x.Mod(x, z) - callContext.stack.push(math.U256(x)) - } else { - callContext.stack.push(x.SetUint64(0)) - } - interpreter.intPool.put(y, z) + x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek() + z.MulMod(&x, &y, z) return nil, nil } @@ -322,16 +189,12 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] // and pushes on the stack arg2 shifted to the left by arg1 number of bits. func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := math.U256(callContext.stack.pop()), math.U256(callContext.stack.peek()) - defer interpreter.intPool.putOne(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { - value.SetUint64(0) - return nil, nil + shift, value := callContext.stack.pop(), callContext.stack.peek() + if shift.LtUint64(256) { + value.Lsh(value, uint(shift.Uint64())) + } else { + value.Clear() } - n := uint(shift.Uint64()) - math.U256(value.Lsh(value, n)) - return nil, nil } @@ -340,16 +203,12 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := math.U256(callContext.stack.pop()), math.U256(callContext.stack.peek()) - defer interpreter.intPool.putOne(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { - value.SetUint64(0) - return nil, nil + shift, value := callContext.stack.pop(), callContext.stack.peek() + if shift.LtUint64(256) { + value.Rsh(value, uint(shift.Uint64())) + } else { + value.Clear() } - n := uint(shift.Uint64()) - math.U256(value.Rsh(value, n)) - return nil, nil } @@ -357,29 +216,24 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one - shift, value := math.U256(callContext.stack.pop()), math.S256(callContext.stack.pop()) - defer interpreter.intPool.putOne(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { + shift, value := callContext.stack.pop(), callContext.stack.peek() + if shift.GtUint64(256) { if value.Sign() >= 0 { - value.SetUint64(0) + value.Clear() } else { - value.SetInt64(-1) + // Max negative shift: all bits set + value.SetAllOne() } - callContext.stack.push(math.U256(value)) return nil, nil } n := uint(shift.Uint64()) - value.Rsh(value, n) - callContext.stack.push(math.U256(value)) - + value.SRsh(value, n) return nil, nil } func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - offset, size := callContext.stack.pop(), callContext.stack.pop() - data := callContext.memory.GetPtr(offset.Int64(), size.Int64()) + offset, size := callContext.stack.pop(), callContext.stack.peek() + data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) if interpreter.hasher == nil { interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) @@ -393,45 +247,50 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by if evm.vmConfig.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } - callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:])) - interpreter.intPool.put(offset, size) + size.SetBytes(interpreter.hasherBuf[:]) return nil, nil } - func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Address().Bytes())) + callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes())) return nil, nil } func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.peek() - slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot))) + address := common.Address(slot.Bytes20()) + slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) return nil, nil } func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes())) + callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) return nil, nil } - func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Caller().Bytes())) + callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes())) return nil, nil } func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().Set(callContext.contract.value)) + v, _ := uint256.FromBig(callContext.contract.value) + callContext.stack.push(v) return nil, nil } func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(getDataBig(callContext.contract.Input, callContext.stack.pop(), big32))) + x := callContext.stack.peek() + if offset, overflow := x.Uint64WithOverflow(); !overflow { + data := getData(callContext.contract.Input, offset, 32) + x.SetBytes(data) + } else { + x.Clear() + } return nil, nil } func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Input)))) + callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input)))) return nil, nil } @@ -441,14 +300,20 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt dataOffset = callContext.stack.pop() length = callContext.stack.pop() ) - callContext.memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(callContext.contract.Input, dataOffset, length)) + dataOffset64, overflow := dataOffset.Uint64WithOverflow() + if overflow { + dataOffset64 = 0xffffffffffffffff + } + // These values are checked for overflow during gas cost calculation + memOffset64 := memOffset.Uint64() + length64 := length.Uint64() + callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64)) - interpreter.intPool.put(memOffset, dataOffset, length) return nil, nil } func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData)))) + callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) return nil, nil } @@ -457,30 +322,33 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call memOffset = callContext.stack.pop() dataOffset = callContext.stack.pop() length = callContext.stack.pop() - - end = interpreter.intPool.get().Add(dataOffset, length) ) - defer interpreter.intPool.put(memOffset, dataOffset, length, end) - if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() { + offset64, overflow := dataOffset.Uint64WithOverflow() + if overflow { return nil, ErrReturnDataOutOfBounds } - callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()]) - + // we can reuse dataOffset now (aliasing it for clarity) + var end = dataOffset + end.Add(&dataOffset, &length) + end64, overflow := end.Uint64WithOverflow() + if overflow || uint64(len(interpreter.returnData)) < end64 { + return nil, ErrReturnDataOutOfBounds + } + callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64]) return nil, nil } func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.peek() - slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot)))) - + slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20())))) return nil, nil } func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - l := interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Code))) + l := new(uint256.Int) + l.SetUint64(uint64(len(callContext.contract.Code))) callContext.stack.push(l) - return nil, nil } @@ -490,24 +358,32 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( codeOffset = callContext.stack.pop() length = callContext.stack.pop() ) - codeCopy := getDataBig(callContext.contract.Code, codeOffset, length) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = 0xffffffffffffffff + } + codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64()) callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - interpreter.intPool.put(memOffset, codeOffset, length) return nil, nil } func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { var ( - addr = common.BigToAddress(callContext.stack.pop()) - memOffset = callContext.stack.pop() - codeOffset = callContext.stack.pop() - length = callContext.stack.pop() + stack = callContext.stack + a = stack.pop() + memOffset = stack.pop() + codeOffset = stack.pop() + length = stack.pop() ) - codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = 0xffffffffffffffff + } + addr := common.Address(a.Bytes20()) + codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - interpreter.intPool.put(memOffset, codeOffset, length) return nil, nil } @@ -539,9 +415,9 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx // this account should be regarded as a non-existent account and zero should be returned. func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.peek() - address := common.BigToAddress(slot) + address := common.Address(slot.Bytes20()) if interpreter.evm.StateDB.Empty(address) { - slot.SetUint64(0) + slot.Clear() } else { slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) } @@ -549,56 +425,69 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx } func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice)) + v, _ := uint256.FromBig(interpreter.evm.GasPrice) + callContext.stack.push(v) return nil, nil } func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - num := callContext.stack.pop() - - n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257) - if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 { - callContext.stack.push(interpreter.evm.GetHash(num.Uint64()).Big()) + num := callContext.stack.peek() + num64, overflow := num.Uint64WithOverflow() + if overflow { + num.Clear() + return nil, nil + } + var upper, lower uint64 + upper = interpreter.evm.BlockNumber.Uint64() + if upper < 257 { + lower = 0 + } else { + lower = upper - 256 + } + if num64 >= lower && num64 < upper { + num.SetBytes(interpreter.evm.GetHash(num64).Bytes()) } else { - callContext.stack.push(interpreter.intPool.getZero()) + num.Clear() } - interpreter.intPool.put(num, n) return nil, nil } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes())) + callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes())) return nil, nil } func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time))) + v, _ := uint256.FromBig(interpreter.evm.Time) + callContext.stack.push(v) return nil, nil } func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber))) + v, _ := uint256.FromBig(interpreter.evm.BlockNumber) + callContext.stack.push(v) return nil, nil } func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Difficulty))) + v, _ := uint256.FromBig(interpreter.evm.Difficulty) + callContext.stack.push(v) return nil, nil } func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.evm.GasLimit))) + callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) return nil, nil } func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - interpreter.intPool.putOne(callContext.stack.pop()) + callContext.stack.pop() return nil, nil } func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { v := callContext.stack.peek() - offset := v.Int64() + offset := int64(v.Uint64()) v.SetBytes(callContext.memory.GetPtr(offset, 32)) return nil, nil } @@ -606,58 +495,51 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // pop value of the stack mStart, val := callContext.stack.pop(), callContext.stack.pop() - callContext.memory.Set32(mStart.Uint64(), val) - - interpreter.intPool.put(mStart, val) + callContext.memory.Set32(mStart.Uint64(), &val) return nil, nil } func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - off, val := callContext.stack.pop().Int64(), callContext.stack.pop().Int64() - callContext.memory.store[off] = byte(val & 0xff) - + off, val := callContext.stack.pop(), callContext.stack.pop() + callContext.memory.store[off.Uint64()] = byte(val.Uint64()) return nil, nil } func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { loc := callContext.stack.peek() - val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), common.BigToHash(loc)) + hash := common.Hash(loc.Bytes32()) + val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash) loc.SetBytes(val.Bytes()) return nil, nil } func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - loc := common.BigToHash(callContext.stack.pop()) + loc := callContext.stack.pop() val := callContext.stack.pop() - interpreter.evm.StateDB.SetState(callContext.contract.Address(), loc, common.BigToHash(val)) - - interpreter.intPool.putOne(val) + interpreter.evm.StateDB.SetState(callContext.contract.Address(), + common.Hash(loc.Bytes32()), common.Hash(val.Bytes32())) return nil, nil } func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { pos := callContext.stack.pop() - if !callContext.contract.validJumpdest(pos) { + if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() - - interpreter.intPool.putOne(pos) return nil, nil } func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { pos, cond := callContext.stack.pop(), callContext.stack.pop() - if cond.Sign() != 0 { - if !callContext.contract.validJumpdest(pos) { + if !cond.IsZero() { + if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() } else { *pc++ } - - interpreter.intPool.put(pos, cond) return nil, nil } @@ -666,17 +548,17 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( } func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetUint64(*pc)) + callContext.stack.push(new(uint256.Int).SetUint64(*pc)) return nil, nil } func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetInt64(int64(callContext.memory.Len()))) + callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len()))) return nil, nil } func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetUint64(callContext.contract.Gas)) + callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas)) return nil, nil } @@ -684,28 +566,30 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] var ( value = callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop() - input = callContext.memory.GetCopy(offset.Int64(), size.Int64()) + input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = callContext.contract.Gas ) if interpreter.evm.chainRules.IsEIP150 { gas -= gas / 64 } + // reuse size int for stackvalue + stackvalue := size callContext.contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value) + res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig()) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas { - callContext.stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { - callContext.stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) + stackvalue.SetBytes(addr.Bytes()) } + callContext.stack.push(&stackvalue) callContext.contract.Gas += returnGas - interpreter.intPool.put(value, offset, size) if suberr == ErrExecutionReverted { return res, nil @@ -718,22 +602,25 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ endowment = callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop() salt = callContext.stack.pop() - input = callContext.memory.GetCopy(offset.Int64(), size.Int64()) + input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = callContext.contract.Gas ) // Apply EIP150 gas -= gas / 64 callContext.contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, endowment, salt) + // reuse size int for stackvalue + stackvalue := size + res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, + endowment.ToBig(), salt.ToBig()) // Push item on the stack based on the returned error. if suberr != nil { - callContext.stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) + stackvalue.SetBytes(addr.Bytes()) } + callContext.stack.push(&stackvalue) callContext.contract.Gas += returnGas - interpreter.intPool.put(endowment, offset, size, salt) if suberr == ErrExecutionReverted { return res, nil @@ -742,130 +629,134 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ } func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + stack := callContext.stack // Pop gas. The actual gas in interpreter.evm.callGasTemp. - interpreter.intPool.putOne(callContext.stack.pop()) + // We can use this as a temporary value + temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - toAddr := common.BigToAddress(addr) - value = math.U256(value) + addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) // Get the arguments from the memory. - args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - if value.Sign() != 0 { + if !value.IsZero() { gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value) + ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig()) if err != nil { - callContext.stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == ErrExecutionReverted { ret = common.CopyBytes(ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } callContext.contract.Gas += returnGas - interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil } func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - interpreter.intPool.putOne(callContext.stack.pop()) + stack := callContext.stack + // We use it as a temporary value + temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - toAddr := common.BigToAddress(addr) - value = math.U256(value) + addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - if value.Sign() != 0 { + if !value.IsZero() { gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value) + ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig()) if err != nil { - callContext.stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == ErrExecutionReverted { ret = common.CopyBytes(ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } callContext.contract.Gas += returnGas - interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil } func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + stack := callContext.stack // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - interpreter.intPool.putOne(callContext.stack.pop()) + // We use it as a temporary value + temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - toAddr := common.BigToAddress(addr) + addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas) if err != nil { - callContext.stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == ErrExecutionReverted { ret = common.CopyBytes(ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } callContext.contract.Gas += returnGas - interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - interpreter.intPool.putOne(callContext.stack.pop()) + stack := callContext.stack + // We use it as a temporary value + temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - toAddr := common.BigToAddress(addr) + addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas) if err != nil { - callContext.stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == ErrExecutionReverted { ret = common.CopyBytes(ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } callContext.contract.Gas += returnGas - interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { offset, size := callContext.stack.pop(), callContext.stack.pop() - ret := callContext.memory.GetPtr(offset.Int64(), size.Int64()) + ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - interpreter.intPool.put(offset, size) return ret, nil } func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { offset, size := callContext.stack.pop(), callContext.stack.pop() - ret := callContext.memory.GetPtr(offset.Int64(), size.Int64()) + ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - interpreter.intPool.put(offset, size) return ret, nil } @@ -874,9 +765,9 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + beneficiary := callContext.stack.pop() balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) - interpreter.evm.StateDB.AddBalance(common.BigToAddress(callContext.stack.pop()), balance) - + interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) interpreter.evm.StateDB.Suicide(callContext.contract.Address()) return nil, nil } @@ -887,12 +778,14 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ func makeLog(size int) executionFunc { return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { topics := make([]common.Hash, size) - mStart, mSize := callContext.stack.pop(), callContext.stack.pop() + stack := callContext.stack + mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { - topics[i] = common.BigToHash(callContext.stack.pop()) + addr := stack.pop() + topics[i] = common.Hash(addr.Bytes32()) } - d := callContext.memory.GetCopy(mStart.Int64(), mSize.Int64()) + d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) interpreter.evm.StateDB.AddLog(&types.Log{ Address: callContext.contract.Address(), Topics: topics, @@ -902,7 +795,6 @@ func makeLog(size int) executionFunc { BlockNumber: interpreter.evm.BlockNumber.Uint64(), }) - interpreter.intPool.put(mStart, mSize) return nil, nil } } @@ -911,13 +803,13 @@ func makeLog(size int) executionFunc { func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { var ( codeLen = uint64(len(callContext.contract.Code)) - integer = interpreter.intPool.get() + integer = new(uint256.Int) ) *pc += 1 if *pc < codeLen { callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc]))) } else { - callContext.stack.push(integer.SetUint64(0)) + callContext.stack.push(integer.Clear()) } return nil, nil } @@ -937,8 +829,9 @@ func makePush(size uint64, pushByteSize int) executionFunc { endMin = startMin + pushByteSize } - integer := interpreter.intPool.get() - callContext.stack.push(integer.SetBytes(common.RightPadBytes(callContext.contract.Code[startMin:endMin], pushByteSize))) + integer := new(uint256.Int) + callContext.stack.push(integer.SetBytes(common.RightPadBytes( + callContext.contract.Code[startMin:endMin], pushByteSize))) *pc += size return nil, nil @@ -948,7 +841,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { // make dup instruction function func makeDup(size int64) executionFunc { return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.dup(interpreter.intPool, int(size)) + callContext.stack.dup(int(size)) return nil, nil } } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index f439b643a5dd..13dfa960d1ec 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -21,14 +21,13 @@ import ( "encoding/json" "fmt" "log" - "math/big" "os" "testing" - "github.com/XinFinOrg/XDPoSChain/params" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) type TwoOperandTestcase struct { @@ -91,31 +90,6 @@ func init() { } } -// getResult is a convenience function to generate the expected values -func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { - var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) - interpreter = env.interpreter.(*EVMInterpreter) - ) - interpreter.intPool = poolOfIntPools.get() - result := make([]TwoOperandTestcase, len(args)) - for i, param := range args { - x := new(big.Int).SetBytes(common.Hex2Bytes(param.x)) - y := new(big.Int).SetBytes(common.Hex2Bytes(param.y)) - stack.push(x) - stack.push(y) - _, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil}) - if err != nil { - log.Fatalln(err) - } - actual := stack.pop() - result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} - } - return result -} - func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { var ( @@ -124,42 +98,23 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu pc = uint64(0) evmInterpreter = env.interpreter.(*EVMInterpreter) ) - // Stuff a couple of nonzero bigints into pool, to ensure that ops do not rely on pooled integers to be zero - evmInterpreter.intPool = poolOfIntPools.get() - evmInterpreter.intPool.put(big.NewInt(-1337)) - evmInterpreter.intPool.put(big.NewInt(-1337)) - evmInterpreter.intPool.put(big.NewInt(-1337)) for i, test := range tests { - x := new(big.Int).SetBytes(common.Hex2Bytes(test.X)) - y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y)) - expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected)) + x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y)) + expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) stack.push(x) stack.push(y) opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil}) + if len(stack.data) != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) + } actual := stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual) } - // Check pool usage - // 1.pool is not allowed to contain anything on the stack - // 2.pool is not allowed to contain the same pointers twice - if evmInterpreter.intPool.pool.len() > 0 { - - poolvals := make(map[*big.Int]struct{}) - poolvals[actual] = struct{}{} - - for evmInterpreter.intPool.pool.len() > 0 { - key := evmInterpreter.intPool.get() - if _, exist := poolvals[key]; exist { - t.Errorf("Testcase %v %d, pool contains double-entry", name, i) - } - poolvals[key] = struct{}{} - } - } } - poolOfIntPools.put(evmInterpreter.intPool) } func TestByteOp(t *testing.T) { @@ -235,6 +190,68 @@ func TestSAR(t *testing.T) { testTwoOperandOp(t, tests, opSAR, "sar") } +func TestAddMod(t *testing.T) { + var ( + env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + stack = newstack() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + pc = uint64(0) + ) + tests := []struct { + x string + y string + z string + expected string + }{ + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, + } + // x + y = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd + // in 256 bit repr, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd + + for i, test := range tests { + x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.x)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y)) + z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z)) + expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected)) + stack.push(z) + stack.push(y) + stack.push(x) + opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil}) + actual := stack.pop() + if actual.Cmp(expected) != 0 { + t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) + } + } +} + +// getResult is a convenience function to generate the expected values +func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { + var ( + env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + interpreter = env.interpreter.(*EVMInterpreter) + ) + result := make([]TwoOperandTestcase, len(args)) + for i, param := range args { + x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) + stack.push(x) + stack.push(y) + _, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil}) + if err != nil { + log.Fatalln(err) + } + actual := stack.pop() + result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} + } + return result +} + // utility function to fill the json-file with testcases // Enable this test to generate the 'testcases_xx.json' files func TestWriteExpectedValues(t *testing.T) { @@ -276,7 +293,6 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { ) env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() // convert args byteArgs := make([][]byte, len(args)) for i, arg := range args { @@ -286,13 +302,13 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { bench.ResetTimer() for i := 0; i < bench.N; i++ { for _, arg := range byteArgs { - a := new(big.Int).SetBytes(arg) + a := new(uint256.Int) + a.SetBytes(arg) stack.push(a) } op(&pc, evmInterpreter, &callCtx{nil, stack, nil}) stack.pop() } - poolOfIntPools.put(evmInterpreter.intPool) } func BenchmarkOpAdd64(b *testing.B) { @@ -512,21 +528,19 @@ func TestOpMstore(t *testing.T) { ) env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() mem.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" - stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0)) + stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } - stack.pushN(big.NewInt(0x1), big.NewInt(0)) + stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } - poolOfIntPools.put(evmInterpreter.intPool) } func BenchmarkOpMstore(bench *testing.B) { @@ -538,18 +552,16 @@ func BenchmarkOpMstore(bench *testing.B) { ) env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() mem.Resize(64) pc := uint64(0) - memStart := big.NewInt(0) - value := big.NewInt(0x1337) + memStart := new(uint256.Int) + value := new(uint256.Int).SetUint64(0x1337) bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(value, memStart) + stack.pushN(*value, *memStart) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } - poolOfIntPools.put(evmInterpreter.intPool) } func BenchmarkOpSHA3(bench *testing.B) { @@ -560,17 +572,15 @@ func BenchmarkOpSHA3(bench *testing.B) { evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() mem.Resize(32) pc := uint64(0) - start := big.NewInt(0) + start := new(uint256.Int) bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(big.NewInt(32), start) + stack.pushN(*uint256.NewInt(32), *start) opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } - poolOfIntPools.put(evmInterpreter.intPool) } func TestCreate2Addreses(t *testing.T) { @@ -644,6 +654,5 @@ func TestCreate2Addreses(t *testing.T) { if !bytes.Equal(expected.Bytes(), address.Bytes()) { t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String()) } - } } diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go deleted file mode 100644 index 82fbfed699de..000000000000 --- a/core/vm/int_pool_verifier.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build VERIFY_EVM_INTEGER_POOL - -package vm - -import "fmt" - -const verifyPool = true - -func verifyIntegerPool(ip *intPool) { - for i, item := range ip.pool.data { - if item.Cmp(checkVal) != 0 { - panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i)) - } - } -} diff --git a/core/vm/int_pool_verifier_empty.go b/core/vm/int_pool_verifier_empty.go deleted file mode 100644 index a5f1dc02b7fe..000000000000 --- a/core/vm/int_pool_verifier_empty.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build !VERIFY_EVM_INTEGER_POOL - -package vm - -const verifyPool = false - -func verifyIntegerPool(ip *intPool) {} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 2662a1922ec8..e857a2169c19 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -83,8 +83,6 @@ type EVMInterpreter struct { evm *EVM cfg Config - intPool *intPool - hasher keccakState // Keccak256 hasher instance shared across opcodes hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes @@ -138,13 +136,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // considered a revert-and-consume-all-gas operation except for // ErrExecutionReverted which means revert-and-keep-gas-left. func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { - if in.intPool == nil { - in.intPool = poolOfIntPools.get() - defer func() { - poolOfIntPools.put(in.intPool) - in.intPool = nil - }() - } // Increment the call depth which is restricted to 1024 in.evm.depth++ @@ -188,9 +179,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( ) contract.Input = input - // Reclaim the stack as an int pool when the execution stops - defer func() { in.intPool.put(stack.data...) }() - if in.cfg.Debug { defer func() { if err != nil { @@ -285,11 +273,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // execute the operation res, err = operation.execute(&pc, in, callContext) - // verifyPool is a build flag. Pool verification makes sure the integrity - // of the integer pool by comparing values to a default value. - if verifyPool { - verifyIntegerPool(in.intPool) - } // if the operation clears the return data (e.g. it has returning data) // set the last return to the result of the operation. if operation.returns { diff --git a/core/vm/intpool.go b/core/vm/intpool.go deleted file mode 100644 index eed074b07338..000000000000 --- a/core/vm/intpool.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package vm - -import ( - "math/big" - "sync" -) - -var checkVal = big.NewInt(-42) - -const poolLimit = 256 - -// intPool is a pool of big integers that -// can be reused for all big.Int operations. -type intPool struct { - pool *Stack -} - -func newIntPool() *intPool { - return &intPool{pool: newstack()} -} - -// get retrieves a big int from the pool, allocating one if the pool is empty. -// Note, the returned int's value is arbitrary and will not be zeroed! -func (p *intPool) get() *big.Int { - if p.pool.len() > 0 { - return p.pool.pop() - } - return new(big.Int) -} - -// getZero retrieves a big int from the pool, setting it to zero or allocating -// a new one if the pool is empty. -func (p *intPool) getZero() *big.Int { - if p.pool.len() > 0 { - return p.pool.pop().SetUint64(0) - } - return new(big.Int) -} - -// putOne returns an allocated big int to the pool to be later reused by get calls. -// Note, the values as saved as is; neither put nor get zeroes the ints out! -// As opposed to 'put' with variadic args, this method becomes inlined by the -// go compiler -func (p *intPool) putOne(i *big.Int) { - if len(p.pool.data) > poolLimit { - return - } - p.pool.push(i) -} - -// put returns an allocated big int to the pool to be later reused by get calls. -// Note, the values as saved as is; neither put nor get zeroes the ints out! -func (p *intPool) put(is ...*big.Int) { - if len(p.pool.data) > poolLimit { - return - } - for _, i := range is { - // verifyPool is a build flag. Pool verification makes sure the integrity - // of the integer pool by comparing values to a default value. - if verifyPool { - i.Set(checkVal) - } - p.pool.push(i) - } -} - -// The intPool pool's default capacity -const poolDefaultCap = 25 - -// intPoolPool manages a pool of intPools. -type intPoolPool struct { - pools []*intPool - lock sync.Mutex -} - -var poolOfIntPools = &intPoolPool{ - pools: make([]*intPool, 0, poolDefaultCap), -} - -// get is looking for an available pool to return. -func (ipp *intPoolPool) get() *intPool { - ipp.lock.Lock() - defer ipp.lock.Unlock() - - if len(poolOfIntPools.pools) > 0 { - ip := ipp.pools[len(ipp.pools)-1] - ipp.pools = ipp.pools[:len(ipp.pools)-1] - return ip - } - return newIntPool() -} - -// put a pool that has been allocated with get. -func (ipp *intPoolPool) put(ip *intPool) { - ipp.lock.Lock() - defer ipp.lock.Unlock() - - if len(ipp.pools) < cap(ipp.pools) { - ipp.pools = append(ipp.pools, ip) - } -} diff --git a/core/vm/intpool_test.go b/core/vm/intpool_test.go deleted file mode 100644 index 6c0d00f3ce5c..000000000000 --- a/core/vm/intpool_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package vm - -import ( - "testing" -) - -func TestIntPoolPoolGet(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - - nip := poolOfIntPools.get() - if nip == nil { - t.Fatalf("Invalid pool allocation") - } -} - -func TestIntPoolPoolPut(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - - nip := poolOfIntPools.get() - if len(poolOfIntPools.pools) != 0 { - t.Fatalf("Pool got added to list when none should have been") - } - - poolOfIntPools.put(nip) - if len(poolOfIntPools.pools) == 0 { - t.Fatalf("Pool did not get added to list when one should have been") - } -} - -func TestIntPoolPoolReUse(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - nip := poolOfIntPools.get() - poolOfIntPools.put(nip) - poolOfIntPools.get() - - if len(poolOfIntPools.pools) != 0 { - t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0) - } -} diff --git a/core/vm/logger.go b/core/vm/logger.go index 970ff90fb36e..6cdb7a7c5bed 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -156,8 +156,8 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui // it in the local storage container. if op == SSTORE && stack.len() >= 2 { var ( - value = common.BigToHash(stack.data[stack.len()-2]) - address = common.BigToHash(stack.data[stack.len()-1]) + value = common.Hash(stack.data[stack.len()-2].Bytes32()) + address = common.Hash(stack.data[stack.len()-1].Bytes32()) ) l.changedValues[contract.Address()][address] = value } @@ -172,7 +172,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui if !l.cfg.DisableStack { stck = make([]*big.Int, len(stack.Data())) for i, item := range stack.Data() { - stck[i] = new(big.Int).Set(item) + stck[i] = new(big.Int).Set(item.ToBig()) } } // Copy a snapshot of the current storage to a new container diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 2eeba8dfacc3..593626eda2e0 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -62,7 +62,12 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint log.Memory = memory.Data() } if !l.cfg.DisableStack { - log.Stack = stack.Data() + //TODO(@holiman) improve this + logstack := make([]*big.Int, len(stack.Data())) + for i, item := range stack.Data() { + logstack[i] = item.ToBig() + } + log.Stack = logstack } return l.encoder.Encode(log) } diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index e3e0a085b998..9ee02b97c028 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -20,10 +20,10 @@ import ( "math/big" "testing" - "github.com/XinFinOrg/XDPoSChain/params" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) type dummyContractRef struct { @@ -57,8 +57,8 @@ func TestStoreCapture(t *testing.T) { stack = newstack() contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) ) - stack.push(big.NewInt(1)) - stack.push(big.NewInt(0)) + stack.push(uint256.NewInt(1)) + stack.push(new(uint256.Int)) var index common.Hash logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) if len(logger.changedValues[contract.Address()]) == 0 { diff --git a/core/vm/memory.go b/core/vm/memory.go index 7fc44a096188..ba5f8485dc95 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -18,9 +18,8 @@ package vm import ( "fmt" - "math/big" - "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/holiman/uint256" ) // Memory implements a simple memory model for the ethereum virtual machine. @@ -50,7 +49,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) { // Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to // 32 bytes. -func (m *Memory) Set32(offset uint64, val *big.Int) { +func (m *Memory) Set32(offset uint64, val *uint256.Int) { // length of store may never be less than offset + size. // The store should be resized PRIOR to setting the memory if offset+32 > uint64(len(m.store)) { @@ -59,7 +58,7 @@ func (m *Memory) Set32(offset uint64, val *big.Int) { // Zero the memory area copy(m.store[offset:offset+32], []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, 0}) // Fill in relevant bits - math.ReadBits(val, m.store[offset:offset+32]) + val.WriteToSlice(m.store[offset:]) } // Resize resizes the memory to size diff --git a/core/vm/stack.go b/core/vm/stack.go index c9c3d07f4b6f..2367da1d7c97 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -18,36 +18,36 @@ package vm import ( "fmt" - "math/big" + + "github.com/holiman/uint256" ) // Stack is an object for basic stack operations. Items popped to the stack are // expected to be changed and modified. stack does not take care of adding newly // initialised objects. type Stack struct { - data []*big.Int + data []uint256.Int } func newstack() *Stack { - return &Stack{data: make([]*big.Int, 0, 1024)} + return &Stack{data: make([]uint256.Int, 0, 16)} } -// Data returns the underlying big.Int array. -func (st *Stack) Data() []*big.Int { +// Data returns the underlying uint256.Int array. +func (st *Stack) Data() []uint256.Int { return st.data } -func (st *Stack) push(d *big.Int) { +func (st *Stack) push(d *uint256.Int) { // NOTE push limit (1024) is checked in baseCheck - //stackItem := new(big.Int).Set(d) - //st.data = append(st.data, stackItem) - st.data = append(st.data, d) + st.data = append(st.data, *d) } -func (st *Stack) pushN(ds ...*big.Int) { +func (st *Stack) pushN(ds ...uint256.Int) { + // FIXME: Is there a way to pass args by pointers. st.data = append(st.data, ds...) } -func (st *Stack) pop() (ret *big.Int) { +func (st *Stack) pop() (ret uint256.Int) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] return @@ -61,17 +61,17 @@ func (st *Stack) swap(n int) { st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] } -func (st *Stack) dup(pool *intPool, n int) { - st.push(pool.get().Set(st.data[st.len()-n])) +func (st *Stack) dup(n int) { + st.push(&st.data[st.len()-n]) } -func (st *Stack) peek() *big.Int { - return st.data[st.len()-1] +func (st *Stack) peek() *uint256.Int { + return &st.data[st.len()-1] } // Back returns the n'th item in stack -func (st *Stack) Back(n int) *big.Int { - return st.data[st.len()-n-1] +func (st *Stack) Back(n int) *uint256.Int { + return &st.data[st.len()-n-1] } // Print dumps the content of the stack diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 94c828fc900e..235f752bac0a 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -162,7 +162,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int { log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx) return new(big.Int) } - return sw.stack.Data()[len(sw.stack.Data())-idx-1] + return sw.stack.Back(idx).ToBig() } // pushObject assembles a JSVM object wrapping a swappable stack and pushes it diff --git a/go.mod b/go.mod index e2065745fde8..8d19bc4956fc 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/gorilla/websocket v1.4.2 github.com/hashicorp/golang-lru v0.5.3 + github.com/holiman/uint256 v1.2.4 github.com/huin/goupnp v1.3.0 github.com/influxdata/influxdb v1.7.9 github.com/jackpal/go-nat-pmp v1.0.2 @@ -61,7 +62,6 @@ require ( github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/holiman/uint256 v1.2.3 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect diff --git a/go.sum b/go.sum index 70a3ac6c9348..19df00ede131 100644 --- a/go.sum +++ b/go.sum @@ -140,6 +140,8 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8 github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= From 06d5da016c6729fad9cfb4348929be3553e68177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 16 Jul 2020 15:32:01 +0300 Subject: [PATCH 05/59] core/vm: use pointers to operations vs. copy by value (#21336) --- core/vm/eips.go | 6 +- core/vm/interpreter.go | 6 +- core/vm/jump_table.go | 163 +++-------------------------------------- 3 files changed, 16 insertions(+), 159 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 65829f1d568a..6ba7bbd6ec7a 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -52,12 +52,11 @@ func enable1884(jt *JumpTable) { jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884 // New opcode - jt[SELFBALANCE] = operation{ + jt[SELFBALANCE] = &operation{ execute: opSelfBalance, constantGas: GasFastStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, } } @@ -71,12 +70,11 @@ func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx // - Adds an opcode that returns the current chain’s EIP-155 unique identifier func enable1344(jt *JumpTable) { // New opcode - jt[CHAINID] = operation{ + jt[CHAINID] = &operation{ execute: opChainID, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, } } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index e857a2169c19..aa70d7c1020e 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -32,7 +32,7 @@ type Config struct { NoRecursion bool // Disables call, callcode, delegate call and create EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - JumpTable [256]operation // EVM instruction table, automatically populated if unset + JumpTable [256]*operation // EVM instruction table, automatically populated if unset EWASMInterpreter string // External EWASM interpreter options EVMInterpreter string // External EVM interpreter options @@ -95,7 +95,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // We use the STOP instruction whether to see // the jump table was initialised. If it was not // we'll set the default jump table. - if !cfg.JumpTable[STOP].valid { + if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { case evm.chainRules.IsIstanbul: @@ -209,7 +209,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // enough stack items available to perform the operation. op = contract.GetOp(pc) operation := in.cfg.JumpTable[op] - if !operation.valid { + if operation == nil { return nil, &ErrInvalidOpCode{opcode: op} } // Validate stack diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index b3a0f7ff7ce9..7580544ba2ca 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -44,7 +44,6 @@ type operation struct { halts bool // indicates whether the operation should halt further execution jumps bool // indicates whether the program counter should not increment writes bool // determines whether this a state modifying operation - valid bool // indication whether the retrieved operation is valid and known reverts bool // determines whether the operation reverts state (implicitly halts) returns bool // determines whether the operations sets the return data content } @@ -60,7 +59,7 @@ var ( ) // JumpTable contains the EVM opcodes supported at a given fork. -type JumpTable [256]operation +type JumpTable [256]*operation // newIstanbulInstructionSet returns the frontier, homestead // byzantium, contantinople and petersburg instructions. @@ -78,42 +77,37 @@ func newIstanbulInstructionSet() JumpTable { // byzantium and contantinople instructions. func newConstantinopleInstructionSet() JumpTable { instructionSet := newByzantiumInstructionSet() - instructionSet[SHL] = operation{ + instructionSet[SHL] = &operation{ execute: opSHL, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, } - instructionSet[SHR] = operation{ + instructionSet[SHR] = &operation{ execute: opSHR, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, } - instructionSet[SAR] = operation{ + instructionSet[SAR] = &operation{ execute: opSAR, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, } - instructionSet[EXTCODEHASH] = operation{ + instructionSet[EXTCODEHASH] = &operation{ execute: opExtCodeHash, constantGas: params.ExtcodeHashGasConstantinople, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, } - instructionSet[CREATE2] = operation{ + instructionSet[CREATE2] = &operation{ execute: opCreate2, constantGas: params.Create2Gas, dynamicGas: gasCreate2, minStack: minStack(4, 1), maxStack: maxStack(4, 1), memorySize: memoryCreate2, - valid: true, writes: true, returns: true, } @@ -124,39 +118,35 @@ func newConstantinopleInstructionSet() JumpTable { // byzantium instructions. func newByzantiumInstructionSet() JumpTable { instructionSet := newSpuriousDragonInstructionSet() - instructionSet[STATICCALL] = operation{ + instructionSet[STATICCALL] = &operation{ execute: opStaticCall, constantGas: params.CallGasEIP150, dynamicGas: gasStaticCall, minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryStaticCall, - valid: true, returns: true, } - instructionSet[RETURNDATASIZE] = operation{ + instructionSet[RETURNDATASIZE] = &operation{ execute: opReturnDataSize, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, } - instructionSet[RETURNDATACOPY] = operation{ + instructionSet[RETURNDATACOPY] = &operation{ execute: opReturnDataCopy, constantGas: GasFastestStep, dynamicGas: gasReturnDataCopy, minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryReturnDataCopy, - valid: true, } - instructionSet[REVERT] = operation{ + instructionSet[REVERT] = &operation{ execute: opRevert, dynamicGas: gasRevert, minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryRevert, - valid: true, reverts: true, returns: true, } @@ -188,14 +178,13 @@ func newTangerineWhistleInstructionSet() JumpTable { // instructions that can be executed during the homestead phase. func newHomesteadInstructionSet() JumpTable { instructionSet := newFrontierInstructionSet() - instructionSet[DELEGATECALL] = operation{ + instructionSet[DELEGATECALL] = &operation{ execute: opDelegateCall, dynamicGas: gasDelegateCall, constantGas: params.CallGasFrontier, minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryDelegateCall, - valid: true, returns: true, } return instructionSet @@ -211,161 +200,138 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(0, 0), maxStack: maxStack(0, 0), halts: true, - valid: true, }, ADD: { execute: opAdd, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, MUL: { execute: opMul, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SUB: { execute: opSub, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, DIV: { execute: opDiv, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SDIV: { execute: opSdiv, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, MOD: { execute: opMod, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SMOD: { execute: opSmod, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, ADDMOD: { execute: opAddmod, constantGas: GasMidStep, minStack: minStack(3, 1), maxStack: maxStack(3, 1), - valid: true, }, MULMOD: { execute: opMulmod, constantGas: GasMidStep, minStack: minStack(3, 1), maxStack: maxStack(3, 1), - valid: true, }, EXP: { execute: opExp, dynamicGas: gasExpFrontier, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SIGNEXTEND: { execute: opSignExtend, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, LT: { execute: opLt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, GT: { execute: opGt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SLT: { execute: opSlt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SGT: { execute: opSgt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, EQ: { execute: opEq, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, ISZERO: { execute: opIszero, constantGas: GasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, AND: { execute: opAnd, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, XOR: { execute: opXor, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, OR: { execute: opOr, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, NOT: { execute: opNot, constantGas: GasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, BYTE: { execute: opByte, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SHA3: { execute: opSha3, @@ -374,56 +340,48 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 1), maxStack: maxStack(2, 1), memorySize: memorySha3, - valid: true, }, ADDRESS: { execute: opAddress, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, BALANCE: { execute: opBalance, constantGas: params.BalanceGasFrontier, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, ORIGIN: { execute: opOrigin, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CALLER: { execute: opCaller, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CALLVALUE: { execute: opCallValue, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CALLDATALOAD: { execute: opCallDataLoad, constantGas: GasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, CALLDATASIZE: { execute: opCallDataSize, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CALLDATACOPY: { execute: opCallDataCopy, @@ -432,14 +390,12 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryCallDataCopy, - valid: true, }, CODESIZE: { execute: opCodeSize, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CODECOPY: { execute: opCodeCopy, @@ -448,21 +404,18 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryCodeCopy, - valid: true, }, GASPRICE: { execute: opGasprice, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, EXTCODESIZE: { execute: opExtCodeSize, constantGas: params.ExtcodeSizeGasFrontier, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, EXTCODECOPY: { execute: opExtCodeCopy, @@ -471,56 +424,48 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(4, 0), maxStack: maxStack(4, 0), memorySize: memoryExtCodeCopy, - valid: true, }, BLOCKHASH: { execute: opBlockhash, constantGas: GasExtStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, COINBASE: { execute: opCoinbase, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, TIMESTAMP: { execute: opTimestamp, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, NUMBER: { execute: opNumber, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, DIFFICULTY: { execute: opDifficulty, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, GASLIMIT: { execute: opGasLimit, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, POP: { execute: opPop, constantGas: GasQuickStep, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - valid: true, }, MLOAD: { execute: opMload, @@ -529,7 +474,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(1, 1), maxStack: maxStack(1, 1), memorySize: memoryMLoad, - valid: true, }, MSTORE: { execute: opMstore, @@ -538,7 +482,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryMStore, - valid: true, }, MSTORE8: { execute: opMstore8, @@ -547,22 +490,18 @@ func newFrontierInstructionSet() JumpTable { memorySize: memoryMStore8, minStack: minStack(2, 0), maxStack: maxStack(2, 0), - - valid: true, }, SLOAD: { execute: opSload, constantGas: params.SloadGasFrontier, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, SSTORE: { execute: opSstore, dynamicGas: gasSStore, minStack: minStack(2, 0), maxStack: maxStack(2, 0), - valid: true, writes: true, }, JUMP: { @@ -571,7 +510,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(1, 0), maxStack: maxStack(1, 0), jumps: true, - valid: true, }, JUMPI: { execute: opJumpi, @@ -579,483 +517,414 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), jumps: true, - valid: true, }, PC: { execute: opPc, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, MSIZE: { execute: opMsize, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, GAS: { execute: opGas, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, JUMPDEST: { execute: opJumpdest, constantGas: params.JumpdestGas, minStack: minStack(0, 0), maxStack: maxStack(0, 0), - valid: true, }, PUSH1: { execute: opPush1, constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH2: { execute: makePush(2, 2), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH3: { execute: makePush(3, 3), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH4: { execute: makePush(4, 4), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH5: { execute: makePush(5, 5), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH6: { execute: makePush(6, 6), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH7: { execute: makePush(7, 7), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH8: { execute: makePush(8, 8), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH9: { execute: makePush(9, 9), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH10: { execute: makePush(10, 10), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH11: { execute: makePush(11, 11), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH12: { execute: makePush(12, 12), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH13: { execute: makePush(13, 13), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH14: { execute: makePush(14, 14), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH15: { execute: makePush(15, 15), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH16: { execute: makePush(16, 16), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH17: { execute: makePush(17, 17), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH18: { execute: makePush(18, 18), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH19: { execute: makePush(19, 19), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH20: { execute: makePush(20, 20), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH21: { execute: makePush(21, 21), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH22: { execute: makePush(22, 22), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH23: { execute: makePush(23, 23), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH24: { execute: makePush(24, 24), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH25: { execute: makePush(25, 25), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH26: { execute: makePush(26, 26), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH27: { execute: makePush(27, 27), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH28: { execute: makePush(28, 28), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH29: { execute: makePush(29, 29), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH30: { execute: makePush(30, 30), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH31: { execute: makePush(31, 31), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH32: { execute: makePush(32, 32), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, DUP1: { execute: makeDup(1), constantGas: GasFastestStep, minStack: minDupStack(1), maxStack: maxDupStack(1), - valid: true, }, DUP2: { execute: makeDup(2), constantGas: GasFastestStep, minStack: minDupStack(2), maxStack: maxDupStack(2), - valid: true, }, DUP3: { execute: makeDup(3), constantGas: GasFastestStep, minStack: minDupStack(3), maxStack: maxDupStack(3), - valid: true, }, DUP4: { execute: makeDup(4), constantGas: GasFastestStep, minStack: minDupStack(4), maxStack: maxDupStack(4), - valid: true, }, DUP5: { execute: makeDup(5), constantGas: GasFastestStep, minStack: minDupStack(5), maxStack: maxDupStack(5), - valid: true, }, DUP6: { execute: makeDup(6), constantGas: GasFastestStep, minStack: minDupStack(6), maxStack: maxDupStack(6), - valid: true, }, DUP7: { execute: makeDup(7), constantGas: GasFastestStep, minStack: minDupStack(7), maxStack: maxDupStack(7), - valid: true, }, DUP8: { execute: makeDup(8), constantGas: GasFastestStep, minStack: minDupStack(8), maxStack: maxDupStack(8), - valid: true, }, DUP9: { execute: makeDup(9), constantGas: GasFastestStep, minStack: minDupStack(9), maxStack: maxDupStack(9), - valid: true, }, DUP10: { execute: makeDup(10), constantGas: GasFastestStep, minStack: minDupStack(10), maxStack: maxDupStack(10), - valid: true, }, DUP11: { execute: makeDup(11), constantGas: GasFastestStep, minStack: minDupStack(11), maxStack: maxDupStack(11), - valid: true, }, DUP12: { execute: makeDup(12), constantGas: GasFastestStep, minStack: minDupStack(12), maxStack: maxDupStack(12), - valid: true, }, DUP13: { execute: makeDup(13), constantGas: GasFastestStep, minStack: minDupStack(13), maxStack: maxDupStack(13), - valid: true, }, DUP14: { execute: makeDup(14), constantGas: GasFastestStep, minStack: minDupStack(14), maxStack: maxDupStack(14), - valid: true, }, DUP15: { execute: makeDup(15), constantGas: GasFastestStep, minStack: minDupStack(15), maxStack: maxDupStack(15), - valid: true, }, DUP16: { execute: makeDup(16), constantGas: GasFastestStep, minStack: minDupStack(16), maxStack: maxDupStack(16), - valid: true, }, SWAP1: { execute: makeSwap(1), constantGas: GasFastestStep, minStack: minSwapStack(2), maxStack: maxSwapStack(2), - valid: true, }, SWAP2: { execute: makeSwap(2), constantGas: GasFastestStep, minStack: minSwapStack(3), maxStack: maxSwapStack(3), - valid: true, }, SWAP3: { execute: makeSwap(3), constantGas: GasFastestStep, minStack: minSwapStack(4), maxStack: maxSwapStack(4), - valid: true, }, SWAP4: { execute: makeSwap(4), constantGas: GasFastestStep, minStack: minSwapStack(5), maxStack: maxSwapStack(5), - valid: true, }, SWAP5: { execute: makeSwap(5), constantGas: GasFastestStep, minStack: minSwapStack(6), maxStack: maxSwapStack(6), - valid: true, }, SWAP6: { execute: makeSwap(6), constantGas: GasFastestStep, minStack: minSwapStack(7), maxStack: maxSwapStack(7), - valid: true, }, SWAP7: { execute: makeSwap(7), constantGas: GasFastestStep, minStack: minSwapStack(8), maxStack: maxSwapStack(8), - valid: true, }, SWAP8: { execute: makeSwap(8), constantGas: GasFastestStep, minStack: minSwapStack(9), maxStack: maxSwapStack(9), - valid: true, }, SWAP9: { execute: makeSwap(9), constantGas: GasFastestStep, minStack: minSwapStack(10), maxStack: maxSwapStack(10), - valid: true, }, SWAP10: { execute: makeSwap(10), constantGas: GasFastestStep, minStack: minSwapStack(11), maxStack: maxSwapStack(11), - valid: true, }, SWAP11: { execute: makeSwap(11), constantGas: GasFastestStep, minStack: minSwapStack(12), maxStack: maxSwapStack(12), - valid: true, }, SWAP12: { execute: makeSwap(12), constantGas: GasFastestStep, minStack: minSwapStack(13), maxStack: maxSwapStack(13), - valid: true, }, SWAP13: { execute: makeSwap(13), constantGas: GasFastestStep, minStack: minSwapStack(14), maxStack: maxSwapStack(14), - valid: true, }, SWAP14: { execute: makeSwap(14), constantGas: GasFastestStep, minStack: minSwapStack(15), maxStack: maxSwapStack(15), - valid: true, }, SWAP15: { execute: makeSwap(15), constantGas: GasFastestStep, minStack: minSwapStack(16), maxStack: maxSwapStack(16), - valid: true, }, SWAP16: { execute: makeSwap(16), constantGas: GasFastestStep, minStack: minSwapStack(17), maxStack: maxSwapStack(17), - valid: true, }, LOG0: { execute: makeLog(0), @@ -1063,7 +932,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryLog, - valid: true, writes: true, }, LOG1: { @@ -1072,7 +940,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryLog, - valid: true, writes: true, }, LOG2: { @@ -1081,7 +948,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(4, 0), maxStack: maxStack(4, 0), memorySize: memoryLog, - valid: true, writes: true, }, LOG3: { @@ -1090,7 +956,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(5, 0), maxStack: maxStack(5, 0), memorySize: memoryLog, - valid: true, writes: true, }, LOG4: { @@ -1099,7 +964,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(6, 0), maxStack: maxStack(6, 0), memorySize: memoryLog, - valid: true, writes: true, }, CREATE: { @@ -1109,7 +973,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 1), maxStack: maxStack(3, 1), memorySize: memoryCreate, - valid: true, writes: true, returns: true, }, @@ -1120,7 +983,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - valid: true, returns: true, }, CALLCODE: { @@ -1130,7 +992,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - valid: true, returns: true, }, RETURN: { @@ -1140,7 +1001,6 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(2, 0), memorySize: memoryReturn, halts: true, - valid: true, }, SELFDESTRUCT: { execute: opSuicide, @@ -1148,7 +1008,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(1, 0), maxStack: maxStack(1, 0), halts: true, - valid: true, writes: true, }, } From 446b9e86f4e742c16529424af136e6a1d17a5030 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 28 Sep 2020 14:14:45 +0200 Subject: [PATCH 06/59] core/vm, params: make 2200 in line with spec (#21605) --- core/vm/gas_table.go | 34 +++++++++++++++++----------------- params/protocol_params.go | 12 ++++-------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 719613f91227..f25ba9e4cdfb 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -164,18 +164,18 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi } // 0. If *gasleft* is less than or equal to 2300, fail the current call. -// 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted. +// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted. // 2. If current value does not equal new value: // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context): -// 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted. -// 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter. -// 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses: +// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted. +// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter. +// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses: // 2.2.1. If original value is not 0: -// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0. -// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter. +// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter. +// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter. // 2.2.2. If original value equals new value (this storage slot is reset): -// 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter. -// 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter. +// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter. +// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter. func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { // If we fail the minimum gas availability invariant, fail (0) if contract.Gas <= params.SstoreSentryGasEIP2200 { @@ -189,33 +189,33 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m value := common.Hash(y.Bytes32()) if current == value { // noop (1) - return params.SstoreNoopGasEIP2200, nil + return params.SloadGasEIP2200, nil } original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) - return params.SstoreInitGasEIP2200, nil + return params.SstoreSetGasEIP2200, nil } if value == (common.Hash{}) { // delete slot (2.1.2b) - evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) } - return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2) + return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) } if original != (common.Hash{}) { if current == (common.Hash{}) { // recreate slot (2.2.1.1) - evm.StateDB.SubRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200) } else if value == (common.Hash{}) { // delete slot (2.2.1.2) - evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) } } if original == value { if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) - evm.StateDB.AddRefund(params.SstoreInitRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) } else { // reset to original existing slot (2.2.2.2) - evm.StateDB.AddRefund(params.SstoreCleanRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) } } - return params.SstoreDirtyGasEIP2200, nil // dirty update (2.2) + return params.SloadGasEIP2200, nil // dirty update (2.2) } func makeGasLog(n uint64) gasFunc { diff --git a/params/protocol_params.go b/params/protocol_params.go index f7ab3cdd1046..d9272981d63e 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -98,14 +98,10 @@ const ( NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value - SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed - SstoreNoopGasEIP2200 uint64 = 800 // Once per SSTORE operation if the value doesn't change. - SstoreDirtyGasEIP2200 uint64 = 800 // Once per SSTORE operation if a dirty value is changed. - SstoreInitGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero - SstoreInitRefundEIP2200 uint64 = 19200 // Once per SSTORE operation for resetting to the original zero value - SstoreCleanGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else - SstoreCleanRefundEIP2200 uint64 = 4200 // Once per SSTORE operation for resetting to the original non-zero value - SstoreClearRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot + SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed + SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero + SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else + SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot Create2Gas uint64 = 32000 // Once per CREATE2 operation SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. From 6077ecb19531cc06159dcef1a2f35f54106844d1 Mon Sep 17 00:00:00 2001 From: aaronbuchwald Date: Fri, 30 Apr 2021 06:49:13 -0400 Subject: [PATCH 07/59] core/vm: replace repeated string with variable in tests (#22774) --- core/vm/instructions_test.go | 69 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 13dfa960d1ec..35f3865bf54a 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -41,6 +41,7 @@ type twoOperandParams struct { y string } +var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" var commonParams []*twoOperandParams var twoOpMethods map[string]executionFunc @@ -354,8 +355,8 @@ func BenchmarkOpSub256(b *testing.B) { } func BenchmarkOpMul(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opMul, x, y) } @@ -386,64 +387,64 @@ func BenchmarkOpSdiv(b *testing.B) { } func BenchmarkOpMod(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opMod, x, y) } func BenchmarkOpSmod(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opSmod, x, y) } func BenchmarkOpExp(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opExp, x, y) } func BenchmarkOpSignExtend(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opSignExtend, x, y) } func BenchmarkOpLt(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opLt, x, y) } func BenchmarkOpGt(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opGt, x, y) } func BenchmarkOpSlt(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opSlt, x, y) } func BenchmarkOpSgt(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opSgt, x, y) } func BenchmarkOpEq(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opEq, x, y) } @@ -453,45 +454,45 @@ func BenchmarkOpEq2(b *testing.B) { opBenchmark(b, opEq, x, y) } func BenchmarkOpAnd(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opAnd, x, y) } func BenchmarkOpOr(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opOr, x, y) } func BenchmarkOpXor(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opXor, x, y) } func BenchmarkOpByte(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opByte, x, y) } func BenchmarkOpAddmod(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - z := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup + z := alphabetSoup opBenchmark(b, opAddmod, x, y, z) } func BenchmarkOpMulmod(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - z := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup + z := alphabetSoup opBenchmark(b, opMulmod, x, y, z) } From 9c8d228dda156a10ef6e3b0eb60bf4ce5bc111ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sat, 1 May 2021 13:19:24 +0200 Subject: [PATCH 08/59] core/vm: clean up contract creation error handling (#22766) Do not keep separate flag for "max code size exceeded" case, but instead assign appropriate error for it sooner. --- core/vm/evm.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index e8e5c1ca9419..dfed7f7e226e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -423,13 +423,16 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, ret, err := run(evm, contract, nil, false) - // check whether the max code size has been exceeded - maxCodeSizeExceeded := evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize + // Check whether the max code size has been exceeded, assign err if the case. + if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { + err = ErrMaxCodeSizeExceeded + } + // if the contract creation ran successfully and no errors were returned // calculate the gas required to store the code. If the code could not // be stored due to not enough gas set an error and let it be handled // by the error checking condition below. - if err == nil && !maxCodeSizeExceeded { + if err == nil { createDataGas := uint64(len(ret)) * params.CreateDataGas if contract.UseGas(createDataGas) { evm.StateDB.SetCode(address, ret) @@ -441,21 +444,17 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. - if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) { + if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { contract.UseGas(contract.Gas) } } - // Assign err if contract code size exceeds the max while the err is still empty. - if maxCodeSizeExceeded && err == nil { - err = ErrMaxCodeSizeExceeded - } + if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) } return ret, address, contract.Gas, err - } // Create creates a new contract using code as deployment code. From 7c6908b959fc670ff38af6a35cda3085cbbba44e Mon Sep 17 00:00:00 2001 From: aaronbuchwald Date: Mon, 3 May 2021 04:58:00 -0400 Subject: [PATCH 09/59] core/vm: fix interpreter comments (#22797) * Fix interpreter comment * Fix comment --- core/vm/interpreter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index aa70d7c1020e..abf2178984db 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -142,7 +142,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( defer func() { in.evm.depth-- }() // Make sure the readOnly is only set if we aren't in readOnly yet. - // This makes also sure that the readOnly flag isn't removed for child calls. + // This also makes sure that the readOnly flag isn't removed for child calls. if readOnly && !in.readOnly { in.readOnly = true defer func() { in.readOnly = false }() @@ -218,7 +218,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - // If the operation is valid, enforce and write restrictions + // If the operation is valid, enforce write restrictions if in.readOnly && in.evm.chainRules.IsByzantium { // If the interpreter is operating in readonly mode, make sure no // state-modifying operation is performed. The 3rd stack item From 21bbe5f568ee86a5688a378734a771b97da7a476 Mon Sep 17 00:00:00 2001 From: Evgeny Danilenko <6655321@bk.ru> Date: Thu, 6 May 2021 11:46:27 +0300 Subject: [PATCH 10/59] core/vm: avoid duplicate log in json logger (#22825) --- core/vm/logger_json.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 593626eda2e0..4fbd4fd75f12 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -85,8 +85,9 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, Time time.Duration `json:"time"` Err string `json:"error,omitempty"` } + var errMsg string if err != nil { - return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()}) + errMsg = err.Error() } - return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""}) + return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg}) } From 7a55b9f7882abe14b11132c72f8bfc916c02f0d1 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 22 Feb 2024 19:53:37 +0800 Subject: [PATCH 11/59] core/state: remove unused methods ReturnGas (#23092) --- core/state/state_object.go | 3 --- core/vm/logger_test.go | 1 - eth/tracers/tracer_test.go | 1 - 3 files changed, 5 deletions(-) diff --git a/core/state/state_object.go b/core/state/state_object.go index 53ea774d6194..6d50461f1587 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -306,9 +306,6 @@ func (self *stateObject) setBalance(amount *big.Int) { } } -// Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *stateObject) ReturnGas(gas *big.Int) {} - func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject { stateObject := newObject(db, self.address, self.data, onDirty) if self.trie != nil { diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 9ee02b97c028..f6c15d9cbb0f 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -30,7 +30,6 @@ type dummyContractRef struct { calledForEach bool } -func (dummyContractRef) ReturnGas(*big.Int) {} func (dummyContractRef) Address() common.Address { return common.Address{} } func (dummyContractRef) Value() *big.Int { return new(big.Int) } func (dummyContractRef) SetCode(common.Hash, []byte) {} diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index a7266c178880..577fd1e576c3 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -40,7 +40,6 @@ func (account) SetBalance(*big.Int) {} func (account) SetNonce(uint64) {} func (account) Balance() *big.Int { return nil } func (account) Address() common.Address { return common.Address{} } -func (account) ReturnGas(*big.Int) {} func (account) SetCode(common.Hash, []byte) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} From f05fa009e28dd9f7b8e32d114f9993b9543a7674 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 24 Aug 2021 13:57:05 +0200 Subject: [PATCH 12/59] core/vm: fix typo in comment (#23450) --- core/vm/gas_table.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index f25ba9e4cdfb..c2f8e385c7c6 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -61,7 +61,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { // as argument: // CALLDATACOPY (stack position 2) // CODECOPY (stack position 2) -// EXTCODECOPY (stack poition 3) +// EXTCODECOPY (stack position 3) // RETURNDATACOPY (stack position 2) func memoryCopierGas(stackpos int) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { From dc8cdf3e62b23dfa520743415dbda88f5c3b8043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 30 Aug 2021 14:13:06 +0200 Subject: [PATCH 13/59] core/vm: rework jumpdest analysis benchmarks (#23499) * core/vm: rework jumpdest analysis benchmarks For BenchmarkJumpdestOpAnalysis use fixed code size of ~1.2MB and classic benchmark loop. * core/vm: clear bitvec in jumpdest analysis benchmark --- core/vm/analysis_test.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go index 99e4e386b404..b68794736f61 100644 --- a/core/vm/analysis_test.go +++ b/core/vm/analysis_test.go @@ -55,9 +55,12 @@ func TestJumpDestAnalysis(t *testing.T) { } } +const analysisCodeSize = 1200 * 1024 + func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { // 1.4 ms - code := make([]byte, 1200000) + code := make([]byte, analysisCodeSize) + bench.SetBytes(analysisCodeSize) bench.ResetTimer() for i := 0; i < bench.N; i++ { codeBitmap(code) @@ -66,7 +69,8 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { } func BenchmarkJumpdestHashing_1200k(bench *testing.B) { // 4 ms - code := make([]byte, 1200000) + code := make([]byte, analysisCodeSize) + bench.SetBytes(analysisCodeSize) bench.ResetTimer() for i := 0; i < bench.N; i++ { crypto.Keccak256Hash(code) @@ -77,13 +81,19 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) { func BenchmarkJumpdestOpAnalysis(bench *testing.B) { var op OpCode bencher := func(b *testing.B) { - code := make([]byte, 32*b.N) + code := make([]byte, analysisCodeSize) + b.SetBytes(analysisCodeSize) for i := range code { code[i] = byte(op) } bits := make(bitvec, len(code)/8+1+4) b.ResetTimer() - codeBitmapInternal(code, bits) + for i := 0; i < b.N; i++ { + for j := range bits { + bits[j] = 0 + } + codeBitmapInternal(code, bits) + } } for op = PUSH1; op <= PUSH32; op++ { bench.Run(op.String(), bencher) From f9e14af6fd285956bc15637e4d2b723cf3b56cc9 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 18 Nov 2021 09:50:52 +0100 Subject: [PATCH 14/59] core/vm: don't use iota for opcode definitions (#23928) --- core/vm/opcodes.go | 123 ++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 322e01d17c99..0376e1f27ec3 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -39,68 +39,68 @@ func (op OpCode) IsStaticJump() bool { // 0x0 range - arithmetic ops. const ( - STOP OpCode = iota - ADD - MUL - SUB - DIV - SDIV - MOD - SMOD - ADDMOD - MULMOD - EXP - SIGNEXTEND + STOP OpCode = 0x0 + ADD OpCode = 0x1 + MUL OpCode = 0x2 + SUB OpCode = 0x3 + DIV OpCode = 0x4 + SDIV OpCode = 0x5 + MOD OpCode = 0x6 + SMOD OpCode = 0x7 + ADDMOD OpCode = 0x8 + MULMOD OpCode = 0x9 + EXP OpCode = 0xa + SIGNEXTEND OpCode = 0xb ) // 0x10 range - comparison ops. const ( - LT OpCode = iota + 0x10 - GT - SLT - SGT - EQ - ISZERO - AND - OR - XOR - NOT - BYTE - SHL - SHR - SAR + LT OpCode = 0x10 + GT OpCode = 0x11 + SLT OpCode = 0x12 + SGT OpCode = 0x13 + EQ OpCode = 0x14 + ISZERO OpCode = 0x15 + AND OpCode = 0x16 + OR OpCode = 0x17 + XOR OpCode = 0x18 + NOT OpCode = 0x19 + BYTE OpCode = 0x1a + SHL OpCode = 0x1b + SHR OpCode = 0x1c + SAR OpCode = 0x1d SHA3 OpCode = 0x20 ) // 0x30 range - closure state. const ( - ADDRESS OpCode = 0x30 + iota - BALANCE - ORIGIN - CALLER - CALLVALUE - CALLDATALOAD - CALLDATASIZE - CALLDATACOPY - CODESIZE - CODECOPY - GASPRICE - EXTCODESIZE - EXTCODECOPY - RETURNDATASIZE - RETURNDATACOPY - EXTCODEHASH + ADDRESS OpCode = 0x30 + BALANCE OpCode = 0x31 + ORIGIN OpCode = 0x32 + CALLER OpCode = 0x33 + CALLVALUE OpCode = 0x34 + CALLDATALOAD OpCode = 0x35 + CALLDATASIZE OpCode = 0x36 + CALLDATACOPY OpCode = 0x37 + CODESIZE OpCode = 0x38 + CODECOPY OpCode = 0x39 + GASPRICE OpCode = 0x3a + EXTCODESIZE OpCode = 0x3b + EXTCODECOPY OpCode = 0x3c + RETURNDATASIZE OpCode = 0x3d + RETURNDATACOPY OpCode = 0x3e + EXTCODEHASH OpCode = 0x3f ) // 0x40 range - block operations. const ( - BLOCKHASH OpCode = 0x40 + iota - COINBASE - TIMESTAMP - NUMBER - DIFFICULTY - GASLIMIT + BLOCKHASH OpCode = 0x40 + COINBASE OpCode = 0x41 + TIMESTAMP OpCode = 0x42 + NUMBER OpCode = 0x43 + DIFFICULTY OpCode = 0x44 + GASLIMIT OpCode = 0x45 CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 ) @@ -121,7 +121,7 @@ const ( JUMPDEST ) -// 0x60 range. +// 0x60 range - pushes. const ( PUSH1 OpCode = 0x60 + iota PUSH2 @@ -155,7 +155,11 @@ const ( PUSH30 PUSH31 PUSH32 - DUP1 +) + +// 0x80 range - dups. +const ( + DUP1 = 0x80 + iota DUP2 DUP3 DUP4 @@ -171,7 +175,11 @@ const ( DUP14 DUP15 DUP16 - SWAP1 +) + +// 0x90 range - swaps. +const ( + SWAP1 = 0x90 + iota SWAP2 SWAP3 SWAP4 @@ -207,12 +215,13 @@ const ( // 0xf0 range - closures. const ( - CREATE OpCode = 0xf0 + iota - CALL - CALLCODE - RETURN - DELEGATECALL - CREATE2 + CREATE OpCode = 0xf0 + CALL OpCode = 0xf1 + CALLCODE OpCode = 0xf2 + RETURN OpCode = 0xf3 + DELEGATECALL OpCode = 0xf4 + CREATE2 OpCode = 0xf5 + STATICCALL OpCode = 0xfa REVERT OpCode = 0xfd SELFDESTRUCT OpCode = 0xff From 3d3cc6c6d74323a6f5f7df1f9fad1d157df8932a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 24 Nov 2021 16:02:12 +0100 Subject: [PATCH 15/59] core/vm: use proper JumpTable type (#23967) --- core/vm/interpreter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index abf2178984db..2571429b2972 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -32,7 +32,7 @@ type Config struct { NoRecursion bool // Disables call, callcode, delegate call and create EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - JumpTable [256]*operation // EVM instruction table, automatically populated if unset + JumpTable JumpTable // EVM instruction table, automatically populated if unset EWASMInterpreter string // External EWASM interpreter options EVMInterpreter string // External EVM interpreter options From f7c6b1abd488c3bbe7bc5c085bffe05ee8b64aaa Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 25 Nov 2021 09:37:47 +0100 Subject: [PATCH 16/59] core/vm, core/state/snapshot: remove unused code (#23956) * core/state/snapshot: remove wiper functionality * core/vm: remove unused 'unofficial' opcodes --- core/vm/opcodes.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 0376e1f27ec3..d6d2a9bcd55e 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -206,13 +206,6 @@ const ( LOG4 ) -// unofficial opcodes used for parsing. -const ( - PUSH OpCode = 0xb0 + iota - DUP - SWAP -) - // 0xf0 range - closures. const ( CREATE OpCode = 0xf0 @@ -389,10 +382,6 @@ var opCodeToString = map[OpCode]string{ STATICCALL: "STATICCALL", REVERT: "REVERT", SELFDESTRUCT: "SELFDESTRUCT", - - PUSH: "PUSH", - DUP: "DUP", - SWAP: "SWAP", } func (op OpCode) String() string { From 6415934da9843f87c03cf7b5bee78e6435c1c364 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 25 Nov 2021 20:10:01 +0100 Subject: [PATCH 17/59] core/vm: simplify op lookup in contract (#23974) --- core/vm/contract.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index 5378267dad03..2be61b51d0ce 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -132,16 +132,11 @@ func (c *Contract) AsDelegate() *Contract { // GetOp returns the n'th element in the contract's byte array func (c *Contract) GetOp(n uint64) OpCode { - return OpCode(c.GetByte(n)) -} - -// GetByte returns the n'th byte in the contract's byte array -func (c *Contract) GetByte(n uint64) byte { if n < uint64(len(c.Code)) { - return c.Code[n] + return OpCode(c.Code[n]) } - return 0 + return STOP } // Caller returns the caller of the contract. From 823ec33484165c2ff0247e23607b10b545d29057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 29 Nov 2021 14:46:24 +0100 Subject: [PATCH 18/59] core/vm: simplify error handling in interpreter loop (#23952) * core/vm: break loop on any error * core/vm: move ErrExecutionReverted to opRevert() * core/vm: use "stop token" to stop the loop * core/vm: unconditionally pc++ in the loop * core/vm: set return data in instruction impls --- core/vm/errors.go | 4 ++++ core/vm/instructions.go | 23 +++++++++++++++-------- core/vm/interpreter.go | 25 +++++++++---------------- core/vm/jump_table.go | 19 +------------------ 4 files changed, 29 insertions(+), 42 deletions(-) diff --git a/core/vm/errors.go b/core/vm/errors.go index c813aa36af36..236e22568b58 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -34,6 +34,10 @@ var ( ErrWriteProtection = errors.New("write protection") ErrReturnDataOutOfBounds = errors.New("return data out of bounds") ErrGasUintOverflow = errors.New("gas uint64 overflow") + + // errStopToken is an internal token indicating interpreter loop termination, + // never returned to outside callers. + errStopToken = errors.New("stop token") ) // ErrStackUnderflow wraps an evm error when the items on the stack less diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 92b3b9ae88c0..987fdcb177d4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -526,7 +526,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } - *pc = pos.Uint64() + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop return nil, nil } @@ -536,9 +536,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } - *pc = pos.Uint64() - } else { - *pc++ + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop } return nil, nil } @@ -592,8 +590,10 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] callContext.contract.Gas += returnGas if suberr == ErrExecutionReverted { + interpreter.returnData = res // set REVERT data to return data buffer return res, nil } + interpreter.returnData = nil // clear dirty return data buffer return nil, nil } @@ -623,8 +623,10 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ callContext.contract.Gas += returnGas if suberr == ErrExecutionReverted { + interpreter.returnData = res // set REVERT data to return data buffer return res, nil } + interpreter.returnData = nil // clear dirty return data buffer return nil, nil } @@ -656,6 +658,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } callContext.contract.Gas += returnGas + interpreter.returnData = ret return ret, nil } @@ -687,6 +690,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( } callContext.contract.Gas += returnGas + interpreter.returnData = ret return ret, nil } @@ -715,6 +719,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt } callContext.contract.Gas += returnGas + interpreter.returnData = ret return ret, nil } @@ -743,6 +748,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) } callContext.contract.Gas += returnGas + interpreter.returnData = ret return ret, nil } @@ -750,18 +756,19 @@ func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] offset, size := callContext.stack.pop(), callContext.stack.pop() ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - return ret, nil + return ret, errStopToken } func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { offset, size := callContext.stack.pop(), callContext.stack.pop() ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - return ret, nil + interpreter.returnData = ret + return ret, ErrExecutionReverted } func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - return nil, nil + return nil, errStopToken } func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { @@ -769,7 +776,7 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) interpreter.evm.StateDB.Suicide(callContext.contract.Address()) - return nil, nil + return nil, errStopToken } // following functions are used by the instruction jump table diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 2571429b2972..5d094c4eea40 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -273,24 +273,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // execute the operation res, err = operation.execute(&pc, in, callContext) - // if the operation clears the return data (e.g. it has returning data) - // set the last return to the result of the operation. - if operation.returns { - in.returnData = res - } - switch { - case err != nil: - return nil, err - case operation.reverts: - log.Debug("ErrExecutionReverted", "pc", pc, "reverts", operation.reverts, "err", err) - return res, ErrExecutionReverted - case operation.halts: - return res, nil - case !operation.jumps: - pc++ + if err != nil { + break } + pc++ } - return nil, nil + + if err == errStopToken { + err = nil // clear stop token error + } + + return res, err } // CanRun tells if the contract, passed as an argument, can be diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 7580544ba2ca..f56dc89d5655 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -41,11 +41,7 @@ type operation struct { // memorySize returns the memory size required for the operation memorySize memorySizeFunc - halts bool // indicates whether the operation should halt further execution - jumps bool // indicates whether the program counter should not increment - writes bool // determines whether this a state modifying operation - reverts bool // determines whether the operation reverts state (implicitly halts) - returns bool // determines whether the operations sets the return data content + writes bool // determines whether this a state modifying operation } var ( @@ -109,7 +105,6 @@ func newConstantinopleInstructionSet() JumpTable { maxStack: maxStack(4, 1), memorySize: memoryCreate2, writes: true, - returns: true, } return instructionSet } @@ -125,7 +120,6 @@ func newByzantiumInstructionSet() JumpTable { minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryStaticCall, - returns: true, } instructionSet[RETURNDATASIZE] = &operation{ execute: opReturnDataSize, @@ -147,8 +141,6 @@ func newByzantiumInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryRevert, - reverts: true, - returns: true, } return instructionSet } @@ -185,7 +177,6 @@ func newHomesteadInstructionSet() JumpTable { minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryDelegateCall, - returns: true, } return instructionSet } @@ -199,7 +190,6 @@ func newFrontierInstructionSet() JumpTable { constantGas: 0, minStack: minStack(0, 0), maxStack: maxStack(0, 0), - halts: true, }, ADD: { execute: opAdd, @@ -509,14 +499,12 @@ func newFrontierInstructionSet() JumpTable { constantGas: GasMidStep, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - jumps: true, }, JUMPI: { execute: opJumpi, constantGas: GasSlowStep, minStack: minStack(2, 0), maxStack: maxStack(2, 0), - jumps: true, }, PC: { execute: opPc, @@ -974,7 +962,6 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(3, 1), memorySize: memoryCreate, writes: true, - returns: true, }, CALL: { execute: opCall, @@ -983,7 +970,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - returns: true, }, CALLCODE: { execute: opCallCode, @@ -992,7 +978,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - returns: true, }, RETURN: { execute: opReturn, @@ -1000,14 +985,12 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryReturn, - halts: true, }, SELFDESTRUCT: { execute: opSuicide, dynamicGas: gasSelfdestruct, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - halts: true, writes: true, }, } From c097e565fd8cb9a03b3deb0ed1779bf27424e14a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 30 Nov 2021 09:34:34 +0000 Subject: [PATCH 19/59] core/vm: rename SHA3 instruction to KECCAK256 (#23976) This was proposed in 2016, Solidity uses this since 2017, and evmone and other VMs use the keccak256 name. This brings geth in line with those. --- core/vm/evm.go | 2 +- core/vm/gas_table.go | 6 +++--- core/vm/instructions.go | 2 +- core/vm/instructions_test.go | 4 ++-- core/vm/jump_table.go | 10 +++++----- core/vm/memory_table.go | 2 +- core/vm/opcodes.go | 6 +++--- params/protocol_params.go | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index dfed7f7e226e..413c74874ec9 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -465,7 +465,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // Create2 creates a new contract using code as deployment code. // -// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:] +// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { codeAndHash := &codeAndHash{code: code} diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index c2f8e385c7c6..45589157bb00 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -248,7 +248,7 @@ func makeGasLog(n uint64) gasFunc { } } -func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { gas, err := memoryGasCost(mem, memorySize) if err != nil { return 0, err @@ -257,7 +257,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if overflow { return 0, ErrGasUintOverflow } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { return 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, wordGas); overflow { @@ -291,7 +291,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS if overflow { return 0, ErrGasUintOverflow } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { return 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, wordGas); overflow { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 987fdcb177d4..4f181a6e6950 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -231,7 +231,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt return nil, nil } -func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { offset, size := callContext.stack.pop(), callContext.stack.peek() data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 35f3865bf54a..88107a69c40d 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -565,7 +565,7 @@ func BenchmarkOpMstore(bench *testing.B) { } } -func BenchmarkOpSHA3(bench *testing.B) { +func BenchmarkOpKeccak256(bench *testing.B) { var ( env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() @@ -580,7 +580,7 @@ func BenchmarkOpSHA3(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { stack.pushN(*uint256.NewInt(32), *start) - opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil}) + opKeccak256(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index f56dc89d5655..a5b5445d804a 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -323,13 +323,13 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, - SHA3: { - execute: opSha3, - constantGas: params.Sha3Gas, - dynamicGas: gasSha3, + KECCAK256: { + execute: opKeccak256, + constantGas: params.Keccak256Gas, + dynamicGas: gasKeccak256, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - memorySize: memorySha3, + memorySize: memoryKeccak256, }, ADDRESS: { execute: opAddress, diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 4fcb41442c4e..e35ca84e0efa 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -16,7 +16,7 @@ package vm -func memorySha3(stack *Stack) (uint64, bool) { +func memoryKeccak256(stack *Stack) (uint64, bool) { return calcMemSize64(stack.Back(0), stack.Back(1)) } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index d6d2a9bcd55e..224c8789210b 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -70,7 +70,7 @@ const ( SHR OpCode = 0x1c SAR OpCode = 0x1d - SHA3 OpCode = 0x20 + KECCAK256 OpCode = 0x20 ) // 0x30 range - closure state. @@ -253,7 +253,7 @@ var opCodeToString = map[OpCode]string{ MULMOD: "MULMOD", // 0x20 range - crypto. - SHA3: "SHA3", + KECCAK256: "KECCAK256", // 0x30 range - closure state. ADDRESS: "ADDRESS", @@ -420,7 +420,7 @@ var stringToOp = map[string]OpCode{ "SAR": SAR, "ADDMOD": ADDMOD, "MULMOD": MULMOD, - "SHA3": SHA3, + "KECCAK256": KECCAK256, "ADDRESS": ADDRESS, "BALANCE": BALANCE, "ORIGIN": ORIGIN, diff --git a/params/protocol_params.go b/params/protocol_params.go index d9272981d63e..f15530649750 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -41,8 +41,8 @@ const ( LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. CallStipend uint64 = 2300 // Free gas given at beginning of call. - Sha3Gas uint64 = 30 // Once per SHA3 operation. - Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. + Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. + Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. From 002be52ae9622ec7fa33be50cb70ccd4ee940cfc Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 30 Nov 2021 13:21:40 +0100 Subject: [PATCH 20/59] core/vm: don't copy JumpTable when no EIP mods are needed (#23977) --- core/vm/interpreter.go | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 5d094c4eea40..582957262b9f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -32,7 +32,7 @@ type Config struct { NoRecursion bool // Disables call, callcode, delegate call and create EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - JumpTable JumpTable // EVM instruction table, automatically populated if unset + JumpTable *JumpTable // EVM instruction table, automatically populated if unset EWASMInterpreter string // External EWASM interpreter options EVMInterpreter string // External EVM interpreter options @@ -92,35 +92,33 @@ type EVMInterpreter struct { // NewEVMInterpreter returns a new instance of the Interpreter. func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { - // We use the STOP instruction whether to see - // the jump table was initialised. If it was not - // we'll set the default jump table. - if cfg.JumpTable[STOP] == nil { - var jt JumpTable + // If jump table was not initialised we set the default one. + if cfg.JumpTable == nil { switch { case evm.chainRules.IsIstanbul: - jt = istanbulInstructionSet + cfg.JumpTable = &istanbulInstructionSet case evm.chainRules.IsConstantinople: - jt = constantinopleInstructionSet + cfg.JumpTable = &constantinopleInstructionSet case evm.chainRules.IsByzantium: - jt = byzantiumInstructionSet + cfg.JumpTable = &byzantiumInstructionSet case evm.chainRules.IsEIP158: - jt = spuriousDragonInstructionSet + cfg.JumpTable = &spuriousDragonInstructionSet case evm.chainRules.IsEIP150: - jt = tangerineWhistleInstructionSet + cfg.JumpTable = &tangerineWhistleInstructionSet case evm.chainRules.IsHomestead: - jt = homesteadInstructionSet + cfg.JumpTable = &homesteadInstructionSet default: - jt = frontierInstructionSet + cfg.JumpTable = &frontierInstructionSet } for i, eip := range cfg.ExtraEips { - if err := EnableEIP(eip, &jt); err != nil { + copy := *cfg.JumpTable + if err := EnableEIP(eip, ©); err != nil { // Disable it, so caller can check if it's activated or not cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...) log.Error("EIP activation failed", "eip", eip, "error", err) } + cfg.JumpTable = © } - cfg.JumpTable = jt } return &EVMInterpreter{ From bfbb6783098c69defb95b78b28ec22239a1fdcf2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 1 Dec 2021 09:21:21 +0000 Subject: [PATCH 21/59] core/vm: move interpreter.ReadOnly check into the opcode implementations (#23970) * core/vm: Move interpreter.ReadOnly check into the opcode implementations Also remove the same check from the interpreter inner loop. * core/vm: Remove obsolete operation.writes flag * core/vm: Capture fault states in logger Co-authored-by: Martin Holst Swende * core/vm: Remove panic added for testing Co-authored-by: Martin Holst Swende --- core/vm/instructions.go | 18 ++++++++++++++++++ core/vm/interpreter.go | 11 ----------- core/vm/jump_table.go | 11 ----------- core/vm/logger_json.go | 2 +- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 4f181a6e6950..ccc3e383b298 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -514,6 +514,9 @@ func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b } func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } loc := callContext.stack.pop() val := callContext.stack.pop() interpreter.evm.StateDB.SetState(callContext.contract.Address(), @@ -561,6 +564,9 @@ func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt } func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } var ( value = callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop() @@ -598,6 +604,9 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] } func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } var ( endowment = callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop() @@ -642,6 +651,9 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by // Get the arguments from the memory. args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + if interpreter.readOnly && !value.IsZero() { + return nil, ErrWriteProtection + } if !value.IsZero() { gas += params.CallStipend } @@ -772,6 +784,9 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } beneficiary := callContext.stack.pop() balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) @@ -784,6 +799,9 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ // make log instruction function func makeLog(size int) executionFunc { return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } topics := make([]common.Hash, size) stack := callContext.stack mStart, mSize := stack.pop(), stack.pop() diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 582957262b9f..eabf1dce19ef 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -216,17 +216,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - // If the operation is valid, enforce write restrictions - if in.readOnly && in.evm.chainRules.IsByzantium { - // If the interpreter is operating in readonly mode, make sure no - // state-modifying operation is performed. The 3rd stack item - // for a call operation is the value. Transferring value from one - // account to the others means the state is modified and should also - // return with an error. - if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) { - return nil, ErrWriteProtection - } - } // Static portion of gas cost = operation.constantGas // For tracing if !contract.UseGas(operation.constantGas) { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index a5b5445d804a..c1b9fd8470b6 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -40,8 +40,6 @@ type operation struct { // memorySize returns the memory size required for the operation memorySize memorySizeFunc - - writes bool // determines whether this a state modifying operation } var ( @@ -104,7 +102,6 @@ func newConstantinopleInstructionSet() JumpTable { minStack: minStack(4, 1), maxStack: maxStack(4, 1), memorySize: memoryCreate2, - writes: true, } return instructionSet } @@ -492,7 +489,6 @@ func newFrontierInstructionSet() JumpTable { dynamicGas: gasSStore, minStack: minStack(2, 0), maxStack: maxStack(2, 0), - writes: true, }, JUMP: { execute: opJump, @@ -920,7 +916,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryLog, - writes: true, }, LOG1: { execute: makeLog(1), @@ -928,7 +923,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryLog, - writes: true, }, LOG2: { execute: makeLog(2), @@ -936,7 +930,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(4, 0), maxStack: maxStack(4, 0), memorySize: memoryLog, - writes: true, }, LOG3: { execute: makeLog(3), @@ -944,7 +937,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(5, 0), maxStack: maxStack(5, 0), memorySize: memoryLog, - writes: true, }, LOG4: { execute: makeLog(4), @@ -952,7 +944,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(6, 0), maxStack: maxStack(6, 0), memorySize: memoryLog, - writes: true, }, CREATE: { execute: opCreate, @@ -961,7 +952,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 1), maxStack: maxStack(3, 1), memorySize: memoryCreate, - writes: true, }, CALL: { execute: opCall, @@ -991,7 +981,6 @@ func newFrontierInstructionSet() JumpTable { dynamicGas: gasSelfdestruct, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - writes: true, }, } } diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 4fbd4fd75f12..4a63120a3df4 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -74,7 +74,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint // CaptureFault outputs state information on the logger. func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { - return nil + return l.CaptureState(env, pc, op, gas, cost, memory, stack, contract, depth, err) } // CaptureEnd is triggered at end of execution. From b1c03864e60a830f342fab9071997b3173afdc9b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 1 Dec 2021 09:33:29 +0000 Subject: [PATCH 22/59] core/vm: rename opSuicide to opSelfdestruct (#24022) The opcode was renamed in the codebase in 2017, but the functions were kept unchanged. --- core/vm/instructions.go | 2 +- core/vm/jump_table.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index ccc3e383b298..3a361eb0e8c4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -783,7 +783,7 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by return nil, errStopToken } -func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index c1b9fd8470b6..1b6f6d1d4559 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -977,7 +977,7 @@ func newFrontierInstructionSet() JumpTable { memorySize: memoryReturn, }, SELFDESTRUCT: { - execute: opSuicide, + execute: opSelfdestruct, dynamicGas: gasSelfdestruct, minStack: minStack(1, 0), maxStack: maxStack(1, 0), From ae267d3da7cb4442bc6ed67129265716ab715431 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 3 Dec 2021 09:16:49 +0000 Subject: [PATCH 23/59] core/vm: remove stack.pushN (#24040) --- core/vm/instructions_test.go | 12 ++++++++---- core/vm/stack.go | 4 ---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 88107a69c40d..1f63d2b040a1 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -532,12 +532,14 @@ func TestOpMstore(t *testing.T) { mem.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" - stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) + stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v))) + stack.push(new(uint256.Int)) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } - stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) + stack.push(new(uint256.Int).SetUint64(0x1)) + stack.push(new(uint256.Int)) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") @@ -560,7 +562,8 @@ func BenchmarkOpMstore(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(*value, *memStart) + stack.push(value) + stack.push(memStart) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } } @@ -579,7 +582,8 @@ func BenchmarkOpKeccak256(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(*uint256.NewInt(32), *start) + stack.push(uint256.NewInt(32)) + stack.push(start) opKeccak256(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } } diff --git a/core/vm/stack.go b/core/vm/stack.go index 2367da1d7c97..cc6037f23b3c 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -42,10 +42,6 @@ func (st *Stack) push(d *uint256.Int) { // NOTE push limit (1024) is checked in baseCheck st.data = append(st.data, *d) } -func (st *Stack) pushN(ds ...uint256.Int) { - // FIXME: Is there a way to pass args by pointers. - st.data = append(st.data, ds...) -} func (st *Stack) pop() (ret uint256.Int) { ret = st.data[len(st.data)-1] From 7d3c783bb9db041447b2ce9ef60de722f62f7cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 3 Dec 2021 11:04:54 +0100 Subject: [PATCH 24/59] core/vm: fill gaps in jump table with opUndefined (#24031) --- core/vm/instructions.go | 4 ++++ core/vm/interpreter.go | 3 --- core/vm/jump_table.go | 11 ++++++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 3a361eb0e8c4..5dcf90b77094 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -779,6 +779,10 @@ func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] return ret, ErrExecutionReverted } +func opUndefined(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + return nil, &ErrInvalidOpCode{opcode: OpCode(callContext.contract.Code[*pc])} +} + func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { return nil, errStopToken } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index eabf1dce19ef..51551678db5d 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -207,9 +207,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // enough stack items available to perform the operation. op = contract.GetOp(pc) operation := in.cfg.JumpTable[op] - if operation == nil { - return nil, &ErrInvalidOpCode{opcode: op} - } // Validate stack if sLen := stack.len(); sLen < operation.minStack { return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 1b6f6d1d4559..55ad78adc53f 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -181,7 +181,7 @@ func newHomesteadInstructionSet() JumpTable { // newFrontierInstructionSet returns the frontier instructions // that can be executed during the frontier phase. func newFrontierInstructionSet() JumpTable { - return JumpTable{ + tbl := JumpTable{ STOP: { execute: opStop, constantGas: 0, @@ -983,4 +983,13 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(1, 0), }, } + + // Fill all unassigned slots with opUndefined. + for i, entry := range tbl { + if entry == nil { + tbl[i] = &operation{execute: opUndefined, maxStack: maxStack(0, 0)} + } + } + + return tbl } From 4c27910a7706fa74b7cfef20e84f4d9c0647beeb Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Fri, 3 Dec 2021 11:10:26 +0100 Subject: [PATCH 25/59] core/vm: move interpreter interruption check to jump instructions (#24026) * core/vm: Remove interpreter loop interruption check * core/vm: Unit test for interpreter loop interruption * core/vm: Check for interpreter loop abort on every jump --- core/vm/instructions.go | 8 ++++++++ core/vm/interpreter.go | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 5dcf90b77094..73173b903981 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -17,6 +17,8 @@ package vm import ( + "sync/atomic" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/params" @@ -525,6 +527,9 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] } func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if atomic.LoadInt32(&interpreter.evm.abort) != 0 { + return nil, errStopToken + } pos := callContext.stack.pop() if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump @@ -534,6 +539,9 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if atomic.LoadInt32(&interpreter.evm.abort) != 0 { + return nil, errStopToken + } pos, cond := callContext.stack.pop(), callContext.stack.pop() if !cond.IsZero() { if !callContext.contract.validJumpdest(&pos) { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 51551678db5d..40a814bfd4f2 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -18,7 +18,6 @@ package vm import ( "hash" - "sync/atomic" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" @@ -192,12 +191,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during // the execution of one of the operations or until the done flag is set by the // parent context. - steps := 0 for { - steps++ - if steps%1000 == 0 && atomic.LoadInt32(&in.evm.abort) != 0 { - break - } if in.cfg.Debug { // Capture pre-execution values for tracing. logged, pcCopy, gasCopy = false, pc, contract.Gas From fbc1cc11ba3656cdd217ab2ffef532d93128b783 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 6 Dec 2021 09:55:50 +0100 Subject: [PATCH 26/59] core/vm: remove no-recursion option from config (24066) --- core/vm/evm.go | 16 ---------------- core/vm/interpreter.go | 1 - tests/vm_test_util.go | 3 +-- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 413c74874ec9..284ea6768c96 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -189,9 +189,6 @@ func (evm *EVM) Interpreter() Interpreter { // the necessary steps to create accounts and reverses the state in case of an // execution error or failed value transfer. func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil - } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -263,9 +260,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // CallCode differs from Call in the sense that it executes the given address' // code with the caller as context. func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil - } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -302,9 +296,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // DelegateCall differs from CallCode in the sense that it executes the given address' // code with the caller as context and the caller is set to the caller of the caller. func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil - } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -332,9 +323,6 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Opcodes that attempt to perform such modifications will result in exceptions // instead of performing the modifications. func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil - } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -412,10 +400,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contract := NewContract(caller, AccountRef(address), value, gas) contract.SetCodeOptionalHash(&address, codeAndHash) - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, address, gas, nil - } - if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 40a814bfd4f2..91bb8dcfaa25 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -28,7 +28,6 @@ import ( type Config struct { Debug bool // Enables debugging Tracer Tracer // Opcode logger - NoRecursion bool // Disables call, callcode, delegate call and create EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages JumpTable *JumpTable // EVM instruction table, automatically populated if unset diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 509022bf5149..bd516a182721 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -20,13 +20,13 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "math/big" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -143,7 +143,6 @@ func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM { Difficulty: t.json.Env.Difficulty, GasPrice: t.json.Exec.GasPrice, } - vmconfig.NoRecursion = true return vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vmconfig) } From 2ce30010bc3fa3b890a8b8ab673c77583cbc985b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 9 Dec 2021 12:55:06 +0000 Subject: [PATCH 27/59] core/vm: remove unused code (IsStaticJump) (#24085) --- core/vm/opcodes.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 224c8789210b..b39232b78abe 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -32,11 +32,6 @@ func (op OpCode) IsPush() bool { return false } -// IsStaticJump specifies if an opcode is JUMP. -func (op OpCode) IsStaticJump() bool { - return op == JUMP -} - // 0x0 range - arithmetic ops. const ( STOP OpCode = 0x0 From 6e9248629611ecea4e9d452bc8de3532caf74914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 17 Dec 2021 10:32:00 +0100 Subject: [PATCH 28/59] core/vm: reverse bit order in bytes of code bitmap (#24120) * core/vm: reverse bit order in bytes of code bitmap This bit order is more natural for bit manipulation operations and we can eliminate some small number of CPU instructions. * core/vm: drop lookup table --- core/vm/analysis.go | 32 +++++++++++++------------------- core/vm/analysis_test.go | 40 ++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/core/vm/analysis.go b/core/vm/analysis.go index 449cded2a896..3733bab6a7c0 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -17,12 +17,12 @@ package vm const ( - set2BitsMask = uint16(0b1100_0000_0000_0000) - set3BitsMask = uint16(0b1110_0000_0000_0000) - set4BitsMask = uint16(0b1111_0000_0000_0000) - set5BitsMask = uint16(0b1111_1000_0000_0000) - set6BitsMask = uint16(0b1111_1100_0000_0000) - set7BitsMask = uint16(0b1111_1110_0000_0000) + set2BitsMask = uint16(0b11) + set3BitsMask = uint16(0b111) + set4BitsMask = uint16(0b1111) + set5BitsMask = uint16(0b1_1111) + set6BitsMask = uint16(0b11_1111) + set7BitsMask = uint16(0b111_1111) ) // bitvec is a bit vector which maps bytes in a program. @@ -30,32 +30,26 @@ const ( // it's data (i.e. argument of PUSHxx). type bitvec []byte -var lookup = [8]byte{ - 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, -} - func (bits bitvec) set1(pos uint64) { - bits[pos/8] |= lookup[pos%8] + bits[pos/8] |= 1 << (pos % 8) } func (bits bitvec) setN(flag uint16, pos uint64) { - a := flag >> (pos % 8) - bits[pos/8] |= byte(a >> 8) - if b := byte(a); b != 0 { - // If the bit-setting affects the neighbouring byte, we can assign - no need to OR it, - // since it's the first write to that byte + a := flag << (pos % 8) + bits[pos/8] |= byte(a) + if b := byte(a >> 8); b != 0 { bits[pos/8+1] = b } } func (bits bitvec) set8(pos uint64) { - a := byte(0xFF >> (pos % 8)) + a := byte(0xFF << (pos % 8)) bits[pos/8] |= a bits[pos/8+1] = ^a } func (bits bitvec) set16(pos uint64) { - a := byte(0xFF >> (pos % 8)) + a := byte(0xFF << (pos % 8)) bits[pos/8] |= a bits[pos/8+1] = 0xFF bits[pos/8+2] = ^a @@ -63,7 +57,7 @@ func (bits bitvec) set16(pos uint64) { // codeSegment checks if the position is in a code segment. func (bits *bitvec) codeSegment(pos uint64) bool { - return ((*bits)[pos/8] & (0x80 >> (pos % 8))) == 0 + return (((*bits)[pos/8] >> (pos % 8)) & 1) == 0 } // codeBitmap collects data locations in code. diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go index b68794736f61..20674bf919d3 100644 --- a/core/vm/analysis_test.go +++ b/core/vm/analysis_test.go @@ -17,6 +17,7 @@ package vm import ( + "math/bits" "testing" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -28,24 +29,27 @@ func TestJumpDestAnalysis(t *testing.T) { exp byte which int }{ - {[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0x40, 0}, - {[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0x50, 0}, - {[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, 0x7F, 0}, - {[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 1}, - {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0x03, 0}, - {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1}, - {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x74, 0}, - {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1}, - {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x3F, 0}, - {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xC0, 1}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x7F, 0}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xFF, 1}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 2}, - {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0x7f, 0}, - {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0xA0, 1}, - {[]byte{byte(PUSH32)}, 0x7F, 0}, - {[]byte{byte(PUSH32)}, 0xFF, 1}, - {[]byte{byte(PUSH32)}, 0xFF, 2}, + {[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0b0000_0010, 0}, + {[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0b0000_1010, 0}, + {[]byte{0x00, byte(PUSH1), 0x00, byte(PUSH1), 0x00, byte(PUSH1), 0x00, byte(PUSH1)}, 0b0101_0100, 0}, + {[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, bits.Reverse8(0x7F), 0}, + {[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 1}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0b1100_0000, 0}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1}, + {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0010_1110, 0}, + {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1}, + {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1100, 0}, + {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0011, 1}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1110, 0}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1111, 1}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 2}, + {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0b1111_1110, 0}, + {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0b0000_0101, 1}, + {[]byte{byte(PUSH32)}, 0b1111_1110, 0}, + {[]byte{byte(PUSH32)}, 0b1111_1111, 1}, + {[]byte{byte(PUSH32)}, 0b1111_1111, 2}, + {[]byte{byte(PUSH32)}, 0b1111_1111, 3}, + {[]byte{byte(PUSH32)}, 0b0000_0001, 4}, } for i, test := range tests { ret := codeBitmap(test.code) From b022ba2c5ddae2b825b60ec017377afd76c22048 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Fri, 17 Dec 2021 13:44:05 +0100 Subject: [PATCH 29/59] core/vm: make INVALID a defined opcode (#24017) * core/vm: Define 0xfe opcode as INVALID * core/vm: Remove opInvalid as opUndefined handles it Co-authored-by: Alex Beregszaszi --- core/vm/opcodes.go | 3 +++ eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index b39232b78abe..b861a62d6de0 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -212,6 +212,7 @@ const ( STATICCALL OpCode = 0xfa REVERT OpCode = 0xfd + INVALID OpCode = 0xfe SELFDESTRUCT OpCode = 0xff ) @@ -376,6 +377,7 @@ var opCodeToString = map[OpCode]string{ CREATE2: "CREATE2", STATICCALL: "STATICCALL", REVERT: "REVERT", + INVALID: "INVALID", SELFDESTRUCT: "SELFDESTRUCT", } @@ -529,6 +531,7 @@ var stringToOp = map[string]OpCode{ "RETURN": RETURN, "CALLCODE": CALLCODE, "REVERT": REVERT, + "INVALID": INVALID, "SELFDESTRUCT": SELFDESTRUCT, } diff --git a/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json b/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json index 7627c8c23d68..ec2ceb426fda 100644 --- a/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json +++ b/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json @@ -59,7 +59,7 @@ "result": { "calls": [ { - "error": "invalid opcode: opcode 0xfe not defined", + "error": "invalid opcode: INVALID", "from": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76", "gas": "0x75fe3", "gasUsed": "0x75fe3", From ac35f0ecec87eb15ffba104b413da5291a7f8909 Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Wed, 11 May 2022 13:03:35 +0800 Subject: [PATCH 30/59] core/vm: clean up some dead functions (#24851) --- core/vm/contracts.go | 2 +- core/vm/memory.go | 19 +------------------ core/vm/stack.go | 15 --------------- 3 files changed, 2 insertions(+), 34 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 28394d4dd23c..b07cce9fc29b 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -521,7 +521,7 @@ func (c *blake2F) Run(input []byte) ([]byte, error) { // Parse the input into the Blake2b call parameters var ( rounds = binary.BigEndian.Uint32(input[0:4]) - final = (input[212] == blake2FFinalBlockBytes) + final = input[212] == blake2FFinalBlockBytes h [8]uint64 m [16]uint64 diff --git a/core/vm/memory.go b/core/vm/memory.go index ba5f8485dc95..7db2308187bf 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -17,8 +17,6 @@ package vm import ( - "fmt" - "github.com/holiman/uint256" ) @@ -68,7 +66,7 @@ func (m *Memory) Resize(size uint64) { } } -// Get returns offset + size as a new slice +// GetCopy returns offset + size as a new slice func (m *Memory) GetCopy(offset, size int64) (cpy []byte) { if size == 0 { return nil @@ -106,18 +104,3 @@ func (m *Memory) Len() int { func (m *Memory) Data() []byte { return m.store } - -// Print dumps the content of the memory. -func (m *Memory) Print() { - fmt.Printf("### mem %d bytes ###\n", len(m.store)) - if len(m.store) > 0 { - addr := 0 - for i := 0; i+32 <= len(m.store); i += 32 { - fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) - addr++ - } - } else { - fmt.Println("-- empty --") - } - fmt.Println("####################") -} diff --git a/core/vm/stack.go b/core/vm/stack.go index cc6037f23b3c..a389c04b725d 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -17,8 +17,6 @@ package vm import ( - "fmt" - "github.com/holiman/uint256" ) @@ -69,16 +67,3 @@ func (st *Stack) peek() *uint256.Int { func (st *Stack) Back(n int) *uint256.Int { return &st.data[st.len()-n-1] } - -// Print dumps the content of the stack -func (st *Stack) Print() { - fmt.Println("### stack ###") - if len(st.data) > 0 { - for i, val := range st.data { - fmt.Printf("%-3d %v\n", i, val) - } - } else { - fmt.Println("-- empty --") - } - fmt.Println("#############") -} From 62b62da730204d458cfeb0f7c76202274250ac1a Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Wed, 11 May 2022 13:04:16 +0800 Subject: [PATCH 31/59] core/vm: separate opcode group for 0x20 range (#24850) --- core/vm/opcodes.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index b861a62d6de0..0917b252ae8b 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -64,7 +64,10 @@ const ( SHL OpCode = 0x1b SHR OpCode = 0x1c SAR OpCode = 0x1d +) +// 0x20 range - crypto. +const ( KECCAK256 OpCode = 0x20 ) From 032b98e942e58ce926f185ad9a7f17f59f7d74a3 Mon Sep 17 00:00:00 2001 From: Qian Bin Date: Wed, 11 May 2022 17:00:29 +0800 Subject: [PATCH 32/59] core/vm: optimize Memory.Set32 (#24847) * core/vm: remove unnecessary memset for Memory.Set32 * core/vm: optimize Memory.Set32 --- core/vm/memory.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/vm/memory.go b/core/vm/memory.go index 7db2308187bf..35b729996075 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -53,10 +53,9 @@ func (m *Memory) Set32(offset uint64, val *uint256.Int) { if offset+32 > uint64(len(m.store)) { panic("invalid memory: store empty") } - // Zero the memory area - copy(m.store[offset:offset+32], []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, 0}) // Fill in relevant bits - val.WriteToSlice(m.store[offset:]) + b32 := val.Bytes32() + copy(m.store[offset:], b32[:]) } // Resize resizes the memory to size From 8b6db6d87a94edcfd844e77cad65bc897cb0d2a9 Mon Sep 17 00:00:00 2001 From: Leon <316032931@qq.com> Date: Mon, 26 Sep 2022 19:20:38 +0800 Subject: [PATCH 33/59] core/vm: better handle error on eip activation check (#25131) * core/vm: correct logic for eip check of NewEVMInterpreter * refactor --- core/vm/interpreter.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 91bb8dcfaa25..59fbf21f7c5a 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -108,15 +108,18 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { default: cfg.JumpTable = &frontierInstructionSet } - for i, eip := range cfg.ExtraEips { + var extraEips []int + for _, eip := range cfg.ExtraEips { copy := *cfg.JumpTable if err := EnableEIP(eip, ©); err != nil { // Disable it, so caller can check if it's activated or not - cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...) log.Error("EIP activation failed", "eip", eip, "error", err) + } else { + extraEips = append(extraEips, eip) } cfg.JumpTable = © } + cfg.ExtraEips = extraEips } return &EVMInterpreter{ From b36678f7adf6b93d7416e89ae2b4f92a397370f8 Mon Sep 17 00:00:00 2001 From: lmittmann <3458786+lmittmann@users.noreply.github.com> Date: Thu, 2 Nov 2023 07:54:28 +0100 Subject: [PATCH 34/59] core/vm: performance tweak of `OpCode.String()` (#28453) make `opCodeToString` a `[256]string` array Co-authored-by: lmittmann --- core/vm/opcodes.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 0917b252ae8b..b9001138b05a 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -219,8 +219,7 @@ const ( SELFDESTRUCT OpCode = 0xff ) -// Since the opcodes aren't all in order we can't use a regular slice. -var opCodeToString = map[OpCode]string{ +var opCodeToString = [256]string{ // 0x0 range - arithmetic ops. STOP: "STOP", ADD: "ADD", @@ -385,12 +384,11 @@ var opCodeToString = map[OpCode]string{ } func (op OpCode) String() string { - str := opCodeToString[op] - if len(str) == 0 { - return fmt.Sprintf("opcode 0x%x not defined", int(op)) + if s := opCodeToString[op]; s != "" { + return s } - return str + return fmt.Sprintf("opcode %#x not defined", int(op)) } var stringToOp = map[string]OpCode{ From 928a0691ca409c17debe1d509e10bda6ff0edc57 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 29 Feb 2024 21:10:25 +0800 Subject: [PATCH 35/59] implement EIP-3198 --- common/constants.go | 2 ++ common/constants/constants.go.devnet | 2 ++ common/constants/constants.go.testnet | 2 ++ core/vm/eips.go | 22 ++++++++++++++++++++++ core/vm/evm.go | 12 +++++++----- core/vm/interpreter.go | 4 ++++ core/vm/jump_table.go | 19 +++++++++++++++++++ core/vm/opcodes.go | 3 +++ params/config.go | 26 +++++++++++++++++++++++++- 9 files changed, 86 insertions(+), 6 deletions(-) diff --git a/common/constants.go b/common/constants.go index 71b1093cf05e..7020e8fd682b 100644 --- a/common/constants.go +++ b/common/constants.go @@ -45,6 +45,8 @@ var TIPXDCX = big.NewInt(38383838) var TIPXDCXLending = big.NewInt(38383838) var TIPXDCXCancellationFee = big.NewInt(38383838) var TIPXDCXCancellationFeeTestnet = big.NewInt(38383838) +var BerlinBlock = big.NewInt(9999999999) +var LondonBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(38383838) var IsTestnet bool = false diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index c121d305239f..58351e020bf0 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -45,6 +45,8 @@ var TIPXDCX = big.NewInt(225000) var TIPXDCXLending = big.NewInt(225000) var TIPXDCXCancellationFee = big.NewInt(225000) var TIPXDCXCancellationFeeTestnet = big.NewInt(225000) +var BerlinBlock = big.NewInt(9999999999) +var LondonBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(0) var IsTestnet bool = false diff --git a/common/constants/constants.go.testnet b/common/constants/constants.go.testnet index 0c1db046cfb8..eae07aa11647 100644 --- a/common/constants/constants.go.testnet +++ b/common/constants/constants.go.testnet @@ -45,6 +45,8 @@ var TIPXDCX = big.NewInt(23779191) var TIPXDCXLending = big.NewInt(23779191) var TIPXDCXCancellationFee = big.NewInt(23779191) var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191) +var BerlinBlock = big.NewInt(9999999999) +var LondonBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(23779191) var IsTestnet bool = false diff --git a/core/vm/eips.go b/core/vm/eips.go index 6ba7bbd6ec7a..77dc30a9100d 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -19,6 +19,7 @@ package vm import ( "fmt" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/params" "github.com/holiman/uint256" ) @@ -28,6 +29,8 @@ import ( // defined jump tables are not polluted. func EnableEIP(eipNum int, jt *JumpTable) error { switch eipNum { + case 3898: + enable3198(jt) case 2200: enable2200(jt) case 1884: @@ -90,3 +93,22 @@ func enable2200(jt *JumpTable) { jt[SLOAD].constantGas = params.SloadGasEIP2200 jt[SSTORE].dynamicGas = gasSStoreEIP2200 } + +// enable3198 applies EIP-3198 (BASEFEE Opcode) +// - Adds an opcode that returns the current block's base fee. +func enable3198(jt *JumpTable) { + // New opcode + jt[BASEFEE] = &operation{ + execute: opBaseFee, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } +} + +// opBaseFee implements BASEFEE opcode +func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + baseFee, _ := uint256.FromBig(common.MinGasPrice50x) + callContext.stack.push(baseFee) + return nil, nil +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 284ea6768c96..576fbdd09436 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -46,12 +46,14 @@ type ( // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { if contract.CodeAddr != nil { - precompiles := PrecompiledContractsHomestead - if evm.chainRules.IsByzantium { - precompiles = PrecompiledContractsByzantium - } - if evm.chainRules.IsIstanbul { + var precompiles map[common.Address]PrecompiledContract + switch { + case evm.chainRules.IsIstanbul: precompiles = PrecompiledContractsIstanbul + case evm.chainRules.IsByzantium: + precompiles = PrecompiledContractsByzantium + default: + precompiles = PrecompiledContractsHomestead } if p := precompiles[*contract.CodeAddr]; p != nil { switch p.(type) { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 59fbf21f7c5a..92bed8e70865 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -93,6 +93,10 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // If jump table was not initialised we set the default one. if cfg.JumpTable == nil { switch { + case evm.chainRules.IsLondon: + cfg.JumpTable = &londonInstructionSet + case evm.chainRules.IsBerlin: + cfg.JumpTable = &berlinInstructionSet case evm.chainRules.IsIstanbul: cfg.JumpTable = &istanbulInstructionSet case evm.chainRules.IsConstantinople: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 55ad78adc53f..68b13fbe2f05 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -50,11 +50,30 @@ var ( byzantiumInstructionSet = newByzantiumInstructionSet() constantinopleInstructionSet = newConstantinopleInstructionSet() istanbulInstructionSet = newIstanbulInstructionSet() + berlinInstructionSet = newBerlinInstructionSet() + londonInstructionSet = newLondonInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +// newLondonInstructionSet returns the frontier, homestead, byzantium, +// constantinople, istanbul, petersburg, berlin and london instructions. +func newLondonInstructionSet() JumpTable { + instructionSet := newBerlinInstructionSet() + // enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 + enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198 + return instructionSet +} + +// newBerlinInstructionSet returns the frontier, homestead, byzantium, +// constantinople, istanbul, petersburg and berlin instructions. +func newBerlinInstructionSet() JumpTable { + instructionSet := newIstanbulInstructionSet() + // enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929 + return instructionSet +} + // newIstanbulInstructionSet returns the frontier, homestead // byzantium, contantinople and petersburg instructions. func newIstanbulInstructionSet() JumpTable { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index b9001138b05a..69b0436db45f 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -101,6 +101,7 @@ const ( GASLIMIT OpCode = 0x45 CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 + BASEFEE OpCode = 0x48 ) // 0x50 range - 'storage' and execution. @@ -280,6 +281,7 @@ var opCodeToString = [256]string{ GASLIMIT: "GASLIMIT", CHAINID: "CHAINID", SELFBALANCE: "SELFBALANCE", + BASEFEE: "BASEFEE", // 0x50 range - 'storage' and execution. POP: "POP", @@ -445,6 +447,7 @@ var stringToOp = map[string]OpCode{ "DIFFICULTY": DIFFICULTY, "GASLIMIT": GASLIMIT, "SELFBALANCE": SELFBALANCE, + "BASEFEE": BASEFEE, "POP": POP, "MLOAD": MLOAD, "MSTORE": MSTORE, diff --git a/params/config.go b/params/config.go index a850bd54d063..a75f7a40538a 100644 --- a/params/config.go +++ b/params/config.go @@ -362,6 +362,8 @@ type ChainConfig struct { ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) + BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) + LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london) // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` @@ -498,7 +500,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v BerlinBlock: %v LondonBlock: %v Engine: %v}", c.ChainId, c.HomesteadBlock, c.DAOForkBlock, @@ -508,6 +510,8 @@ func (c *ChainConfig) String() string { c.EIP158Block, c.ByzantiumBlock, c.ConstantinopleBlock, + c.BerlinBlock, + c.LondonBlock, engine, ) } @@ -554,6 +558,16 @@ func (c *ChainConfig) IsIstanbul(num *big.Int) bool { return isForked(common.TIPXDCXCancellationFee, num) } +// IsBerlin returns whether num is either equal to the Berlin fork block or greater. +func (c *ChainConfig) IsBerlin(num *big.Int) bool { + return isForked(common.BerlinBlock, num) +} + +// IsLondon returns whether num is either equal to the London fork block or greater. +func (c *ChainConfig) IsLondon(num *big.Int) bool { + return isForked(common.LondonBlock, num) +} + func (c *ChainConfig) IsTIP2019(num *big.Int) bool { return isForked(common.TIP2019Block, num) } @@ -655,6 +669,13 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) { return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) } + if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) { + return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock) + } + if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) { + return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock) + } + return nil } @@ -722,6 +743,7 @@ type Rules struct { ChainId *big.Int IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool + IsBerlin, IsLondon bool } func (c *ChainConfig) Rules(num *big.Int) Rules { @@ -739,5 +761,7 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsConstantinople: c.IsConstantinople(num), IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), + IsBerlin: c.IsBerlin(num), + IsLondon: c.IsLondon(num), } } From cf221952c5e3a1f1025111249403b132824d230f Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 3 Mar 2024 15:41:00 +1100 Subject: [PATCH 36/59] Use github action (#460) --- .github/workflows/ci.yml | 133 +++++++++++++++++++++++++++++++++++++++ mobile/android_test.go | 1 + 2 files changed, 134 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000000..c9836aead5a6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,133 @@ +name: CI + +on: + push: + branches: + - '*' + tags: + - '*' + +env: + GOPROXY: https://proxy.golang.org + GO111MODULE: on + tf_version: "1.3.0" + tf_init_cli_options: "-input=false" + tf_validation_cli_options: "" + tf_plan_cli_options: "-lock=false -input=false" + tf_apply_cli_options: "-auto-approve -input=false" + +jobs: + tests: + env: + GOPATH: ${{ github.workspace }} + GOBIN: ${{ github.workspace }}/bin + runs-on: ubuntu-latest + defaults: + run: + working-directory: ${{ env.GOPATH }}/src/XDPoSChain + strategy: + matrix: + include: + - name: A-B tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[a-b].*") + - name: C-[a-m] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[a-m].*") + - name: C-[n-o] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[n-o].*") + - name: C-[p-z] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/c[p-z].*") + - name: D-I tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[d-i].*") + - name: J-N tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[j-n].*") + - name: O-R tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[o-r].*") + - name: S tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/s.*") + - name: T-Z tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[t-z].*") + steps: + - name: Check out code + uses: actions/checkout@v3 + with: + path: ${{ env.GOPATH }}/src/XDPoSChain + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: '1.21.x' + - name: Run tests + run: ${{ matrix.script }} + env: + GO111MODULE: auto + + tag_build: + runs-on: ubuntu-latest + needs: tests + if: startsWith(github.ref, 'refs/tags/') + steps: + - uses: actions/checkout@v3 + - name: Login to Docker Hub + run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin + - name: Build Docker image + run: docker build -t xinfinorg/xdposchain:${GITHUB_REF#refs/tags/} -f cicd/Dockerfile . + - name: Push Docker image + run: docker push xinfinorg/xdposchain:${GITHUB_REF#refs/tags/} + + devnet_build_push: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + needs: tests + steps: + - uses: actions/checkout@v3 + - name: Login to Docker Hub + run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin + - name: Build and Push Docker images + run: | + docker pull xinfinorg/devnet:latest + docker tag xinfinorg/devnet:latest xinfinorg/devnet:previous + docker rmi xinfinorg/devnet:latest + docker build -t xinfinorg/devnet:latest -f cicd/Dockerfile . + docker push xinfinorg/devnet:latest + docker push xinfinorg/devnet:previous + + devnet_terraform_apply: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + needs: devnet_build_push + environment: devnet + env: + AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + steps: + - uses: actions/checkout@v3 + - name: Terraform Apply + run: | + cd cicd/devnet/terraform + terraform init ${{ env.tf_init_cli_options }} + terraform apply ${{ env.tf_apply_cli_options }} + sleep 5 + source .env + for ((i=$us_east_2_start;i<$us_east_2_end;i++)); do + echo "Force deploy xdc-$i" + aws ecs update-service --region us-east-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; + done + for ((i=$eu_west_1_start;i<$eu_west_1_end;i++)); do + echo "Force deploy xdc-$i" + aws ecs update-service --region eu-west-1 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; + done + for ((i=$ap_southeast_2_start;i<$ap_southeast_2_end;i++)); do + echo "Force deploy xdc-$i" + aws ecs update-service --region ap-southeast-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; + done + aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager; + + + devnet_send_notification: + runs-on: ubuntu-latest + needs: devnet_terraform_apply + if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + steps: + - uses: actions/checkout@v3 + - name: Send deployment notification + run: | + curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=${GITHUB_SHA}" diff --git a/mobile/android_test.go b/mobile/android_test.go index 9bb5fd0c4f79..8cc4daacb8a8 100644 --- a/mobile/android_test.go +++ b/mobile/android_test.go @@ -154,6 +154,7 @@ public class AndroidTest extends InstrumentationTestCase { // // This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest func TestAndroid(t *testing.T) { + t.Skip("skip this test since it's not being used") // Skip tests on Windows altogether if runtime.GOOS == "windows" { t.Skip("cannot test Android bindings on Windows, skipping") From 317dc4cdf6814c6e2d52811f1a25911f14f79570 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 3 Mar 2024 16:20:38 +1100 Subject: [PATCH 37/59] remove all CI warning by upgrade to nodejs 20 image (#461) * upgrade to nodejs 20 * disable travis ci --- .github/workflows/ci.yml | 21 +++++++++++---------- .travis.yml => .travis.yml.bak | 0 2 files changed, 11 insertions(+), 10 deletions(-) rename .travis.yml => .travis.yml.bak (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9836aead5a6..f64086133d14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,12 +48,13 @@ jobs: script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[t-z].*") steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: ${{ env.GOPATH }}/src/XDPoSChain - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: + cache: false go-version: '1.21.x' - name: Run tests run: ${{ matrix.script }} @@ -65,7 +66,7 @@ jobs: needs: tests if: startsWith(github.ref, 'refs/tags/') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login to Docker Hub run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin - name: Build Docker image @@ -78,7 +79,7 @@ jobs: if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') needs: tests steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Login to Docker Hub run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin - name: Build and Push Docker images @@ -99,7 +100,7 @@ jobs: AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Terraform Apply run: | cd cicd/devnet/terraform @@ -109,17 +110,17 @@ jobs: source .env for ((i=$us_east_2_start;i<$us_east_2_end;i++)); do echo "Force deploy xdc-$i" - aws ecs update-service --region us-east-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; + aws ecs update-service --region us-east-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10; done for ((i=$eu_west_1_start;i<$eu_west_1_end;i++)); do echo "Force deploy xdc-$i" - aws ecs update-service --region eu-west-1 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; + aws ecs update-service --region eu-west-1 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10; done for ((i=$ap_southeast_2_start;i<$ap_southeast_2_end;i++)); do echo "Force deploy xdc-$i" - aws ecs update-service --region ap-southeast-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager; + aws ecs update-service --region ap-southeast-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager | head -n 10; done - aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager; + aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager | head -n 10; devnet_send_notification: @@ -127,7 +128,7 @@ jobs: needs: devnet_terraform_apply if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Send deployment notification run: | curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=${GITHUB_SHA}" diff --git a/.travis.yml b/.travis.yml.bak similarity index 100% rename from .travis.yml rename to .travis.yml.bak From e324d7d18f503b54132e61e09ffab28825d01091 Mon Sep 17 00:00:00 2001 From: Banana-J Date: Sun, 3 Mar 2024 22:27:01 +1100 Subject: [PATCH 38/59] fix: race condition of x.signer in v1 consensus (#445) Co-authored-by: wjrjerome --- consensus/XDPoS/engines/engine_v1/engine.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index f01f486aa702..b647f43055e0 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -708,9 +708,9 @@ func (x *XDPoS_v1) Prepare(chain consensus.ChainReader, header *types.Header) er if err != nil { return err } - if number%x.config.Epoch != 0 { - x.lock.RLock() + x.lock.RLock() + if number%x.config.Epoch != 0 { // Gather all the proposals that make sense voting on addresses := make([]common.Address, 0, len(x.proposals)) for address, authorize := range x.proposals { @@ -727,14 +727,16 @@ func (x *XDPoS_v1) Prepare(chain consensus.ChainReader, header *types.Header) er copy(header.Nonce[:], utils.NonceDropVote) } } - x.lock.RUnlock() } + signer := x.signer + x.lock.RUnlock() + parent := chain.GetHeader(header.ParentHash, number-1) if parent == nil { return consensus.ErrUnknownAncestor } // Set the correct difficulty - header.Difficulty = x.calcDifficulty(chain, parent, x.signer) + header.Difficulty = x.calcDifficulty(chain, parent, signer) log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty) // Ensure the extra data has all it's components if len(header.Extra) < utils.ExtraVanity { @@ -956,7 +958,10 @@ func (x *XDPoS_v1) Seal(chain consensus.ChainReader, block *types.Block, stop <- // that a new block should have based on the previous blocks in the chain and the // current signer. func (x *XDPoS_v1) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { - return x.calcDifficulty(chain, parent, x.signer) + x.lock.Lock() + signer := x.signer + x.lock.Unlock() + return x.calcDifficulty(chain, parent, signer) } func (x *XDPoS_v1) calcDifficulty(chain consensus.ChainReader, parent *types.Header, signer common.Address) *big.Int { From a31489541d79b963bda43fabcf6c276df8cf871a Mon Sep 17 00:00:00 2001 From: Gerui Wang Date: Sat, 2 Mar 2024 18:45:28 +0800 Subject: [PATCH 39/59] implement EIP-4399, PREVRANDAO opcode. and remove unused LondonBlock and BerlinBlock in `params/config.go` (already defined in constants.go) --- common/constants.go | 1 + common/constants/constants.go.devnet | 1 + common/constants/constants.go.testnet | 1 + core/evm.go | 9 ++++++- core/vm/evm.go | 1 + core/vm/instructions.go | 11 +++++++++ core/vm/instructions_test.go | 34 +++++++++++++++++++++++++++ core/vm/interpreter.go | 4 +++- core/vm/jump_table.go | 12 ++++++++++ core/vm/opcodes.go | 4 +++- params/config.go | 27 +++++++++++---------- 11 files changed, 89 insertions(+), 16 deletions(-) diff --git a/common/constants.go b/common/constants.go index 7020e8fd682b..349706679b97 100644 --- a/common/constants.go +++ b/common/constants.go @@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(38383838) var TIPXDCXCancellationFeeTestnet = big.NewInt(38383838) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) +var MergeBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(38383838) var IsTestnet bool = false diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index 58351e020bf0..d5df25b477ed 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(225000) var TIPXDCXCancellationFeeTestnet = big.NewInt(225000) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) +var MergeBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(0) var IsTestnet bool = false diff --git a/common/constants/constants.go.testnet b/common/constants/constants.go.testnet index eae07aa11647..5b173297feee 100644 --- a/common/constants/constants.go.testnet +++ b/common/constants/constants.go.testnet @@ -47,6 +47,7 @@ var TIPXDCXCancellationFee = big.NewInt(23779191) var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) +var MergeBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(23779191) var IsTestnet bool = false diff --git a/core/evm.go b/core/evm.go index f3fb1a234971..bc1c72ce5bc2 100644 --- a/core/evm.go +++ b/core/evm.go @@ -23,17 +23,23 @@ import ( "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" + "github.com/XinFinOrg/XDPoSChain/crypto" ) // NewEVMContext creates a new context for use in the EVM. func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainContext, author *common.Address) vm.Context { // If we don't have an explicit author (i.e. not mining), extract from the header - var beneficiary common.Address + var ( + beneficiary common.Address + random common.Hash + ) if author == nil { beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation } else { beneficiary = *author } + // since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random + random = crypto.Keccak256Hash(header.Number.Bytes()) return vm.Context{ CanTransfer: CanTransfer, Transfer: Transfer, @@ -45,6 +51,7 @@ func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainConte Difficulty: new(big.Int).Set(header.Difficulty), GasLimit: header.GasLimit, GasPrice: new(big.Int).Set(msg.GasPrice()), + Random: &random, } } diff --git a/core/vm/evm.go b/core/vm/evm.go index 576fbdd09436..227d38412c86 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -107,6 +107,7 @@ type Context struct { BlockNumber *big.Int // Provides information for NUMBER Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY + Random *common.Hash // Provides information for PREVRANDAO } // EVM is the Ethereum Virtual Machine base object and provides diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 73173b903981..a020654440c6 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -477,6 +477,17 @@ func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) return nil, nil } +func opRandom(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + var v *uint256.Int + if interpreter.evm.Context.Random != nil { + v = new(uint256.Int).SetBytes((interpreter.evm.Context.Random.Bytes())) + } else { // if context random is not set, use emptyCodeHash as default + v = new(uint256.Int).SetBytes(emptyCodeHash.Bytes()) + } + callContext.stack.push(v) + return nil, nil +} + func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 1f63d2b040a1..200aa1477235 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "log" + "math/big" "os" "testing" @@ -661,3 +662,36 @@ func TestCreate2Addreses(t *testing.T) { } } } + +func TestRandom(t *testing.T) { + type testcase struct { + name string + random common.Hash + } + + for _, tt := range []testcase{ + {name: "empty hash", random: common.Hash{}}, + {name: "1", random: common.Hash{0}}, + {name: "emptyCodeHash", random: emptyCodeHash}, + {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, + } { + var ( + env = NewEVM(Context{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + opRandom(&pc, evmInterpreter, &callCtx{nil, stack, nil}) + if len(stack.data) != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) + } + actual := stack.pop() + expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes())) + if overflow { + t.Errorf("Testcase %v: invalid overflow", tt.name) + } + if actual.Cmp(expected) != 0 { + t.Errorf("Testcase %v: expected %x, got %x", tt.name, expected, actual) + } + } +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 92bed8e70865..29fc4a2cb763 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -93,6 +93,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // If jump table was not initialised we set the default one. if cfg.JumpTable == nil { switch { + case evm.chainRules.IsMerge: + cfg.JumpTable = &mergeInstructionSet case evm.chainRules.IsLondon: cfg.JumpTable = &londonInstructionSet case evm.chainRules.IsBerlin: @@ -262,7 +264,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } pc++ } - + if err == errStopToken { err = nil // clear stop token error } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 68b13fbe2f05..298ec63ab3db 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -52,11 +52,23 @@ var ( istanbulInstructionSet = newIstanbulInstructionSet() berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() + mergeInstructionSet = newMergeInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +func newMergeInstructionSet() JumpTable { + instructionSet := newLondonInstructionSet() + instructionSet[PREVRANDAO] = &operation{ + execute: opRandom, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } + return instructionSet +} + // newLondonInstructionSet returns the frontier, homestead, byzantium, // constantinople, istanbul, petersburg, berlin and london instructions. func newLondonInstructionSet() JumpTable { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 69b0436db45f..3b123f3a4994 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -98,6 +98,8 @@ const ( TIMESTAMP OpCode = 0x42 NUMBER OpCode = 0x43 DIFFICULTY OpCode = 0x44 + RANDOM OpCode = 0x44 // Same as DIFFICULTY + PREVRANDAO OpCode = 0x44 // Same as DIFFICULTY GASLIMIT OpCode = 0x45 CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 @@ -277,7 +279,7 @@ var opCodeToString = [256]string{ COINBASE: "COINBASE", TIMESTAMP: "TIMESTAMP", NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", + DIFFICULTY: "DIFFICULTY", // TODO rename to PREVRANDAO post merge GASLIMIT: "GASLIMIT", CHAINID: "CHAINID", SELFBALANCE: "SELFBALANCE", diff --git a/params/config.go b/params/config.go index a75f7a40538a..3462e4dc2c7f 100644 --- a/params/config.go +++ b/params/config.go @@ -362,8 +362,6 @@ type ChainConfig struct { ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium) ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated) - BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) - LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london) // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` @@ -500,7 +498,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v BerlinBlock: %v LondonBlock: %v Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %v Engine: %v}", c.ChainId, c.HomesteadBlock, c.DAOForkBlock, @@ -510,8 +508,10 @@ func (c *ChainConfig) String() string { c.EIP158Block, c.ByzantiumBlock, c.ConstantinopleBlock, - c.BerlinBlock, - c.LondonBlock, + common.TIPXDCXCancellationFee, + common.BerlinBlock, + common.LondonBlock, + common.MergeBlock, engine, ) } @@ -568,6 +568,12 @@ func (c *ChainConfig) IsLondon(num *big.Int) bool { return isForked(common.LondonBlock, num) } +// IsMerge returns whether num is either equal to the Merge fork block or greater. +// Different from Geth which uses `block.difficulty != nil` +func (c *ChainConfig) IsMerge(num *big.Int) bool { + return isForked(common.MergeBlock, num) +} + func (c *ChainConfig) IsTIP2019(num *big.Int) bool { return isForked(common.TIP2019Block, num) } @@ -669,13 +675,6 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) { return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) } - if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) { - return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock) - } - if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) { - return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock) - } - return nil } @@ -744,6 +743,7 @@ type Rules struct { IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool + IsMerge bool } func (c *ChainConfig) Rules(num *big.Int) Rules { @@ -762,6 +762,7 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), IsBerlin: c.IsBerlin(num), - IsLondon: c.IsLondon(num), + IsLondon: c.IsLondon(num), + IsMerge: c.IsMerge(num), } } From 72e5ee06456a59a4f0f1bc4fdd6a9ebe5be0f7ca Mon Sep 17 00:00:00 2001 From: Gerui Wang Date: Mon, 4 Mar 2024 00:34:49 +0800 Subject: [PATCH 40/59] lint for `params/config.go` --- params/config.go | 122 +++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/params/config.go b/params/config.go index 3462e4dc2c7f..0904b54786f7 100644 --- a/params/config.go +++ b/params/config.go @@ -242,19 +242,19 @@ var ( // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. AllEthashProtocolChanges = &ChainConfig{ - ChainId: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP150Hash: common.Hash{}, - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), + ChainId: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: nil, - Ethash: new(EthashConfig), - Clique: nil, - XDPoS: nil, + Ethash: new(EthashConfig), + Clique: nil, + XDPoS: nil, } // AllXDPoSProtocolChanges contains every protocol change (EIPs) introduced @@ -262,52 +262,52 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllXDPoSProtocolChanges = &ChainConfig{ - ChainId: big.NewInt(89), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP150Hash: common.Hash{}, - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), + AllXDPoSProtocolChanges = &ChainConfig{ + ChainId: big.NewInt(89), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: nil, - Ethash: nil, - Clique: nil, - XDPoS: &XDPoSConfig{Period: 0, Epoch: 900}, + Ethash: nil, + Clique: nil, + XDPoS: &XDPoSConfig{Period: 0, Epoch: 900}, } AllCliqueProtocolChanges = &ChainConfig{ - ChainId: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP150Hash: common.Hash{}, - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), + ChainId: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: nil, - Ethash: nil, - Clique: &CliqueConfig{Period: 0, Epoch: 900}, - XDPoS: nil, + Ethash: nil, + Clique: &CliqueConfig{Period: 0, Epoch: 900}, + XDPoS: nil, } // XDPoS config with v2 engine after block 901 TestXDPoSMockChainConfig = &ChainConfig{ - ChainId: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP150Hash: common.Hash{}, - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), + ChainId: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: nil, - Ethash: new(EthashConfig), - Clique: nil, + Ethash: new(EthashConfig), + Clique: nil, XDPoS: &XDPoSConfig{ Epoch: 900, Gap: 450, @@ -323,21 +323,21 @@ var ( } TestChainConfig = &ChainConfig{ - ChainId: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP150Hash: common.Hash{}, - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), + ChainId: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: nil, - Ethash: new(EthashConfig), - Clique: nil, - XDPoS: nil, + Ethash: new(EthashConfig), + Clique: nil, + XDPoS: nil, } - TestRules = TestChainConfig.Rules(new(big.Int)) + TestRules = TestChainConfig.Rules(new(big.Int)) ) // ChainConfig is the core config which determines the blockchain settings. From b86c7f646fc90e28fac8c103271393228ff0de18 Mon Sep 17 00:00:00 2001 From: Wanwiset Peerapatanapokin Date: Mon, 4 Mar 2024 07:59:45 +0400 Subject: [PATCH 41/59] Try remove privacy pkg (#466) * try removing privacy pkg * rm more reference * trigger CI --- core/vm/contracts.go | 43 +- core/vm/privacy/bulletproof.go | 1400 --------------------------- core/vm/privacy/bulletproof.json | 1 - core/vm/privacy/bulletproof_test.go | 527 ---------- core/vm/privacy/ringct.go | 632 ------------ core/vm/privacy/ringct_test.go | 324 ------- 6 files changed, 1 insertion(+), 2926 deletions(-) delete mode 100644 core/vm/privacy/bulletproof.go delete mode 100644 core/vm/privacy/bulletproof.json delete mode 100644 core/vm/privacy/bulletproof_test.go delete mode 100644 core/vm/privacy/ringct.go delete mode 100644 core/vm/privacy/ringct_test.go diff --git a/core/vm/contracts.go b/core/vm/contracts.go index b07cce9fc29b..1f03559e2a87 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -22,8 +22,6 @@ import ( "errors" "math/big" - "github.com/XinFinOrg/XDPoSChain/core/vm/privacy" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -63,8 +61,6 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{6}): &bn256AddByzantium{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{}, common.BytesToAddress([]byte{8}): &bn256PairingByzantium{}, - common.BytesToAddress([]byte{30}): &ringSignatureVerifier{}, - common.BytesToAddress([]byte{40}): &bulletproofVerifier{}, common.BytesToAddress([]byte{41}): &XDCxLastPrice{}, common.BytesToAddress([]byte{42}): &XDCxEpochPrice{}, } @@ -81,8 +77,6 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, common.BytesToAddress([]byte{9}): &blake2F{}, - common.BytesToAddress([]byte{30}): &ringSignatureVerifier{}, - common.BytesToAddress([]byte{40}): &bulletproofVerifier{}, common.BytesToAddress([]byte{41}): &XDCxLastPrice{}, common.BytesToAddress([]byte{42}): &XDCxEpochPrice{}, } @@ -426,42 +420,6 @@ func runBn256Pairing(input []byte) ([]byte, error) { return false32Byte, nil } -type ringSignatureVerifier struct{} -type bulletproofVerifier struct{} - -func (c *bulletproofVerifier) RequiredGas(input []byte) uint64 { - //the gas should depends on the ringsize - return 100000 -} - -func (c *ringSignatureVerifier) RequiredGas(input []byte) uint64 { - //the gas should depends on the ringsize - return 100000 -} - -func (c *ringSignatureVerifier) Run(proof []byte) ([]byte, error) { - der, err := privacy.Deserialize(proof) - if err != nil { - return []byte{}, errors.New("Fail to deserialize proof") - } - if !privacy.Verify(der, false) { - return []byte{}, errors.New("Fail to verify ring signature") - } - return []byte{}, nil -} - -func (c *bulletproofVerifier) Run(proof []byte) ([]byte, error) { - mrp := new(privacy.MultiRangeProof) - if mrp.Deserialize(proof) != nil { - return []byte{}, errors.New("failed to deserialize bulletproofs") - } - - if !privacy.MRPVerify(mrp) { - return []byte{}, errors.New("failed to verify bulletproof") - } - return []byte{}, nil -} - // bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve // conforming to Istanbul consensus rules. type bn256PairingIstanbul struct{} @@ -488,6 +446,7 @@ func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { return runBn256Pairing(input) } + type blake2F struct{} func (c *blake2F) RequiredGas(input []byte) uint64 { diff --git a/core/vm/privacy/bulletproof.go b/core/vm/privacy/bulletproof.go deleted file mode 100644 index f620d230b2ca..000000000000 --- a/core/vm/privacy/bulletproof.go +++ /dev/null @@ -1,1400 +0,0 @@ -package privacy - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha256" - "encoding/binary" - "errors" - "fmt" - "math" - "math/big" - "strconv" - - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/log" - - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/btcsuite/btcd/btcec" -) - -type Bulletproof struct { - proofData []byte -} - -var EC CryptoParams -var VecLength = 512 // support maximum 8 spending value, each 64 bit (gwei is unit) -var curve elliptic.Curve = crypto.S256() - -/* -Implementation of BulletProofs -*/ -type ECPoint struct { - X, Y *big.Int -} - -func (p *ECPoint) toECPubKey() *ecdsa.PublicKey { - return &ecdsa.PublicKey{curve, p.X, p.Y} -} - -func toECPoint(key *ecdsa.PublicKey) *ECPoint { - return &ECPoint{key.X, key.Y} -} - -// Equal returns true if points p (self) and p2 (arg) are the same. -func (p ECPoint) Equal(p2 ECPoint) bool { - if p.X.Cmp(p2.X) == 0 && p2.Y.Cmp(p2.Y) == 0 { - return true - } - return false -} - -// Mult multiplies point p by scalar s and returns the resulting point -func (p ECPoint) Mult(s *big.Int) ECPoint { - modS := new(big.Int).Mod(s, EC.N) - X, Y := EC.C.ScalarMult(p.X, p.Y, modS.Bytes()) - return ECPoint{X, Y} -} - -// Add adds points p and p2 and returns the resulting point -func (p ECPoint) Add(p2 ECPoint) ECPoint { - X, Y := EC.C.Add(p.X, p.Y, p2.X, p2.Y) - return ECPoint{X, Y} -} - -// Neg returns the additive inverse of point p -func (p ECPoint) Neg() ECPoint { - negY := new(big.Int).Neg(p.Y) - modValue := negY.Mod(negY, EC.C.Params().P) // mod P is fine here because we're describing a curve point - return ECPoint{p.X, modValue} -} - -type CryptoParams struct { - C elliptic.Curve // curve - KC *btcec.KoblitzCurve // curve - BPG []ECPoint // slice of gen 1 for BP - BPH []ECPoint // slice of gen 2 for BP - N *big.Int // scalar prime - U ECPoint // a point that is a fixed group element with an unknown discrete-log relative to g,h - V int // Vector length - G ECPoint // G value for commitments of a single value - H ECPoint // H value for commitments of a single value -} - -func (c CryptoParams) Zero() ECPoint { - return ECPoint{big.NewInt(0), big.NewInt(0)} -} - -func check(e error) { - if e != nil { - panic(e) - } -} - -/* -Vector Pedersen Commitment -Given an array of values, we commit the array with different generators -for each element and for each randomness. -*/ -func VectorPCommit(value []*big.Int) (ECPoint, []*big.Int) { - R := make([]*big.Int, EC.V) - - commitment := EC.Zero() - - for i := 0; i < EC.V; i++ { - r, err := rand.Int(rand.Reader, EC.N) - check(err) - - R[i] = r - - modValue := new(big.Int).Mod(value[i], EC.N) - - // mG, rH - lhsX, lhsY := EC.C.ScalarMult(EC.BPG[i].X, EC.BPG[i].Y, modValue.Bytes()) - rhsX, rhsY := EC.C.ScalarMult(EC.BPH[i].X, EC.BPH[i].Y, r.Bytes()) - - commitment = commitment.Add(ECPoint{lhsX, lhsY}).Add(ECPoint{rhsX, rhsY}) - } - - return commitment, R -} - -/* -Two Vector P Commit -Given an array of values, we commit the array with different generators -for each element and for each randomness. -*/ -func TwoVectorPCommit(a []*big.Int, b []*big.Int) ECPoint { - if len(a) != len(b) { - log.Debug("TwoVectorPCommit: Arrays not of the same length", "len(a)", len(a), "len(b)", len(b)) - } - - commitment := EC.Zero() - - for i := 0; i < EC.V; i++ { - commitment = commitment.Add(EC.BPG[i].Mult(a[i])).Add(EC.BPH[i].Mult(b[i])) - } - - return commitment -} - -/* -Vector Pedersen Commitment with Gens -Given an array of values, we commit the array with different generators -for each element and for each randomness. -We also pass in the Generators we want to use -*/ -func TwoVectorPCommitWithGens(G, H []ECPoint, a, b []*big.Int) ECPoint { - if len(G) != len(H) || len(G) != len(a) || len(a) != len(b) { - log.Debug("TwoVectorPCommitWithGens: Arrays not of the same length", "len(G)", len(G), "len(H)", len(H), "len(a)", len(a), "len(b)", len(b)) - } - - commitment := EC.Zero() - - for i := 0; i < len(G); i++ { - modA := new(big.Int).Mod(a[i], EC.N) - modB := new(big.Int).Mod(b[i], EC.N) - - commitment = commitment.Add(G[i].Mult(modA)).Add(H[i].Mult(modB)) - } - - return commitment -} - -// The length here always has to be a power of two -func InnerProduct(a []*big.Int, b []*big.Int) *big.Int { - if len(a) != len(b) { - log.Debug("InnerProduct: Arrays not of the same length", "len(a)", len(a), "len(b", len(b)) - } - - c := big.NewInt(0) - - for i := range a { - tmp1 := new(big.Int).Mul(a[i], b[i]) - c = new(big.Int).Add(c, new(big.Int).Mod(tmp1, EC.N)) - } - - return new(big.Int).Mod(c, EC.N) -} - -func VectorAdd(v []*big.Int, w []*big.Int) []*big.Int { - if len(v) != len(w) { - log.Debug("VectorAdd: Arrays not of the same length", "len(v)", len(v), "len(w)", len(w)) - } - result := make([]*big.Int, len(v)) - - for i := range v { - result[i] = new(big.Int).Mod(new(big.Int).Add(v[i], w[i]), EC.N) - } - - return result -} - -func VectorHadamard(v, w []*big.Int) []*big.Int { - if len(v) != len(w) { - log.Debug("VectorHadamard: Arrays not of the same length", "len(v)", len(v), "len(w)", len(w)) - } - - result := make([]*big.Int, len(v)) - - for i := range v { - result[i] = new(big.Int).Mod(new(big.Int).Mul(v[i], w[i]), EC.N) - } - - return result -} - -func VectorAddScalar(v []*big.Int, s *big.Int) []*big.Int { - result := make([]*big.Int, len(v)) - - for i := range v { - result[i] = new(big.Int).Mod(new(big.Int).Add(v[i], s), EC.N) - } - - return result -} - -func ScalarVectorMul(v []*big.Int, s *big.Int) []*big.Int { - result := make([]*big.Int, len(v)) - - for i := range v { - result[i] = new(big.Int).Mod(new(big.Int).Mul(v[i], s), EC.N) - } - - return result -} - -func HashPointsToBytes(points []ECPoint) []byte { - input := []byte{} - - for index := 0; index < len(points); index++ { - pointInByte := append(PadTo32Bytes(points[index].X.Bytes()), PadTo32Bytes(points[index].Y.Bytes())...) - input = append(input, pointInByte...) - } - - return crypto.Keccak256(input) -} - -/* -InnerProd Proof -This stores the argument values -*/ -type InnerProdArg struct { - L []ECPoint - R []ECPoint - A *big.Int - B *big.Int - - Challenges []*big.Int -} - -func GenerateNewParams(G, H []ECPoint, x *big.Int, L, R, P ECPoint) ([]ECPoint, []ECPoint, ECPoint) { - nprime := len(G) / 2 - - Gprime := make([]ECPoint, nprime) - Hprime := make([]ECPoint, nprime) - - xinv := new(big.Int).ModInverse(x, EC.N) - - // Gprime = xinv * G[:nprime] + x*G[nprime:] - // Hprime = x * H[:nprime] + xinv*H[nprime:] - - for i := range Gprime { - //fmt.Printf("i: %d && i+nprime: %d\n", i, i+nprime) - Gprime[i] = G[i].Mult(xinv).Add(G[i+nprime].Mult(x)) - Hprime[i] = H[i].Mult(x).Add(H[i+nprime].Mult(xinv)) - } - - x2 := new(big.Int).Mod(new(big.Int).Mul(x, x), EC.N) - xinv2 := new(big.Int).ModInverse(x2, EC.N) - - Pprime := L.Mult(x2).Add(P).Add(R.Mult(xinv2)) // x^2 * L + P + xinv^2 * R - - return Gprime, Hprime, Pprime -} - -/* - Inner Product Argument - -Proves that =c -This is a building block for BulletProofs -*/ -func InnerProductProveSub(proof InnerProdArg, G, H []ECPoint, a []*big.Int, b []*big.Int, u ECPoint, P ECPoint) InnerProdArg { - if len(a) == 1 { - // Prover sends a & b - proof.A = a[0] - proof.B = b[0] - return proof - } - - curIt := int(math.Log2(float64(len(a)))) - 1 - - nprime := len(a) / 2 - cl := InnerProduct(a[:nprime], b[nprime:]) // either this line - cr := InnerProduct(a[nprime:], b[:nprime]) // or this line - L := TwoVectorPCommitWithGens(G[nprime:], H[:nprime], a[:nprime], b[nprime:]).Add(u.Mult(cl)) - R := TwoVectorPCommitWithGens(G[:nprime], H[nprime:], a[nprime:], b[:nprime]).Add(u.Mult(cr)) - - proof.L[curIt] = L - proof.R[curIt] = R - - // prover sends L & R and gets a challenge - // LvalInBytes := append(PadTo32Bytes(L.X.Bytes()), PadTo32Bytes(L.Y.Bytes())...) - // RvalInBytes := append(PadTo32Bytes(R.X.Bytes()), PadTo32Bytes(R.Y.Bytes())...) - // input := append(LvalInBytes, RvalInBytes...) - // s256 := crypto.Keccak256(input) - s256 := HashPointsToBytes([]ECPoint{L, R}) - - x := new(big.Int).SetBytes(s256[:]) - - proof.Challenges[curIt] = x - - Gprime, Hprime, Pprime := GenerateNewParams(G, H, x, L, R, P) - - xinv := new(big.Int).ModInverse(x, EC.N) - - // or these two lines - aprime := VectorAdd( - ScalarVectorMul(a[:nprime], x), - ScalarVectorMul(a[nprime:], xinv)) - bprime := VectorAdd( - ScalarVectorMul(b[:nprime], xinv), - ScalarVectorMul(b[nprime:], x)) - - return InnerProductProveSub(proof, Gprime, Hprime, aprime, bprime, u, Pprime) -} - -// rpresult.IPP = InnerProductProve(left, right, that, P, EC.U, EC.BPG, HPrime) -func InnerProductProve(a []*big.Int, b []*big.Int, c *big.Int, P, U ECPoint, G, H []ECPoint) InnerProdArg { - loglen := int(math.Log2(float64(len(a)))) - - challenges := make([]*big.Int, loglen+1) - Lvals := make([]ECPoint, loglen) - Rvals := make([]ECPoint, loglen) - - runningProof := InnerProdArg{ - Lvals, - Rvals, - big.NewInt(0), - big.NewInt(0), - challenges} - - // randomly generate an x value from public data - // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) - // x := crypto.Keccak256(input) - x := HashPointsToBytes([]ECPoint{P}) - - runningProof.Challenges[loglen] = new(big.Int).SetBytes(x[:]) - - Pprime := P.Add(U.Mult(new(big.Int).Mul(new(big.Int).SetBytes(x[:]), c))) - - ux := U.Mult(new(big.Int).SetBytes(x[:])) - //fmt.Printf("Prover Pprime value to run sub off of: %s\n", Pprime) - - return InnerProductProveSub(runningProof, G, H, a, b, ux, Pprime) -} - -/* - Inner Product Verify - -Given a inner product proof, verifies the correctness of the proof -Since we're using the Fiat-Shamir transform, we need to verify all x hash computations, -all g' and h' computations -P : the Pedersen commitment we are verifying is a commitment to the innner product -ipp : the proof -*/ -func InnerProductVerify(c *big.Int, P, U ECPoint, G, H []ECPoint, ipp InnerProdArg) bool { - //fmt.Println("Verifying Inner Product Argument") - //fmt.Printf("Commitment Value: %s \n", P) - // s1 := sha256.Sum256([]byte(P.X.String() + P.Y.String())) - - // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) - // s1 := crypto.Keccak256(input) - s1 := HashPointsToBytes([]ECPoint{P}) - - chal1 := new(big.Int).SetBytes(s1[:]) - ux := U.Mult(chal1) - curIt := len(ipp.Challenges) - 1 - - if ipp.Challenges[curIt].Cmp(chal1) != 0 { - log.Info("Initial Challenge Failed") - return false - } - - curIt -= 1 - - Gprime := G - Hprime := H - Pprime := P.Add(ux.Mult(c)) // line 6 from protocol 1 - //fmt.Printf("New Commitment value with u^cx: %s \n", Pprime) - - for curIt >= 0 { - Lval := ipp.L[curIt] - Rval := ipp.R[curIt] - - // prover sends L & R and gets a challenge - // s256 := sha256.Sum256([]byte( - // Lval.X.String() + Lval.Y.String() + - // Rval.X.String() + Rval.Y.String())) - // LvalInBytes := append(PadTo32Bytes(Lval.X.Bytes()), PadTo32Bytes(Lval.Y.Bytes())...) - // RvalInBytes := append(PadTo32Bytes(Rval.X.Bytes()), PadTo32Bytes(Rval.Y.Bytes())...) - // input := append(LvalInBytes, RvalInBytes...) - // s256 := crypto.Keccak256(input) - s256 := HashPointsToBytes([]ECPoint{Lval, Rval}) - - chal2 := new(big.Int).SetBytes(s256[:]) - - if ipp.Challenges[curIt].Cmp(chal2) != 0 { - log.Info("Challenge verification failed", "index", strconv.Itoa(curIt)) - return false - } - - Gprime, Hprime, Pprime = GenerateNewParams(Gprime, Hprime, chal2, Lval, Rval, Pprime) - curIt -= 1 - } - ccalc := new(big.Int).Mod(new(big.Int).Mul(ipp.A, ipp.B), EC.N) - - Pcalc1 := Gprime[0].Mult(ipp.A) - Pcalc2 := Hprime[0].Mult(ipp.B) - Pcalc3 := ux.Mult(ccalc) - Pcalc := Pcalc1.Add(Pcalc2).Add(Pcalc3) - - return Pprime.Equal(Pcalc) -} - -/* Inner Product Verify Fast -Given a inner product proof, verifies the correctness of the proof. Does the same as above except -we replace n separate exponentiations with a single multi-exponentiation. -*/ - -func InnerProductVerifyFast(c *big.Int, P, U ECPoint, G, H []ECPoint, ipp InnerProdArg) bool { - // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) - // s1 := crypto.Keccak256(input) - s1 := HashPointsToBytes([]ECPoint{P}) - - chal1 := new(big.Int).SetBytes(s1[:]) - ux := U.Mult(chal1) - curIt := len(ipp.Challenges) - 1 - - // check all challenges - if ipp.Challenges[curIt].Cmp(chal1) != 0 { - log.Debug("Initial Challenge Failed") - return false - } - - for j := curIt - 1; j >= 0; j-- { - Lval := ipp.L[j] - Rval := ipp.R[j] - - // prover sends L & R and gets a challenge - // LvalInBytes := append(PadTo32Bytes(Lval.X.Bytes()), PadTo32Bytes(Lval.Y.Bytes())...) - // RvalInBytes := append(PadTo32Bytes(Rval.X.Bytes()), PadTo32Bytes(Rval.Y.Bytes())...) - // input := append(LvalInBytes, RvalInBytes...) - // s256 := crypto.Keccak256(input) - s256 := HashPointsToBytes([]ECPoint{Lval, Rval}) - - chal2 := new(big.Int).SetBytes(s256[:]) - - if ipp.Challenges[j].Cmp(chal2) != 0 { - log.Debug("Challenge verification failed", "index", strconv.Itoa(j)) - return false - } - } - // begin computing - - curIt -= 1 - Pprime := P.Add(ux.Mult(c)) // line 6 from protocol 1 - - tmp1 := EC.Zero() - for j := curIt; j >= 0; j-- { - x2 := new(big.Int).Exp(ipp.Challenges[j], big.NewInt(2), EC.N) - x2i := new(big.Int).ModInverse(x2, EC.N) - //fmt.Println(tmp1) - tmp1 = ipp.L[j].Mult(x2).Add(ipp.R[j].Mult(x2i)).Add(tmp1) - //fmt.Println(tmp1) - } - rhs := Pprime.Add(tmp1) - //rhs=P+c*ux + L*xw+R*x2i 公式29 - - sScalars := make([]*big.Int, EC.V) - invsScalars := make([]*big.Int, EC.V) - - for i := 0; i < EC.V; i++ { - si := big.NewInt(1) - for j := curIt; j >= 0; j-- { - // original challenge if the jth bit of i is 1, inverse challenge otherwise - chal := ipp.Challenges[j] - if big.NewInt(int64(i)).Bit(j) == 0 { - chal = new(big.Int).ModInverse(chal, EC.N) - } - // fmt.Printf("Challenge raised to value: %d\n", chal) - si = new(big.Int).Mod(new(big.Int).Mul(si, chal), EC.N) - } - //fmt.Printf("Si value: %d\n", si) - sScalars[i] = si - invsScalars[i] = new(big.Int).ModInverse(si, EC.N) - } - - ccalc := new(big.Int).Mod(new(big.Int).Mul(ipp.A, ipp.B), EC.N) - lhs := TwoVectorPCommitWithGens(G, H, ScalarVectorMul(sScalars, ipp.A), ScalarVectorMul(invsScalars, ipp.B)).Add(ux.Mult(ccalc)) - - if !rhs.Equal(lhs) { - log.Info("IPVerify - Final Commitment checking failed") - return false - } - - return true -} - -// from here: https://play.golang.org/p/zciRZvD0Gr with a fix -func PadLeft(str, pad string, l int) string { - strCopy := str - for len(strCopy) < l { - strCopy = pad + strCopy - } - - return strCopy -} - -func STRNot(str string) string { - result := "" - - for _, i := range str { - if i == '0' { - result += "1" - } else { - result += "0" - } - } - return result -} - -func StrToBigIntArray(str string) []*big.Int { - result := make([]*big.Int, len(str)) - - for i := range str { - t, success := new(big.Int).SetString(string(str[i]), 10) - if success { - result[i] = t - } - } - - return result -} - -func reverse(l []*big.Int) []*big.Int { - result := make([]*big.Int, len(l)) - - for i := range l { - result[i] = l[len(l)-i-1] - } - - return result -} - -func PowerVector(l int, base *big.Int) []*big.Int { - result := make([]*big.Int, l) - - for i := 0; i < l; i++ { - result[i] = new(big.Int).Exp(base, big.NewInt(int64(i)), EC.N) - } - - return result -} - -func RandVector(l int) []*big.Int { - result := make([]*big.Int, l) - - for i := 0; i < l; i++ { - x, err := rand.Int(rand.Reader, EC.N) - check(err) - result[i] = x - } - - return result -} - -func VectorSum(y []*big.Int) *big.Int { - result := big.NewInt(0) - - for _, j := range y { - result = new(big.Int).Mod(new(big.Int).Add(result, j), EC.N) - } - - return result -} - -type RangeProof struct { - Comm ECPoint - A ECPoint - S ECPoint - T1 ECPoint - T2 ECPoint - Tau *big.Int - Th *big.Int - Mu *big.Int - IPP InnerProdArg - - // challenges - Cy *big.Int - Cz *big.Int - Cx *big.Int -} - -/* -Delta is a helper function that is used in the range proof -\delta(y, z) = (z-z^2)<1^n, y^n> - z^3<1^n, 2^n> -*/ - -func Delta(y []*big.Int, z *big.Int) *big.Int { - result := big.NewInt(0) - - // (z-z^2)<1^n, y^n> - z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N) - t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N) - t2 := new(big.Int).Mod(new(big.Int).Mul(t1, VectorSum(y)), EC.N) - - // z^3<1^n, 2^n> - z3 := new(big.Int).Mod(new(big.Int).Mul(z2, z), EC.N) - po2sum := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(EC.V)), EC.N), big.NewInt(1)) - t3 := new(big.Int).Mod(new(big.Int).Mul(z3, po2sum), EC.N) - - result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) - - return result -} - -// Calculates (aL - z*1^n) + sL*x -func CalculateL(aL, sL []*big.Int, z, x *big.Int) []*big.Int { - result := make([]*big.Int, len(aL)) - - tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z)) - tmp2 := ScalarVectorMul(sL, x) - - result = VectorAdd(tmp1, tmp2) - - return result -} - -func CalculateR(aR, sR, y, po2 []*big.Int, z, x *big.Int) []*big.Int { - if len(aR) != len(sR) || len(aR) != len(y) || len(y) != len(po2) { - log.Info("CalculateR: Arrays not of the same length") - } - - result := make([]*big.Int, len(aR)) - - z2 := new(big.Int).Exp(z, big.NewInt(2), EC.N) - tmp11 := VectorAddScalar(aR, z) - tmp12 := ScalarVectorMul(sR, x) - tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12)) - tmp2 := ScalarVectorMul(po2, z2) - - result = VectorAdd(tmp1, tmp2) - - return result -} - -// Calculates (aL - z*1^n) + sL*x -func CalculateLMRP(aL, sL []*big.Int, z, x *big.Int) []*big.Int { - result := make([]*big.Int, len(aL)) - - tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z)) - tmp2 := ScalarVectorMul(sL, x) - - result = VectorAdd(tmp1, tmp2) - - return result -} - -func CalculateRMRP(aR, sR, y, zTimesTwo []*big.Int, z, x *big.Int) []*big.Int { - if len(aR) != len(sR) || len(aR) != len(y) || len(y) != len(zTimesTwo) { - log.Info("CalculateRMRP: Arrays not of the same length") - } - - result := make([]*big.Int, len(aR)) - - tmp11 := VectorAddScalar(aR, z) - tmp12 := ScalarVectorMul(sR, x) - tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12)) - - result = VectorAdd(tmp1, zTimesTwo) - - return result -} - -/* -DeltaMRP is a helper function that is used in the multi range proof -\delta(y, z) = (z-z^2)<1^n, y^n> - \sum_j z^3+j<1^n, 2^n> -*/ - -func DeltaMRP(y []*big.Int, z *big.Int, m int) *big.Int { - result := big.NewInt(0) - - // (z-z^2)<1^n, y^n> - z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N) - t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N) - t2 := new(big.Int).Mod(new(big.Int).Mul(t1, VectorSum(y)), EC.N) - - // \sum_j z^3+j<1^n, 2^n> - // <1^n, 2^n> = 2^n - 1 - po2sum := new(big.Int).Sub( - new(big.Int).Exp( - big.NewInt(2), big.NewInt(int64(EC.V/m)), EC.N), big.NewInt(1)) - t3 := big.NewInt(0) - - for j := 0; j < m; j++ { - zp := new(big.Int).Exp(z, big.NewInt(3+int64(j)), EC.N) - tmp1 := new(big.Int).Mod(new(big.Int).Mul(zp, po2sum), EC.N) - t3 = new(big.Int).Mod(new(big.Int).Add(t3, tmp1), EC.N) - } - - result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) - - return result -} - -type MultiRangeProof struct { - Comms []ECPoint - A ECPoint - S ECPoint - T1 ECPoint - T2 ECPoint - Tau *big.Int - Th *big.Int - Mu *big.Int - IPP InnerProdArg - - // challenges - Cy *big.Int - Cz *big.Int - Cx *big.Int -} - -func serializePointArray(pa []ECPoint, serializeSize bool) []byte { - ret := []byte{} - - if serializeSize { - size := make([]byte, 4) - binary.BigEndian.PutUint32(size, uint32(len(pa))) - ret = append(ret, size[:]...) - } - - for i := 0; i < len(pa); i++ { - sp := SerializeCompressed(pa[i].toECPubKey()) - ret = append(ret, sp[:]...) - } - return ret -} - -func deserializePointArray(input []byte, numPoint uint32) ([]ECPoint, error) { - if len(input) <= 4 { - return []ECPoint{}, errors.New("input data invalid") - } - numPointSize := uint32(0) - if numPoint == 0 { - numPointSize = 4 - numPoint = binary.BigEndian.Uint32(input[0:4]) - } - if uint32(len(input)) < (numPointSize + 33*numPoint) { - return []ECPoint{}, errors.New("input data too short") - } - ret := make([]ECPoint, numPoint) - offset := numPointSize - for i := 0; i < int(numPoint); i++ { - compressed := DeserializeCompressed(curve, input[offset:offset+33]) - if compressed == nil { - return ret, errors.New("invalid input data") - } - ret[i] = *toECPoint(compressed) - offset += 33 - } - return ret, nil -} - -func (ipp *InnerProdArg) Serialize() []byte { - proof := []byte{} - - spa := serializePointArray(ipp.L, false) - proof = append(proof, spa[:]...) - - spa = serializePointArray(ipp.R, false) - proof = append(proof, spa[:]...) - - if ipp.A.Cmp(big.NewInt(0)) < 0 { - ipp.A.Mod(ipp.A, EC.N) - } - sp := PadTo32Bytes(ipp.A.Bytes()) - proof = append(proof, sp[:]...) - - sp = PadTo32Bytes(ipp.B.Bytes()) - proof = append(proof, sp[:]...) - - for i := 0; i < len(ipp.Challenges); i++ { - sp = PadTo32Bytes(ipp.Challenges[i].Bytes()) - proof = append(proof, sp[:]...) - } - - return proof -} - -func (ipp *InnerProdArg) Deserialize(proof []byte, numChallenges int) error { - if len(proof) <= 12 { - return errors.New("proof data too short") - } - offset := 0 - L, err := deserializePointArray(proof[:], uint32(numChallenges)-1) - if err != nil { - return err - } - ipp.L = append(ipp.L, L[:]...) - offset += len(L) * 33 - - R, err := deserializePointArray(proof[offset:], uint32(numChallenges)-1) - if err != nil { - return err - } - - ipp.R = append(ipp.R, R[:]...) - offset += len(R) * 33 - - if len(proof) <= offset+64+4 { - return errors.New("proof data too short") - } - - ipp.A = new(big.Int).SetBytes(proof[offset : offset+32]) - offset += 32 - ipp.B = new(big.Int).SetBytes(proof[offset : offset+32]) - offset += 32 - - if len(proof) <= (offset + 32*numChallenges) { - return errors.New("input data too short") - } - for i := 0; i < numChallenges; i++ { - ipp.Challenges = append(ipp.Challenges, new(big.Int).SetBytes(proof[offset:offset+32])) - offset += 32 - } - return nil -} - -func (mrp *MultiRangeProof) Serialize() []byte { - proof := []byte{} - - serializedPA := serializePointArray(mrp.Comms, true) - proof = append(proof, serializedPA[:]...) - - sp := SerializeCompressed(mrp.A.toECPubKey()) - proof = append(proof, sp[:]...) - - sp = SerializeCompressed(mrp.S.toECPubKey()) - proof = append(proof, sp[:]...) - - sp = SerializeCompressed(mrp.T1.toECPubKey()) - proof = append(proof, sp[:]...) - - sp = SerializeCompressed(mrp.T2.toECPubKey()) - proof = append(proof, sp[:]...) - - //Tau, Th, Mu - sp = PadTo32Bytes(mrp.Tau.Bytes()) - proof = append(proof, sp[:]...) - - if mrp.Th.Cmp(big.NewInt(0)) < 0 { - mrp.Th.Mod(mrp.Th, EC.N) - } - sp = PadTo32Bytes(mrp.Th.Bytes()) - proof = append(proof, sp[:]...) - - sp = PadTo32Bytes(mrp.Mu.Bytes()) - proof = append(proof, sp[:]...) - - //challenges - sp = mrp.IPP.Serialize() - proof = append(proof, sp[:]...) - - //challenges - sp = PadTo32Bytes(mrp.Cy.Bytes()) - proof = append(proof, sp[:]...) - - sp = PadTo32Bytes(mrp.Cz.Bytes()) - proof = append(proof, sp[:]...) - - sp = PadTo32Bytes(mrp.Cx.Bytes()) - proof = append(proof, sp[:]...) - - return proof -} - -func (mrp *MultiRangeProof) Deserialize(proof []byte) error { - Cs, err := deserializePointArray(proof[:], 0) - if err != nil { - return err - } - mrp.Comms = append(mrp.Comms, Cs[:]...) - - offset := 4 + len(Cs)*33 - - if len(proof) <= offset+4+4*33+6*32 { - return errors.New("invalid input data") - } - compressed := DeserializeCompressed(curve, proof[offset:offset+33]) - if compressed == nil { - return errors.New("failed to decode A") - } - offset += 33 - mrp.A = *toECPoint(compressed) - - compressed = DeserializeCompressed(curve, proof[offset:offset+33]) - if compressed == nil { - return errors.New("failed to decode S") - } - offset += 33 - mrp.S = *toECPoint(compressed) - - compressed = DeserializeCompressed(curve, proof[offset:offset+33]) - if compressed == nil { - return errors.New("failed to decode T2") - } - offset += 33 - mrp.T1 = *toECPoint(compressed) - - compressed = DeserializeCompressed(curve, proof[offset:offset+33]) - if compressed == nil { - return errors.New("failed to decode T2") - } - offset += 33 - mrp.T2 = *toECPoint(compressed) - - mrp.Tau = new(big.Int).SetBytes(proof[offset : offset+32]) - offset += 32 - - mrp.Th = new(big.Int).SetBytes(proof[offset : offset+32]) - offset += 32 - - mrp.Mu = new(big.Int).SetBytes(proof[offset : offset+32]) - offset += 32 - - numChallenges := int(math.Log2(float64(len(mrp.Comms)*bitsPerValue))) + 1 - mrp.IPP.Deserialize(proof[offset:], numChallenges) - offset += len(mrp.IPP.L)*33 + len(mrp.IPP.R)*33 + len(mrp.IPP.Challenges)*32 + 2*32 - - mrp.Cy = new(big.Int).SetBytes(proof[offset : offset+32]) - offset += 32 - - mrp.Cz = new(big.Int).SetBytes(proof[offset : offset+32]) - offset += 32 - - mrp.Cx = new(big.Int).SetBytes(proof[offset : offset+32]) - offset += 32 - - return nil -} - -func pedersenCommitment(gamma *big.Int, value *big.Int) ECPoint { - return EC.G.Mult(value).Add(EC.H.Mult(gamma)) -} - -var MAX_64_BITS = new(big.Int).SetUint64(0xFFFFFFFFFFFFFFFF) - -/* -MultiRangeProof Prove -Takes in a list of values and provides an aggregate -range proof for all the values. -changes: - - all values are concatenated - r(x) is computed differently - tau_x calculation is different - delta calculation is different - -{(g, h \in G, \textbf{V} \in G^m ; \textbf{v, \gamma} \in Z_p^m) : - - V_j = h^{\gamma_j}g^{v_j} \wedge v_j \in [0, 2^n - 1] \forall j \in [1, m]} -*/ -var bitsPerValue = 64 - -func MRPProve(values []*big.Int) (MultiRangeProof, error) { - var acceptedInputNumber bool - - MRPResult := MultiRangeProof{} - - m := len(values) - - if m == 1 || m == 2 || m == 4 || m == 8 { - acceptedInputNumber = true - } - - if !acceptedInputNumber { - return MultiRangeProof{}, errors.New("Value number is not supported - just 1, 2, 4, 8") - } - - EC = genECPrimeGroupKey(m * bitsPerValue) - - // we concatenate the binary representation of the values - - PowerOfTwos := PowerVector(bitsPerValue, big.NewInt(2)) - - Comms := make([]ECPoint, m) - gammas := make([]*big.Int, m) - aLConcat := make([]*big.Int, EC.V) - aRConcat := make([]*big.Int, EC.V) - - for j := range values { - v := values[j] - if v.Cmp(big.NewInt(0)) == -1 { - return MultiRangeProof{}, errors.New("Value is below range! Not proving") - } - - if v.Cmp(MAX_64_BITS) == 1 { - return MultiRangeProof{}, errors.New("Value is above range! Not proving") - } - - if v.Cmp(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bitsPerValue)), EC.N)) == 1 { - return MultiRangeProof{}, errors.New("Value is above range! Not proving") - } - - gamma, err := rand.Int(rand.Reader, EC.N) - - check(err) - Comms[j] = pedersenCommitment(gamma, v) - gammas[j] = gamma - - // break up v into its bitwise representation - aL := reverse(StrToBigIntArray(PadLeft(fmt.Sprintf("%b", v), "0", bitsPerValue))) - aR := VectorAddScalar(aL, big.NewInt(-1)) - - for i := range aR { - aLConcat[bitsPerValue*j+i] = aL[i] - aRConcat[bitsPerValue*j+i] = aR[i] - } - } - - //compare aL, aR - MRPResult.Comms = Comms - - alpha, err := rand.Int(rand.Reader, EC.N) - check(err) - - A := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, aLConcat, aRConcat).Add(EC.H.Mult(alpha)) - MRPResult.A = A - - // fmt.Println("Ec.V %+v", EC.V) - sL := RandVector(EC.V) - sR := RandVector(EC.V) - - rho, err := rand.Int(rand.Reader, EC.N) - check(err) - - S := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, sL, sR).Add(EC.H.Mult(rho)) - MRPResult.S = S - - // input := append(PadTo32Bytes(A.X.Bytes()), PadTo32Bytes(A.Y.Bytes())...) - // chal1s256 := crypto.Keccak256(input) - chal1s256 := HashPointsToBytes(append(Comms, A)) - - // chal1s256 := sha256.Sum256([]byte(A.X.String() + A.Y.String())) - cy := new(big.Int).SetBytes(chal1s256[:]) - MRPResult.Cy = cy - - // input = append(PadTo32Bytes(S.X.Bytes()), PadTo32Bytes(S.Y.Bytes())...) - // chal2s256 := crypto.Keccak256(input) - chal2s256 := HashPointsToBytes(append(Comms, A, S)) - - // chal2s256 := sha256.Sum256([]byte(S.X.String() + S.Y.String())) - cz := new(big.Int).SetBytes(chal2s256[:]) - MRPResult.Cz = cz - - zPowersTimesTwoVec := make([]*big.Int, EC.V) - for j := 0; j < m; j++ { - zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) - for i := 0; i < bitsPerValue; i++ { - zPowersTimesTwoVec[j*bitsPerValue+i] = new(big.Int).Mod(new(big.Int).Mul(PowerOfTwos[i], zp), EC.N) - } - } - - PowerOfCY := PowerVector(EC.V, cy) - // fmt.Println(PowerOfCY) - l0 := VectorAddScalar(aLConcat, new(big.Int).Neg(cz)) - l1 := sL - r0 := VectorAdd( - VectorHadamard( - PowerOfCY, - VectorAddScalar(aRConcat, cz)), - zPowersTimesTwoVec) - r1 := VectorHadamard(sR, PowerOfCY) - - //calculate t0 - vz2 := big.NewInt(0) - z2 := new(big.Int).Mod(new(big.Int).Mul(cz, cz), EC.N) - PowerOfCZ := PowerVector(m, cz) - for j := 0; j < m; j++ { - vz2 = new(big.Int).Add(vz2, - new(big.Int).Mul( - PowerOfCZ[j], - new(big.Int).Mul(values[j], z2))) - vz2 = new(big.Int).Mod(vz2, EC.N) - } - - t0 := new(big.Int).Mod(new(big.Int).Add(vz2, DeltaMRP(PowerOfCY, cz, m)), EC.N) - - t1 := new(big.Int).Mod(new(big.Int).Add(InnerProduct(l1, r0), InnerProduct(l0, r1)), EC.N) - t2 := InnerProduct(l1, r1) - - // given the t_i values, we can generate commitments to them - tau1, err := rand.Int(rand.Reader, EC.N) - check(err) - tau2, err := rand.Int(rand.Reader, EC.N) - check(err) - - T1 := pedersenCommitment(tau1, t1) // EC.G.Mult(t1).Add(EC.H.Mult(tau1)) //commitment to t1 - T2 := pedersenCommitment(tau2, t2) //EC.G.Mult(t2).Add(EC.H.Mult(tau2)) //commitment to t2 - - MRPResult.T1 = T1 - MRPResult.T2 = T2 - - // t1Byte := append(PadTo32Bytes(T1.X.Bytes()), PadTo32Bytes(T1.Y.Bytes())...) - // t2Byte := append(PadTo32Bytes(T2.X.Bytes()), PadTo32Bytes(T2.Y.Bytes())...) - // input = append(t1Byte, t2Byte...) - // chal3s256 := crypto.Keccak256(input) - chal3s256 := HashPointsToBytes(append(Comms, A, S, T1, T2)) - - // chal3s256 := sha256.Sum256([]byte(T1.X.String() + T1.Y.String() + T2.X.String() + T2.Y.String())) - cx := new(big.Int).SetBytes(chal3s256[:]) - - MRPResult.Cx = cx - - left := CalculateLMRP(aLConcat, sL, cz, cx) - right := CalculateRMRP(aRConcat, sR, PowerOfCY, zPowersTimesTwoVec, cz, cx) - - thatPrime := new(big.Int).Mod( // t0 + t1*x + t2*x^2 - new(big.Int).Add(t0, new(big.Int).Add(new(big.Int).Mul(t1, cx), new(big.Int).Mul(new(big.Int).Mul(cx, cx), t2))), EC.N) - - that := InnerProduct(left, right) // NOTE: BP Java implementation calculates this from the t_i - - // thatPrime and that should be equal - if thatPrime.Cmp(that) != 0 { - fmt.Println("Proving -- Uh oh! Two diff ways to compute same value not working") - fmt.Printf("\tthatPrime = %s\n", thatPrime.String()) - fmt.Printf("\tthat = %s \n", that.String()) - } - - MRPResult.Th = that - - vecRandomnessTotal := big.NewInt(0) - for j := 0; j < m; j++ { - zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) - tmp1 := new(big.Int).Mul(gammas[j], zp) - vecRandomnessTotal = new(big.Int).Mod(new(big.Int).Add(vecRandomnessTotal, tmp1), EC.N) - } - //fmt.Println(vecRandomnessTotal) - taux1 := new(big.Int).Mod(new(big.Int).Mul(tau2, new(big.Int).Mul(cx, cx)), EC.N) - taux2 := new(big.Int).Mod(new(big.Int).Mul(tau1, cx), EC.N) - taux := new(big.Int).Mod(new(big.Int).Add(taux1, new(big.Int).Add(taux2, vecRandomnessTotal)), EC.N) - - MRPResult.Tau = taux - - mu := new(big.Int).Mod(new(big.Int).Add(alpha, new(big.Int).Mul(rho, cx)), EC.N) - MRPResult.Mu = mu - - HPrime := make([]ECPoint, len(EC.BPH)) - - for i := range HPrime { - HPrime[i] = EC.BPH[i].Mult(new(big.Int).ModInverse(PowerOfCY[i], EC.N)) - } - - P := TwoVectorPCommitWithGens(EC.BPG, HPrime, left, right) - //fmt.Println(P) - - MRPResult.IPP = InnerProductProve(left, right, that, P, EC.U, EC.BPG, HPrime) - - return MRPResult, nil -} - -/* -MultiRangeProof Verify -Takes in a MultiRangeProof and verifies its correctness -*/ -func MRPVerify(mrp *MultiRangeProof) bool { - m := len(mrp.Comms) - EC = genECPrimeGroupKey(m * bitsPerValue) - - //changes: - // check 1 changes since it includes all commitments - // check 2 commitment generation is also different - - // verify the challenges - // input := append(PadTo32Bytes(mrp.A.X.Bytes()), PadTo32Bytes(mrp.A.Y.Bytes())...) - // chal1s256 := crypto.Keccak256(input) - chal1s256 := HashPointsToBytes(append(mrp.Comms, mrp.A)) - - cy := new(big.Int).SetBytes(chal1s256[:]) - - if cy.Cmp(mrp.Cy) != 0 { - log.Debug("MRPVerify challenge failed!", "Cy", common.Bytes2Hex(mrp.Cy.Bytes())) - return false - } - - // input = append(PadTo32Bytes(mrp.S.X.Bytes()), PadTo32Bytes(mrp.S.Y.Bytes())...) - // chal2s256 := crypto.Keccak256(input) - chal2s256 := HashPointsToBytes(append(mrp.Comms, mrp.A, mrp.S)) - - // chal2s256 := sha256.Sum256([]byte(mrp.S.X.String() + mrp.S.Y.String())) - cz := new(big.Int).SetBytes(chal2s256[:]) - if cz.Cmp(mrp.Cz) != 0 { - log.Debug("MRPVerify challenge failed!", "Cz", common.Bytes2Hex(mrp.Cz.Bytes())) - return false - } - - // t1Byte := append(PadTo32Bytes(mrp.T1.X.Bytes()), PadTo32Bytes(mrp.T1.Y.Bytes())...) - // t2Byte := append(PadTo32Bytes(mrp.T2.X.Bytes()), PadTo32Bytes(mrp.T2.Y.Bytes())...) - // input = append(t1Byte, t2Byte...) - // chal3s256 := crypto.Keccak256(input) - chal3s256 := HashPointsToBytes(append(mrp.Comms, mrp.A, mrp.S, mrp.T1, mrp.T2)) - - // chal3s256 := sha256.Sum256([]byte(T1.X.String() + T1.Y.String() + T2.X.String() + T2.Y.String())) - // cx := new(big.Int).SetBytes(chal3s256[:]) - //chal3s256 := sha256.Sum256([]byte(mrp.T1.X.String() + mrp.T1.Y.String() + mrp.T2.X.String() + mrp.T2.Y.String())) - - cx := new(big.Int).SetBytes(chal3s256[:]) - - if cx.Cmp(mrp.Cx) != 0 { - log.Debug("MRPVerify challenge failed!", "Cx", common.Bytes2Hex(mrp.Cx.Bytes())) - return false - } - - // given challenges are correct, very range proof - PowersOfY := PowerVector(EC.V, cy) - - // t_hat * G + tau * H - lhs := pedersenCommitment(mrp.Tau, mrp.Th) //EC.G.Mult(mrp.Th).Add(EC.H.Mult(mrp.Tau)) - - // z^2 * \bold{z}^m \bold{V} + delta(y,z) * G + x * T1 + x^2 * T2 - CommPowers := EC.Zero() - PowersOfZ := PowerVector(m, cz) - z2 := new(big.Int).Mod(new(big.Int).Mul(cz, cz), EC.N) - - for j := 0; j < m; j++ { - CommPowers = CommPowers.Add(mrp.Comms[j].Mult(new(big.Int).Mul(z2, PowersOfZ[j]))) - } - - // TODO i need to change how to calculate the commitment here also ? - // need to compare and double check with privacy-client lib - rhs := EC.G.Mult(DeltaMRP(PowersOfY, cz, m)).Add( - mrp.T1.Mult(cx)).Add( - mrp.T2.Mult(new(big.Int).Mul(cx, cx))).Add(CommPowers) - - if !lhs.Equal(rhs) { - log.Debug("Rangeproof failed") - return false - } - - tmp1 := EC.Zero() - zneg := new(big.Int).Mod(new(big.Int).Neg(cz), EC.N) - for i := range EC.BPG { - tmp1 = tmp1.Add(EC.BPG[i].Mult(zneg)) - } - - PowerOfTwos := PowerVector(bitsPerValue, big.NewInt(2)) - tmp2 := EC.Zero() - // generate h' - HPrime := make([]ECPoint, len(EC.BPH)) - - for i := range HPrime { - mi := new(big.Int).ModInverse(PowersOfY[i], EC.N) - HPrime[i] = EC.BPH[i].Mult(mi) - } - - for j := 0; j < m; j++ { - for i := 0; i < bitsPerValue; i++ { - val1 := new(big.Int).Mul(cz, PowersOfY[j*bitsPerValue+i]) - zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) - val2 := new(big.Int).Mod(new(big.Int).Mul(zp, PowerOfTwos[i]), EC.N) - tmp2 = tmp2.Add(HPrime[j*bitsPerValue+i].Mult(new(big.Int).Add(val1, val2))) - } - } - - // without subtracting this value should equal muCH + l[i]G[i] + r[i]H'[i] - // we want to make sure that the innerproduct checks out, so we subtract it - P := mrp.A.Add(mrp.S.Mult(cx)).Add(tmp1).Add(tmp2).Add(EC.H.Mult(mrp.Mu).Neg()) - //fmt.Println(P) - - if !InnerProductVerifyFast(mrp.Th, P, EC.U, EC.BPG, HPrime, mrp.IPP) { - log.Debug("Range proof failed!") - return false - } - - return true -} - -// NewECPrimeGroupKey returns the curve (field), -// Generator 1 x&y, Generator 2 x&y, order of the generators -func NewECPrimeGroupKey(n int) CryptoParams { - curValue := btcec.S256().Gx - s256 := sha256.New() - gen1Vals := make([]ECPoint, n) - gen2Vals := make([]ECPoint, n) - u := ECPoint{big.NewInt(0), big.NewInt(0)} - cg := ECPoint{} - ch := ECPoint{} - - j := 0 - confirmed := 0 - for confirmed < (2*n + 3) { - s256.Write(new(big.Int).Add(curValue, big.NewInt(int64(j))).Bytes()) - - potentialXValue := make([]byte, 33) - binary.LittleEndian.PutUint32(potentialXValue, 2) - for i, elem := range s256.Sum(nil) { - potentialXValue[i+1] = elem - } - - gen2, err := btcec.ParsePubKey(potentialXValue, btcec.S256()) - if err == nil { - if confirmed == 2*n { // once we've generated all g and h values then assign this to u - u = ECPoint{gen2.X, gen2.Y} - //fmt.Println("Got that U value") - } else if confirmed == 2*n+1 { - cg = ECPoint{gen2.X, gen2.Y} - - } else if confirmed == 2*n+2 { - ch = ECPoint{gen2.X, gen2.Y} - } else { - if confirmed%2 == 0 { - gen1Vals[confirmed/2] = ECPoint{gen2.X, gen2.Y} - //fmt.Println("new G Value") - } else { - gen2Vals[confirmed/2] = ECPoint{gen2.X, gen2.Y} - //fmt.Println("new H value") - } - } - confirmed += 1 - } - j += 1 - } - - return CryptoParams{ - btcec.S256(), - btcec.S256(), - gen1Vals, - gen2Vals, - btcec.S256().N, - u, - n, - cg, - ch} -} - -func genECPrimeGroupKey(n int) CryptoParams { - // curValue := btcec.S256().Gx - // s256 := sha256.New() - gen1Vals := make([]ECPoint, n) - gen2Vals := make([]ECPoint, n) - // u := ECPoint{big.NewInt(0), big.NewInt(0)} - hx, _ := new(big.Int).SetString("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0", 16) - hy, _ := new(big.Int).SetString("31d3c6863973926e049e637cb1b5f40a36dac28af1766968c30c2313f3a38904", 16) - ch := ECPoint{hx, hy} - - gx, _ := new(big.Int).SetString("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16) - gy, _ := new(big.Int).SetString("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16) - cg := ECPoint{gx, gy} - - i := 0 - for i < n { - gen2Vals[i] = ch.Mult( - big.NewInt(int64(i*2 + 1)), - ) - gen1Vals[i] = cg.Mult( - big.NewInt(int64(i*2 + 2)), - ) - i++ - } - - u := cg.Mult( - big.NewInt(int64(n + 3)), - ) - - return CryptoParams{ - btcec.S256(), - btcec.S256(), - gen1Vals, - gen2Vals, - btcec.S256().N, - u, - n, - ch, - cg} -} - -func init() { - // just need the base parameter N, P, G, H for this init, ignore everything else - EC = CryptoParams{ - btcec.S256(), - btcec.S256(), - make([]ECPoint, VecLength), - make([]ECPoint, VecLength), - btcec.S256().N, - ECPoint{big.NewInt(0), big.NewInt(0)}, - VecLength, - ECPoint{big.NewInt(0), big.NewInt(0)}, - ECPoint{big.NewInt(0), big.NewInt(0)}} -} diff --git a/core/vm/privacy/bulletproof.json b/core/vm/privacy/bulletproof.json deleted file mode 100644 index 263dd7927099..000000000000 --- a/core/vm/privacy/bulletproof.json +++ /dev/null @@ -1 +0,0 @@ -{"Ipp":{"L":[{"x":"7be22788517d8c7e149727c5397cf7b2d6713546dee4f974f3239a1f53e64f44","y":"b18c08695e40b801ace64f00bf7010245dcf972cad7ce1e97e441fa68575bb72"},{"x":"29956e85820c1ede4f6ec987b3c20cbf7c0fc0aa6a8cd823e73105fc681c1880","y":"228e434578a40d247a2b5f1c2a7a5154716fb06e98d8087b3dce9cc85429b72c"},{"x":"b39703b5680857d7629a39374451baae59459418c23f44f522838efd952e69db","y":"60033aa9807a0c081c530c30292eca71f61e17433513ead9caadce7997d97981"},{"x":"074cd428559b2bf9c0a6d54479254bdeedc26e655b7f0d822aec9a6d1f1068b3","y":"915958a8e24fefe8ec5e6338661567c8e6178d03ac35422284701bf2ab37d592"},{"x":"80d27e070c49f6d41d49254295a9e70ebaf82d8780c93f1c48f018609e3822dd","y":"5c5e365b72835ff1e24ff3a130e1cc332e36d891ba383752e469a667ecf5db01"},{"x":"6e9c63c3188c29d593417b5cb96ab0f149d905a194ace7a3e523d15f8f8922cd","y":"a91566f2cae911542a0658f63451b4a87cea5c668ab4220c974a640442021da6"}],"R":[{"x":"34f512b1fe0989ce77f8cb5b3a95dd31b55c38d4b6278e4923d6cf224d59c421","y":"8757d403eb39a181418db28f742f0ff3f59991929800c424ebce792f27353335"},{"x":"900bc03a4e26a61bb11de434ee150970a22dce77320b26fea1b172dbdfef5b10","y":"de9d1b833decc36f84ae4ce1be87414674ebef7b58009563f35a722347eb76d7"},{"x":"0ca13ff9d0a929b417a165ceb42c532dd54c34e8f1e9083208630dadafdb5ee4","y":"0bba4e3ac3b20cde8074d4683742f924bdb070a590c1d9d7c8f239748359015c"},{"x":"c29728cbb8393b65f8f224e587eccee9d332d2936b36bb460a9f9df720ef5add","y":"88295fbbe05f25534a43ac528a890b9a26ab3bf95a63e4803168d56b430190ce"},{"x":"4cd804793a0169e0bfe1206907cec530a733efb7cca86f08bff8d04bfb82394b","y":"453594ec2090934e75348db0db070c79b6302cf289d1907adbc198493fee417e"},{"x":"9d9a42ba4b30cc68841ce6528f0d4ffa7990b35fc533f7cb0e4bf0d26cc9c6b6","y":"d6dbdb179262c2bc1fc734d4e20a2c54c5e0b8b8d0c48ee630e146f4cef1f486"}],"A":"-2d23144bda297742e1eafb40911ccdbf2f5e950e8b73b4eaf8f78b857d5c7a93","B":"162325c6d1160e0c033135e32b57351c17ef79755cf4f19c6d6c0ba8117e8d09","Challenges":["139fb4a37d387933e43987862d67297c65af5302c6995bfe7181646dcfc169f1","d43aa9b8fdd97b12e3c4bfabf4c36c59dabb08ace9d5da221f676cc7244000e0","3013fdc8605a4dd479898c2b9abbab45ed9b9676267d8c86a0b7b737448c3d8e","b7a16a537783cd7a9e5bddad4536754a6b16a152fedd2df46a36f76a51cd66d9","5408a38e71aa569b6ddc2ccc84a3b2fa29253905072bf8ed5035406cda89e6fa","1ab61f234a5982567e51af82efd118ebcb44befabfaa5f07183471508e3f4208","3e666d903ccd1312a9a83506b298dfac2c2f11f796087d91104487a577802faa"]},"Comms":["71a29edddcf580c0ab4c992f34df4bcd7f23079bf29c07267e354c0e1b0c7b7d0d7d7f55259df2570dc37a2b6719eb58b2195f06163d29b7aca710cbf9b66560"],"A":"e7d28764b37d275ee1b0cc589ab2aa46cc60a406d6cb0def47dcb5fc49b3279dc65b2eb160328db9dc5953a263a5eba0855b51c66a5e23ac0a3b997d060643ed","S":"78aa79b16298d8c7cc587c8fe521d13b9c768256a8842e74e82970507efea3b9735e4324787071ded1e049c888aee40fe85cff0fff4ffdc4d2d3dc2a17ac306a","cy":"3e38e29bf805dc855b687f969a1a854a0c23fddb8ff1f480079929518e8df1f6","cz":"d794749e1c1442762943a40422a6f9bd284f938fce652c382fea05a0e33ca68f","T1":"45e56a5bc134b7b19d53670d57b45874a01442b58a853dd0960f4a7ec1a093f59be5d93e35a55f2006f03cf9c31801942d9a8739a9408c67d93c4dff6c9f7346","T2":"ad380e6564ae44cd6f91656e66c031cfe21aa47cc511ae51e4a2a8604bd181cd181be8eb1b8e0de71458eed4ce538667a392cc0e7c4e85ab7f4cff54161c8087","cx":"75327f647ab5124eefa92e51f769cdb64a24fe5f88a059b002f2d590c9236894","Th":"-ee380a27a0b95643f9a3a5a975c9b35638ba2a7105a36c7ffd90acdd16029323","Tau":"38059dcf3dfc1da9646f3dc66c8b1487c3375577e751e8341b2addd45ef06306","Mu":"e648b63a1857d1e9723b01ac3151c29fd723845f8aa2a7d6ae867feba8db53e5"} \ No newline at end of file diff --git a/core/vm/privacy/bulletproof_test.go b/core/vm/privacy/bulletproof_test.go deleted file mode 100644 index 8088ac98e9d3..000000000000 --- a/core/vm/privacy/bulletproof_test.go +++ /dev/null @@ -1,527 +0,0 @@ -package privacy - -import ( - "crypto/rand" - "encoding/json" - "fmt" - "io" - "math/big" - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestInnerProductProveLen1(t *testing.T) { - fmt.Println("TestInnerProductProve1") - EC = genECPrimeGroupKey(1) - a := make([]*big.Int, 1) - b := make([]*big.Int, 1) - - a[0] = big.NewInt(1) - - b[0] = big.NewInt(1) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - } -} - -func TestInnerProductProveLen2(t *testing.T) { - fmt.Println("TestInnerProductProve2") - EC = genECPrimeGroupKey(2) - a := make([]*big.Int, 2) - b := make([]*big.Int, 2) - - a[0] = big.NewInt(1) - a[1] = big.NewInt(1) - - b[0] = big.NewInt(1) - b[1] = big.NewInt(1) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - fmt.Println("P after two vector commitment with gen ", P) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - } -} - -func TestInnerProductProveLen4(t *testing.T) { - fmt.Println("TestInnerProductProve4") - EC = genECPrimeGroupKey(4) - a := make([]*big.Int, 4) - b := make([]*big.Int, 4) - - a[0] = big.NewInt(1) - a[1] = big.NewInt(1) - a[2] = big.NewInt(1) - a[3] = big.NewInt(1) - - b[0] = big.NewInt(1) - b[1] = big.NewInt(1) - b[2] = big.NewInt(1) - b[3] = big.NewInt(1) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - } -} - -func TestInnerProductProveLen8(t *testing.T) { - fmt.Println("TestInnerProductProve8") - EC = genECPrimeGroupKey(8) - a := make([]*big.Int, 8) - b := make([]*big.Int, 8) - - a[0] = big.NewInt(1) - a[1] = big.NewInt(1) - a[2] = big.NewInt(1) - a[3] = big.NewInt(1) - a[4] = big.NewInt(1) - a[5] = big.NewInt(1) - a[6] = big.NewInt(1) - a[7] = big.NewInt(1) - - b[0] = big.NewInt(2) - b[1] = big.NewInt(2) - b[2] = big.NewInt(2) - b[3] = big.NewInt(2) - b[4] = big.NewInt(2) - b[5] = big.NewInt(2) - b[6] = big.NewInt(2) - b[7] = big.NewInt(2) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - } -} - -func TestInnerProductProveLen64Rand(t *testing.T) { - fmt.Println("TestInnerProductProveLen64Rand") - EC = genECPrimeGroupKey(64) - a := RandVector(64) - b := RandVector(64) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - fmt.Printf("Values Used: \n\ta = %s\n\tb = %s\n", a, b) - } - -} - -func TestInnerProductVerifyFastLen1(t *testing.T) { - fmt.Println("TestInnerProductProve1") - EC = genECPrimeGroupKey(1) - a := make([]*big.Int, 1) - b := make([]*big.Int, 1) - - a[0] = big.NewInt(2) - - b[0] = big.NewInt(2) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - } -} - -func TestInnerProductVerifyFastLen2(t *testing.T) { - fmt.Println("TestInnerProductProve2") - EC = genECPrimeGroupKey(2) - a := make([]*big.Int, 2) - b := make([]*big.Int, 2) - - a[0] = big.NewInt(2) - a[1] = big.NewInt(3) - - b[0] = big.NewInt(2) - b[1] = big.NewInt(3) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - } -} - -func TestInnerProductVerifyFastLen4(t *testing.T) { - fmt.Println("TestInnerProductProve4") - EC = genECPrimeGroupKey(4) - a := make([]*big.Int, 4) - b := make([]*big.Int, 4) - - a[0] = big.NewInt(1) - a[1] = big.NewInt(1) - a[2] = big.NewInt(1) - a[3] = big.NewInt(1) - - b[0] = big.NewInt(1) - b[1] = big.NewInt(1) - b[2] = big.NewInt(1) - b[3] = big.NewInt(1) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - } -} - -func TestInnerProductVerifyFastLen8(t *testing.T) { - fmt.Println("TestInnerProductProve8") - EC = genECPrimeGroupKey(8) - a := make([]*big.Int, 8) - b := make([]*big.Int, 8) - - a[0] = big.NewInt(1) - a[1] = big.NewInt(1) - a[2] = big.NewInt(1) - a[3] = big.NewInt(1) - a[4] = big.NewInt(1) - a[5] = big.NewInt(1) - a[6] = big.NewInt(1) - a[7] = big.NewInt(1) - - b[0] = big.NewInt(2) - b[1] = big.NewInt(2) - b[2] = big.NewInt(2) - b[3] = big.NewInt(2) - b[4] = big.NewInt(2) - b[5] = big.NewInt(2) - b[6] = big.NewInt(2) - b[7] = big.NewInt(2) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - } -} - -func TestInnerProductVerifyFastLen64Rand(t *testing.T) { - fmt.Println("TestInnerProductProveLen64Rand") - EC = genECPrimeGroupKey(64) - a := RandVector(64) - b := RandVector(64) - - c := InnerProduct(a, b) - - P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) - - ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) - - if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { - fmt.Println("Inner Product Proof correct") - } else { - t.Error("Inner Product Proof incorrect") - fmt.Printf("Values Used: \n\ta = %s\n\tb = %s\n", a, b) - } - -} - -func TestMRPProveZERO(t *testing.T) { - - mRangeProof, _ := MRPProve([]*big.Int{ - new(big.Int).SetInt64(0), - }) - mv := MRPVerify(&mRangeProof) - assert.Equal(t, mv, true, " MRProof incorrect") -} - -func TestMRPProve_MAX_2_POW_64(t *testing.T) { - - mRangeProof, _ := MRPProve([]*big.Int{ - new(big.Int).SetUint64(0xFFFFFFFFFFFFFFFF), - }) - mv := MRPVerify(&mRangeProof) - assert.Equal(t, mv, true, " MRProof incorrect") -} - -func TestMRPProveOutOfSupportedRange(t *testing.T) { - - value, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFF", 16) - _, err := MRPProve([]*big.Int{ - value, - }) - assert.NotNil(t, err, " MRProof incorrect") -} - -func TestMRPProve_RANDOM(t *testing.T) { - - mRangeProof, _ := MRPProve(Rand64Vector(1)) - mv := MRPVerify(&mRangeProof) - assert.Equal(t, mv, true, " MRProof incorrect") - - mRangeProof, _ = MRPProve(Rand64Vector(2)) - mv = MRPVerify(&mRangeProof) - assert.Equal(t, mv, true, " MRProof incorrect") - - mRangeProof, _ = MRPProve(Rand64Vector(4)) - mv = MRPVerify(&mRangeProof) - assert.Equal(t, mv, true, " MRProof incorrect") - - mRangeProof, _ = MRPProve(Rand64Vector(8)) - mv = MRPVerify(&mRangeProof) - assert.Equal(t, mv, true, " MRProof incorrect") -} - -func Rand64Vector(l int) []*big.Int { - result := make([]*big.Int, l) - - for i := 0; i < l; i++ { - x, err := rand.Int(rand.Reader, big.NewInt(0xFFFFFFFFFFFFFFF)) - check(err) - result[i] = x - } - - return result -} - -func TestMRPProveValueNumberNotSupported(t *testing.T) { - - _, err := MRPProve(Rand64Vector(3)) - assert.NotNil(t, err, "MRProof incorrect - accepted 3 inputs") - - _, err = MRPProve(Rand64Vector(5)) - assert.NotNil(t, err, "MRProof incorrect - accepted 5 inputs") - - _, err = MRPProve(Rand64Vector(6)) - assert.NotNil(t, err, "MRProof incorrect - accepted 6 inputs") - - _, err = MRPProve(Rand64Vector(7)) - assert.NotNil(t, err, "MRProof incorrect - accepted 7 inputs") - - _, err = MRPProve(Rand64Vector(10)) - assert.NotNil(t, err, "MRProof incorrect - accepted 10 inputs") - - _, err = MRPProve(Rand64Vector(1)) - assert.Nil(t, err, "MRProof incorrect - not accepted 1 inputs") - - _, err = MRPProve(Rand64Vector(2)) - assert.Nil(t, err, "MRProof incorrect - not accepted 2 inputs") - - _, err = MRPProve(Rand64Vector(4)) - fmt.Println(err) - assert.Nil(t, err, "MRProof incorrect - not accepted 4 inputs") - - _, err = MRPProve(Rand64Vector(8)) - assert.Nil(t, err, "MRProof incorrect - not accepted 8 inputs") -} - -type Point struct { - x string - y string -} - -type IPP struct { - L []map[string]string `json:"L"` - R []map[string]string `json:"R"` - A string `json:"A"` - B string `json:"B"` - Challenges []string `json:"Challenges"` -} - -type BulletProof struct { - Comms []string `json:"Comms"` - A string `json:"A"` - S string `json:"S"` - Cx string `json:"Cx"` - Cy string `json:"Cy"` - Cz string `json:"Cz"` - T1 string `json:"T1"` - T2 string `json:"T2"` - Th string `json:"Th"` - Tau string `json:"Tau"` - Mu string `json:"Mu"` - Ipp IPP `json:"Ipp"` -} - -func parseTestData(filePath string) MultiRangeProof { - jsonFile, err := os.Open(filePath) - - if err != nil { - fmt.Println(err) - } - - defer jsonFile.Close() - - byteValue, _ := io.ReadAll(jsonFile) - - // we initialize our Users array - // var result map[string]interface{} - result := BulletProof{} - - json.Unmarshal([]byte(byteValue), &result) - - fmt.Println("result ", result.Tau) - fmt.Println("result ", result.Th) - fmt.Println("result.Ipp ", result.Ipp) - - ipp := result.Ipp - - proof := MultiRangeProof{ - Comms: MapECPointFromHex(result.Comms, ECPointFromHex), - A: ECPointFromHex(result.A), - S: ECPointFromHex(result.S), - T1: ECPointFromHex(result.T1), - T2: ECPointFromHex(result.T2), - Th: bigIFromHex(result.Th), - Tau: bigIFromHex(result.Tau), - Mu: bigIFromHex(result.Mu), - Cx: bigIFromHex(result.Cx), - Cy: bigIFromHex(result.Cy), - Cz: bigIFromHex(result.Cz), - IPP: InnerProdArg{ - L: MapECPoint(ipp.L, ECPointFromPoint), - R: MapECPoint(ipp.R, ECPointFromPoint), - A: bigIFromHex(ipp.A), - B: bigIFromHex(ipp.B), - Challenges: MapBigI(ipp.Challenges, bigIFromHex), - }, - } - - fmt.Println(proof) - return proof -} - -/* -* -Utils for parsing data from json -*/ -func MapBigI(list []string, f func(string) *big.Int) []*big.Int { - result := make([]*big.Int, len(list)) - - for i, item := range list { - result[i] = f(item) - } - return result -} - -func MapECPointFromHex(list []string, f func(string) ECPoint) []ECPoint { - result := make([]ECPoint, len(list)) - - for i, item := range list { - result[i] = f(item) - } - return result -} - -func MapECPoint(list []map[string]string, f func(Point) ECPoint) []ECPoint { - result := make([]ECPoint, len(list)) - - for i, item := range list { - result[i] = f(Point{ - x: item["x"], - y: item["y"], - }) - } - return result -} - -func bigIFromHex(hex string) *big.Int { - tmp, _ := new(big.Int).SetString(hex, 16) - return tmp -} - -func ECPointFromHex(hex string) ECPoint { - Px, _ := new(big.Int).SetString(hex[:64], 16) - Py, _ := new(big.Int).SetString(hex[64:], 16) - P := ECPoint{Px, Py} - return P -} - -func ECPointFromPoint(ecpoint Point) ECPoint { - Px, _ := new(big.Int).SetString(ecpoint.x, 16) - Py, _ := new(big.Int).SetString(ecpoint.y, 16) - P := ECPoint{Px, Py} - return P -} - -func TestMRPGeneration(t *testing.T) { - values := make([]*big.Int, 2) - values[0] = big.NewInt(1000) - values[1] = big.NewInt(100000) - mrp, err := MRPProve(values) - if err != nil { - t.Error("failed to generate bulletproof") - } - - v := MRPVerify(&mrp) - serilizedBp := mrp.Serialize() - - newMRP := new(MultiRangeProof) - if newMRP.Deserialize(serilizedBp) != nil { - t.Error("failed to deserialized bulletproof") - } - - v = v && MRPVerify(newMRP) - - if !v { - t.Error("failed to verify bulletproof") - } -} diff --git a/core/vm/privacy/ringct.go b/core/vm/privacy/ringct.go deleted file mode 100644 index 93c97c56cee1..000000000000 --- a/core/vm/privacy/ringct.go +++ /dev/null @@ -1,632 +0,0 @@ -package privacy - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "encoding/binary" - "errors" - "fmt" - "math/big" - - "github.com/XinFinOrg/XDPoSChain/common" - - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" - - "github.com/XinFinOrg/XDPoSChain/log" -) - -// These constants define the lengths of serialized public keys. -const ( - PubKeyBytesLenCompressed = 33 - PubKeyBytesLenUncompressed = 65 - PubKeyBytesLenHybrid = 65 -) - -const ( - pubkeyCompressed byte = 0x2 // y_bit + x coord - pubkeyUncompressed byte = 0x4 // x coord + y coord - pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord -) - -// The proof contains pretty much stuffs -// The proof contains pretty much stuffs -// Ring size rs: 1 byte => proof[0] -// num input: number of real inputs: 1 byte => proof[1] -// List of inputs/UTXO index typed uint64 => total size = rs * numInput * 8 = proof[0]*proof[1]*8 -// List of key images: total size = numInput * 33 = proof[1] * 33 -// number of output n: 1 byte -// List of output => n * 130 bytes -// transaction fee: uint256 => 32 byte -// ringCT proof size ctSize: uint16 => 2 byte -// ringCT proof: ctSize bytes -// bulletproofs: bp -type PrivateSendVerifier struct { - proof []byte - //ringCT RingCT -} - -type Ring []*ecdsa.PublicKey - -type RingSignature struct { - NumRing int - Size int // size of ring - M [32]byte // message - C *big.Int // ring signature value, 1 element - S [][]*big.Int // ring signature values: [NumRing][Size] - Ring []Ring // array of rings of pubkeys: [NumRing] - I []*ecdsa.PublicKey // key images, size = the number of rings [NumRing] - Curve elliptic.Curve - SerializedRing []byte //temporary memory stored the raw ring ct used in case of verifying ringCT with message verification -} - -func (p *PrivateSendVerifier) verify() bool { - return false -} - -func (p *PrivateSendVerifier) deserialize() { - -} - -// helper function, returns type of v -func typeof(v interface{}) string { - return fmt.Sprintf("%T", v) -} - -func isOdd(a *big.Int) bool { - return a.Bit(0) == 1 -} - -// SerializeCompressed serializes a public key in a 33-byte compressed format. -func SerializeCompressed(p *ecdsa.PublicKey) []byte { - b := make([]byte, 0, PubKeyBytesLenCompressed) - format := pubkeyCompressed - if isOdd(p.Y) { - format |= 0x1 - } - b = append(b, format) - return append(b, PadTo32Bytes(p.X.Bytes())...) -} - -func DeserializeCompressed(curve elliptic.Curve, b []byte) *ecdsa.PublicKey { - x := new(big.Int).SetBytes(b[1:33]) - // Y = +-sqrt(x^3 + B) - x3 := new(big.Int).Mul(x, x) - x3.Mul(x3, x) - x3.Add(x3, curve.Params().B) - - // now calculate sqrt mod p of x2 + B - // This code used to do a full sqrt based on tonelli/shanks, - // but this was replaced by the algorithms referenced in - // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 - PPlus1Div4 := new(big.Int).Add(curve.Params().P, big.NewInt(1)) - PPlus1Div4 = PPlus1Div4.Div(PPlus1Div4, big.NewInt(4)) - y := new(big.Int).Exp(x3, PPlus1Div4, curve.Params().P) - ybit := b[0]%2 == 1 - if ybit != isOdd(y) { - y.Sub(curve.Params().P, y) - } - if ybit != isOdd(y) { - return nil - } - return &ecdsa.PublicKey{curve, x, y} -} - -// bytes returns the public key ring as a byte slice. -func (r Ring) Bytes() (b []byte) { - for _, pub := range r { - b = append(b, PadTo32Bytes(pub.X.Bytes())...) - b = append(b, PadTo32Bytes(pub.Y.Bytes())...) - } - return -} - -func PadTo32Bytes(in []byte) []byte { - padded := make([]byte, 32) - if len(in) >= 32 { - copy(padded, in) - } else { - copy(padded[32-len(in):], in) - } - - return padded -} - -// converts the signature to a byte array -// this is the format that will be used when passing EVM bytecode -func (r *RingSignature) Serialize() ([]byte, error) { - sig := []byte{} - // add size and message - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(r.NumRing)) - sig = append(sig, b[:]...) // 8 bytes - - b = make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(r.Size)) - sig = append(sig, b[:]...) // 8 bytes - - sig = append(sig, PadTo32Bytes(r.M[:])...) // 32 bytes - sig = append(sig, PadTo32Bytes(r.C.Bytes())...) // 32 bytes - - for k := 0; k < r.NumRing; k++ { - // 96 bytes each iteration - for i := 0; i < r.Size; i++ { - sig = append(sig, PadTo32Bytes(r.S[k][i].Bytes())...) - } - } - for k := 0; k < r.NumRing; k++ { - // 96 bytes each iteration - for i := 0; i < r.Size; i++ { - rb := SerializeCompressed(r.Ring[k][i]) - sig = append(sig, rb...) - } - } - - for k := 0; k < r.NumRing; k++ { - // 64 bytes - rb := SerializeCompressed(r.I[k]) - sig = append(sig, rb...) - } - - if len(sig) != 8+8+32+32+(32+33)*r.NumRing*r.Size+33*r.NumRing { - return []byte{}, errors.New("Could not serialize ring signature") - } - - return sig, nil -} - -func computeSignatureSize(numRing int, ringSize int) int { - const MaxInt = int(^uint(0) >> 1) - - if numRing < 0 || ringSize < 0 { - return -1 - } - - // Calculate term and check for overflow - - term := numRing * ringSize * 65 - - if term < 0 || term < numRing || term < ringSize { - return -1 - } - - return 8 + 8 + 32 + 32 + numRing*ringSize*32 + numRing*ringSize*33 + numRing*33 -} - -// deserializes the byteified signature into a RingSignature struct -func Deserialize(r []byte) (*RingSignature, error) { - if len(r) < 16 { - return nil, errors.New("Failed to deserialize ring signature") - } - offset := 0 - sig := new(RingSignature) - numRing := r[offset : offset+8] - offset += 8 - size := r[offset : offset+8] - offset += 8 - - size_uint := binary.BigEndian.Uint64(size) - size_int := int(size_uint) - sig.Size = size_int - - size_uint = binary.BigEndian.Uint64(numRing) - size_int = int(size_uint) - sig.NumRing = size_int - - if len(r) != computeSignatureSize(sig.NumRing, sig.Size) { - return nil, fmt.Errorf("incorrect ring size, len r: %d, sig.NumRing: %d sig.Size: %d", len(r), sig.NumRing, sig.Size) - } - - m := r[offset : offset+32] - offset += 32 - - var m_byte [32]byte - copy(m_byte[:], m) - - sig.M = m_byte - sig.C = new(big.Int).SetBytes(r[offset : offset+32]) - offset += 32 - - sig.S = make([][]*big.Int, sig.NumRing) - for i := 0; i < sig.NumRing; i++ { - sig.S[i] = make([]*big.Int, sig.Size) - for j := 0; j < sig.Size; j++ { - sig.S[i][j] = new(big.Int).SetBytes(r[offset : offset+32]) - offset += 32 - } - } - - sig.Curve = crypto.S256() - - sig.Ring = make([]Ring, sig.NumRing) - for i := 0; i < sig.NumRing; i++ { - sig.Ring[i] = make([]*ecdsa.PublicKey, sig.Size) - for j := 0; j < sig.Size; j++ { - compressedKey := r[offset : offset+33] - offset += 33 - compressedPubKey := DeserializeCompressed(sig.Curve, compressedKey) - sig.Ring[i][j] = compressedPubKey - } - } - - sig.I = make([]*ecdsa.PublicKey, sig.NumRing) - for i := 0; i < sig.NumRing; i++ { - compressedKey := r[offset : offset+33] - offset += 33 - compressedPubKey := DeserializeCompressed(sig.Curve, compressedKey) - sig.I[i] = compressedPubKey - } - - sig.SerializedRing = r - - if !Verify(sig, false) { - return nil, errors.New("failed to deserialize, invalid ring signature") - } - - return sig, nil -} - -// takes public key ring and places the public key corresponding to `privkey` in index s of the ring -// returns a key ring of type []*ecdsa.PublicKey -func GenKeyRing(ring []*ecdsa.PublicKey, privkey *ecdsa.PrivateKey, s int) ([]*ecdsa.PublicKey, error) { - size := len(ring) + 1 - new_ring := make([]*ecdsa.PublicKey, size) - pubkey := privkey.Public().(*ecdsa.PublicKey) - - if s > len(ring) { - return nil, errors.New("index s out of bounds") - } - - new_ring[s] = pubkey - for i := 1; i < size; i++ { - idx := (i + s) % size - new_ring[idx] = ring[i-1] - } - - return new_ring, nil -} - -// creates a ring with size specified by `size` and places the public key corresponding to `privkey` in index s of the ring -// returns a new key ring of type []*ecdsa.PublicKey -func GenNewKeyRing(size int, privkey *ecdsa.PrivateKey, s int) ([]*ecdsa.PublicKey, error) { - ring := make([]*ecdsa.PublicKey, size) - pubkey := privkey.Public().(*ecdsa.PublicKey) - - if s >= len(ring) { - return nil, errors.New("index s out of bounds") - } - - ring[s] = pubkey - - for i := 1; i < size; i++ { - idx := (i + s) % size - priv, err := crypto.GenerateKey() - if err != nil { - return nil, err - } - - pub := priv.Public() - ring[idx] = pub.(*ecdsa.PublicKey) - } - - return ring, nil -} - -// calculate key image I = x * H_p(P) where H_p is a hash function that returns a point -// H_p(P) = sha3(P) * G -func GenKeyImage(privkey *ecdsa.PrivateKey) *ecdsa.PublicKey { - pubkey := privkey.Public().(*ecdsa.PublicKey) - image := new(ecdsa.PublicKey) - - // calculate sha3(P) - h_x, h_y := HashPoint(pubkey) - - // calculate H_p(P) = x * sha3(P) * G - i_x, i_y := privkey.Curve.ScalarMult(h_x, h_y, privkey.D.Bytes()) - - image.X = i_x - image.Y = i_y - return image -} - -func HashPoint(p *ecdsa.PublicKey) (*big.Int, *big.Int) { - input := append(PadTo32Bytes(p.X.Bytes()), PadTo32Bytes(p.Y.Bytes())...) - log.Info("HashPoint", "input ", common.Bytes2Hex(input)) - hash := crypto.Keccak256(input) - log.Info("HashPoint", "hash ", common.Bytes2Hex(hash)) - return p.Curve.ScalarBaseMult(hash[:]) -} - -// create ring signature from list of public keys given inputs: -// msg: byte array, message to be signed -// ring: array of *ecdsa.PublicKeys to be included in the ring -// privkey: *ecdsa.PrivateKey of signer -// s: index of signer in ring -func Sign(m [32]byte, rings []Ring, privkeys []*ecdsa.PrivateKey, s int) (*RingSignature, error) { - numRing := len(rings) - if numRing < 1 { - return nil, errors.New("there is no ring to make signature") - } - // check ringsize > 1 - ringsize := len(rings[0]) - if ringsize < 2 { - return nil, errors.New("size of ring less than two") - } else if s >= ringsize || s < 0 { - return nil, errors.New("secret index out of range of ring size") - } - - // setup - //pubkey := privkey.Public().(*ecdsa.PublicKey) - pubkeys := make([]*ecdsa.PublicKey, numRing) - for i := 0; i < numRing; i++ { - pubkeys[i] = &privkeys[i].PublicKey - } - //cast to BitCurve used in go-eth since elliptic.Curve.Add() and elliptic.Curve.ScalarMult() is deprecated - curve := pubkeys[0].Curve.(*secp256k1.BitCurve) - sig := new(RingSignature) - sig.Size = ringsize - sig.NumRing = numRing - sig.M = m - sig.Ring = rings - sig.Curve = curve - - // check that key at index s is indeed the signer - for i := 0; i < numRing; i++ { - if rings[i][s] != pubkeys[i] { - return nil, errors.New("secret index in ring is not signer") - } - } - - // generate key image - images := make([]*ecdsa.PublicKey, numRing) - for i := 0; i < numRing; i++ { - images[i] = GenKeyImage(privkeys[i]) - } - sig.I = images - - // start at c[1] - // pick random scalar u (glue value), calculate c[1] = H(m, u*G) where H is a hash function and G is the base point of the curve - C := make([]*big.Int, ringsize) - S := make([][]*big.Int, numRing) - for i := 0; i < numRing; i++ { - S[i] = make([]*big.Int, ringsize) - } - - //Initialize S except S[..][s] - for i := 0; i < numRing; i++ { - for j := 0; j < ringsize; j++ { - if j != s { - randomGenerated, err := rand.Int(rand.Reader, curve.Params().P) - if err != nil { - return nil, err - } - S[i][j] = randomGenerated - } - } - } - - L := make([][]*ecdsa.PublicKey, numRing) - R := make([][]*ecdsa.PublicKey, numRing) - for i := 0; i < numRing; i++ { - L[i] = make([]*ecdsa.PublicKey, ringsize) - R[i] = make([]*ecdsa.PublicKey, ringsize) - } - alpha := make([]*big.Int, numRing) - - var l []byte - //compute L[i][s], R[i][s], i = 0..numRing - for i := 0; i < numRing; i++ { - randomGenerated, err := rand.Int(rand.Reader, curve.Params().P) - if err != nil { - return nil, err - } - alpha[i] = randomGenerated - // start at secret index s/PI - // compute L_s = u*G - l_x, l_y := curve.ScalarBaseMult(PadTo32Bytes(alpha[i].Bytes())) - L[i][s] = &ecdsa.PublicKey{curve, l_x, l_y} - lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) - l = append(l, lT...) - // compute R_s = u*H_p(P[s]) - h_x, h_y := HashPoint(pubkeys[i]) - r_x, r_y := curve.ScalarMult(h_x, h_y, PadTo32Bytes(alpha[i].Bytes())) - R[i][s] = &ecdsa.PublicKey{curve, r_x, r_y} - rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) - l = append(l, rT...) - } - - // concatenate m and u*G and calculate c[s+1] = H(m, L_s, R_s) - C_j := crypto.Keccak256(append(m[:], l...)) - idx := s + 1 - if idx == ringsize { - idx = 0 - } - if idx == 0 { - C[0] = new(big.Int).SetBytes(C_j[:]) - } else { - C[idx] = new(big.Int).SetBytes(C_j[:]) - } - for idx != s { - var l []byte - for j := 0; j < numRing; j++ { - // calculate L[j][idx] = s[j][idx]*G + c[idx]*Ring[j][idx] - px, py := curve.ScalarMult(rings[j][idx].X, rings[j][idx].Y, PadTo32Bytes(C[idx].Bytes())) // px, py = c_i*P_i - sx, sy := curve.ScalarBaseMult(PadTo32Bytes(S[j][idx].Bytes())) // sx, sy = s[n-1]*G - if px == nil || py == nil || sx == nil || sy == nil { - return nil, errors.New("Could not create ring signature") - } - l_x, l_y := curve.Add(sx, sy, px, py) - L[j][idx] = &ecdsa.PublicKey{curve, l_x, l_y} - lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) - l = append(l, lT...) - - // calculate R[j][idx] = s[j][idx]*H_p(Ring[j][idx]) + c[idx]*I[j] - px, py = curve.ScalarMult(images[j].X, images[j].Y, C[idx].Bytes()) // px, py = c_i*I - hx, hy := HashPoint(rings[j][idx]) - sx, sy = curve.ScalarMult(hx, hy, S[j][idx].Bytes()) // sx, sy = s[n-1]*H_p(P_i) - if px == nil || py == nil || sx == nil || sy == nil { - return nil, errors.New("Could not create ring signature") - } - r_x, r_y := curve.Add(sx, sy, px, py) - R[j][idx] = &ecdsa.PublicKey{curve, r_x, r_y} - rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) - l = append(l, rT...) - } - - idx++ - if idx == ringsize { - idx = 0 - } - - var ciIdx int - if idx == 0 { - ciIdx = 0 - } else { - ciIdx = idx - } - cSha := crypto.Keccak256(append(PadTo32Bytes(m[:]), l...)) - C[ciIdx] = new(big.Int).SetBytes(cSha[:]) - } - - //compute S[j][s] = alpha[j] - c[s] * privkeys[j], privkeys[j] = private key corresponding to key image I[j] - for j := 0; j < numRing; j++ { - cx := C[s] - // close ring by finding S[j][s] = (alpha[j] - c[s]*privkeys[s] ) mod P where k[s] is the private key and P is the order of the curve - S[j][s] = new(big.Int).Mod(new(big.Int).Sub(alpha[j], new(big.Int).Mul(cx, privkeys[j].D)), curve.Params().N) - } - - // everything ok, add values to signature - sig.S = S - sig.C = C[0] - sig.NumRing = numRing - sig.Size = ringsize - sig.C = C[0] - - return sig, nil -} - -// verify ring signature contained in RingSignature struct -// returns true if a valid signature, false otherwise -func Verify(sig *RingSignature, verifyMes bool) bool { - // setup - rings := sig.Ring - ringsize := sig.Size - numRing := sig.NumRing - S := sig.S - C := make([]*big.Int, ringsize+1) - C[0] = sig.C - //cast to BitCurve used in go-eth since elliptic.Curve.Add() and elliptic.Curve.ScalarMult() is deprecated - curve := sig.Curve.(*secp256k1.BitCurve) - image := sig.I - - //check on curve - for i := 0; i < numRing; i++ { - onCurve := curve.IsOnCurve(image[i].X, image[i].Y) - if !onCurve { - return false - } - for j := 0; j < ringsize; j++ { - onCurve := curve.IsOnCurve(rings[i][j].X, rings[i][j].Y) - if !onCurve { - return false - } - } - } - - // calculate c[i+1] = H(m, s[i]*G + c[i]*P[i]) - // and c[0] = H)(m, s[n-1]*G + c[n-1]*P[n-1]) where n is the ring size - //log.Info("C", "0", common.Bytes2Hex(C[0].Bytes())) - for j := 0; j < ringsize; j++ { - var l []byte - for i := 0; i < numRing; i++ { - // Validate S[i][j] and C[j] - if !isValidScalar(S[i][j], curve) || !isValidScalar(C[j], curve) { - return false // Or handle the error as required - } - - // calculate L[i][j] = s[i][j]*G + c[j]*Ring[i][j] - px, py := curve.ScalarMult(rings[i][j].X, rings[i][j].Y, C[j].Bytes()) // px, py = c_i*P_i - sx, sy := curve.ScalarBaseMult(S[i][j].Bytes()) // sx, sy = s[i]*G - if px == nil || py == nil || sx == nil || sy == nil { - return false - } - l_x, l_y := curve.Add(sx, sy, px, py) - lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) - //log.Info("L[i][j]", "i", i, "j", j, "L", common.Bytes2Hex(lT)) - l = append(l, lT...) - - // calculate R_i = s[i][j]*H_p(Ring[i][j]) + c[j]*I[j] - px, py = curve.ScalarMult(image[i].X, image[i].Y, C[j].Bytes()) // px, py = c[i]*I - hx, hy := HashPoint(rings[i][j]) - - // Validate S[i][j], hx, and hy - if !isValidScalar(S[i][j], curve) || !isValidScalar(hx, curve) || !isValidScalar(hy, curve) { - return false // Or handle the error as required - } - //log.Info("H[i][j]", "i", i, "j", j, "x.input", common.Bytes2Hex(rings[i][j].X.Bytes()), "y.input", common.Bytes2Hex(rings[i][j].Y.Bytes())) - //log.Info("H[i][j]", "i", i, "j", j, "x", common.Bytes2Hex(hx.Bytes()), "y", common.Bytes2Hex(hy.Bytes())) - sx, sy = curve.ScalarMult(hx, hy, S[i][j].Bytes()) // sx, sy = s[i]*H_p(P[i]) - if px == nil || py == nil || sx == nil || sy == nil { - return false - } - r_x, r_y := curve.Add(sx, sy, px, py) - rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) - //log.Info("R[i][j]", "i", i, "j", j, "L", common.Bytes2Hex(rT)) - l = append(l, rT...) - } - - // calculate c[i+1] = H(m, L_i, R_i) - //cj_mes := append(PadTo32Bytes(sig.M[:]), l...) - C_j := crypto.Keccak256(append(PadTo32Bytes(sig.M[:]), l...)) - //log.Info("C hash input", "j", j + 1, "C_input", common.Bytes2Hex(cj_mes)) - - /*if j == ringsize-1 { - C[0] = new(big.Int).SetBytes(C_j[:]) - } else {*/ - C[j+1] = new(big.Int).SetBytes(C_j[:]) - //log.Info("C", "j", j + 1, "C", common.Bytes2Hex(C[j + 1].Bytes())) - //} - } - - return bytes.Equal(sig.C.Bytes(), C[ringsize].Bytes()) -} - -func isValidScalar(scalar *big.Int, curve elliptic.Curve) bool { - return scalar.Sign() >= 0 && scalar.Cmp(curve.Params().N) < 0 -} - -func Link(sig_a *RingSignature, sig_b *RingSignature) bool { - for i := 0; i < len(sig_a.I); i++ { - for j := 0; j < len(sig_b.I); j++ { - if sig_a.I[i].X == sig_b.I[j].X && sig_a.I[i].Y == sig_b.I[j].Y { - return true - } - } - } - return false -} - -// function returns(mutiple rings, private keys, message, error) -func GenerateMultiRingParams(numRing int, ringSize int, s int) (rings []Ring, privkeys []*ecdsa.PrivateKey, m [32]byte, err error) { - for i := 0; i < numRing; i++ { - privkey, err := crypto.GenerateKey() - if err != nil { - return nil, nil, [32]byte{}, err - } - privkeys = append(privkeys, privkey) - - ring, err := GenNewKeyRing(ringSize, privkey, s) - if err != nil { - return nil, nil, [32]byte{}, err - } - rings = append(rings, ring) - } - - _, err = rand.Read(m[:]) - if err != nil { - return nil, nil, [32]byte{}, err - } - return rings, privkeys, m, nil -} diff --git a/core/vm/privacy/ringct_test.go b/core/vm/privacy/ringct_test.go deleted file mode 100644 index 3e5b771c0bce..000000000000 --- a/core/vm/privacy/ringct_test.go +++ /dev/null @@ -1,324 +0,0 @@ -package privacy - -import ( - "bytes" - "encoding/binary" - "fmt" - "math/big" - "testing" - - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/stretchr/testify/assert" - - "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" -) - -func TestSign(t *testing.T) { - /*for i := 14; i < 15; i++ { - for j := 14; j < 15; j++ { - for k := 0; k <= j; k++ {*/ - numRing := 5 - ringSize := 10 - s := 9 - fmt.Println("Generate random ring parameter ") - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) - - fmt.Println("numRing ", numRing) - fmt.Println("ringSize ", ringSize) - fmt.Println("index of real one ", s) - - fmt.Println("Ring ", rings) - fmt.Println("privkeys ", privkeys) - fmt.Println("m ", m) - - ringSignature, err := Sign(m, rings, privkeys, s) - if err != nil { - t.Error("Failed to create Ring signature") - } - - sig, err := ringSignature.Serialize() - if err != nil { - t.Error("Failed to Serialize input Ring signature") - } - - deserializedSig, err := Deserialize(sig) - if err != nil { - t.Error("Failed to Deserialize Ring signature") - } - verified := Verify(deserializedSig, false) - - if !verified { - t.Error("Failed to verify Ring signature") - } - -} - -func TestDeserialize(t *testing.T) { - numRing := 5 - ringSize := 10 - s := 5 - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) - - ringSignature, err := Sign(m, rings, privkeys, s) - if err != nil { - t.Error("Failed to create Ring signature") - } - - // A normal signature. - sig, err := ringSignature.Serialize() - if err != nil { - t.Error("Failed to Serialize input Ring signature") - } - - // Modify the serialized signature s.t. - // the new signature passes the length check - // but triggers buffer overflow in Deserialize(). - // ringSize: 10 -> 56759212534490939 - // len(sig): 3495 -> 3804 - // 80 + 5 * (56759212534490939*65 + 33) = 18446744073709551616 + 3804 - bs := make([]byte, 8) - binary.BigEndian.PutUint64(bs, 56759212534490939) - for i := 0; i < 8; i++ { - sig[i+8] = bs[i] - } - tail := make([]byte, 3804-len(sig)) - sig = append(sig, tail...) - - _, err = Deserialize(sig) - assert.EqualError(t, err, "incorrect ring size, len r: 3804, sig.NumRing: 5 sig.Size: 56759212534490939") -} - -func TestVerify1(t *testing.T) { - numRing := 5 - ringSize := 10 - s := 7 - - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) - if err != nil { - t.Error("fail to generate rings") - } - - ringSignature, err := Sign(m, rings, privkeys, s) - if err != nil { - t.Error("fail to create ring signature") - } - - sig, err := ringSignature.Serialize() - if err != nil { - t.Error("fail to serialize input ring signature") - } - - deserializedSig, err := Deserialize(sig) - if err != nil { - t.Error("fail to deserialize ring signature") - } - - assert.True(t, Verify(deserializedSig, false), "Verify should return true") -} - -func TestDeserialize2(t *testing.T) { - numRing := 5 - ringSize := 10 - s := 7 - - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) - if err != nil { - t.Error("fail to generate rings") - } - - ringSignature, err := Sign(m, rings, privkeys, s) - if err != nil { - t.Error("fail to create ring signature") - } - - // change one sig to the scalar field - ringSignature.S[0][0] = curve.Params().N - - sig, err := ringSignature.Serialize() - if err != nil { - t.Error("fail to serialize input ring signature") - } - - _, err = Deserialize(sig) - assert.EqualError(t, err, "failed to deserialize, invalid ring signature") -} - -func TestPadTo32Bytes(t *testing.T) { - arr := [44]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34} - - // test input slice is longer than 32 bytes - assert.True(t, bytes.Equal(PadTo32Bytes(arr[0:]), arr[0:32]), "Test PadTo32Bytes longer than 32 bytes #1") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[1:]), arr[1:33]), "Test PadTo32Bytes longer than 32 bytes #2") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[2:]), arr[2:34]), "Test PadTo32Bytes longer than 32 bytes #3") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[3:]), arr[3:35]), "Test PadTo32Bytes longer than 32 bytes #4") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[4:]), arr[4:36]), "Test PadTo32Bytes longer than 32 bytes #5") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[5:]), arr[5:37]), "Test PadTo32Bytes longer than 32 bytes #6") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[6:]), arr[6:38]), "Test PadTo32Bytes longer than 32 bytes #7") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[7:]), arr[7:39]), "Test PadTo32Bytes longer than 32 bytes #8") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[8:]), arr[8:40]), "Test PadTo32Bytes longer than 32 bytes #9") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[9:]), arr[9:41]), "Test PadTo32Bytes longer than 32 bytes #10") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:]), arr[10:42]), "Test PadTo32Bytes longer than 32 bytes #11") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[11:]), arr[11:43]), "Test PadTo32Bytes longer than 32 bytes #12") - - // test input slice is equal 32 bytes - assert.True(t, bytes.Equal(PadTo32Bytes(arr[0:32]), arr[0:32]), "Test PadTo32Bytes equal 32 bytes #1") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[1:33]), arr[1:33]), "Test PadTo32Bytes equal 32 bytes #2") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[2:34]), arr[2:34]), "Test PadTo32Bytes equal 32 bytes #3") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[3:35]), arr[3:35]), "Test PadTo32Bytes equal 32 bytes #4") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[4:36]), arr[4:36]), "Test PadTo32Bytes equal 32 bytes #5") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[5:37]), arr[5:37]), "Test PadTo32Bytes equal 32 bytes #6") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[6:38]), arr[6:38]), "Test PadTo32Bytes equal 32 bytes #7") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[7:39]), arr[7:39]), "Test PadTo32Bytes equal 32 bytes #8") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[8:40]), arr[8:40]), "Test PadTo32Bytes equal 32 bytes #9") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[9:41]), arr[9:41]), "Test PadTo32Bytes equal 32 bytes #10") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:42]), arr[10:42]), "Test PadTo32Bytes equal 32 bytes #11") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[11:43]), arr[11:43]), "Test PadTo32Bytes equal 32 bytes #12") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[12:44]), arr[12:44]), "Test PadTo32Bytes equal 32 bytes #13") - - // test input slice is shorter than 32 bytes - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:32]), arr[0:32]), "Test PadTo32Bytes shorter than 32 bytes #1") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:33]), arr[1:33]), "Test PadTo32Bytes shorter than 32 bytes #2") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:34]), arr[2:34]), "Test PadTo32Bytes shorter than 32 bytes #3") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:35]), arr[3:35]), "Test PadTo32Bytes shorter than 32 bytes #4") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:36]), arr[4:36]), "Test PadTo32Bytes shorter than 32 bytes #5") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:37]), arr[5:37]), "Test PadTo32Bytes shorter than 32 bytes #6") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:38]), arr[6:38]), "Test PadTo32Bytes shorter than 32 bytes #7") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:39]), arr[7:39]), "Test PadTo32Bytes shorter than 32 bytes #8") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:40]), arr[8:40]), "Test PadTo32Bytes shorter than 32 bytes #9") - assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:41]), arr[9:41]), "Test PadTo32Bytes shorter than 32 bytes #10") -} - -func TestCurveAddNegative(t *testing.T) { - curve := crypto.S256().(*secp256k1.BitCurve) - - x1, y1 := curve.ScalarBaseMult(new(big.Int).SetUint64(uint64(2)).Bytes()) - fmt.Printf("Point(%x, %x)\n", x1, y1) - - x2 := x1 - y2 := new(big.Int).Neg(y1) // negative of point (x1,y1) - - x3, y3 := curve.Add(x1, y1, x2, y2) - fmt.Printf("Output is Point(%x, %x)\n", x3, y3) - - x0 := new(big.Int).SetUint64(uint64(0)) - y0 := new(big.Int).SetUint64(uint64(0)) // infinity - - if (x3.Cmp(x0) == 0) && (y3.Cmp(y0) == 0) { - // fmt.Printf("Correct, add negative of self should yield (0,0)") - } else { - t.Error("Incorrect, add negative of self did not yield (0,0)") - } -} - -func TestCurveAddZero(t *testing.T) { - // curve := crypto.S256() - curve := crypto.S256().(*secp256k1.BitCurve) - - x1, y1 := curve.ScalarBaseMult(new(big.Int).SetUint64(uint64(1)).Bytes()) - fmt.Printf("Point(%x, %x)\n", x1, y1) - - x0 := new(big.Int).SetUint64(uint64(0)) - y0 := new(big.Int).SetUint64(uint64(0)) // infinity - fmt.Printf("Is point (%d,%d) on the curve: %t \n", x0, y0, curve.IsOnCurve(x0, y0)) - - x2, y2 := curve.Add(x1, y1, x0, y0) - fmt.Printf("Output is Point(%x, %x)\n", x2, y2) - - if (x1.Cmp(x2) == 0) && (y1.Cmp(y2) == 0) { - // fmt.Printf("Correct, Point on curve is the same after Zero addition\n") - } else { - t.Error("Incorrect, Point on curve changed after Zero addition\n") - } -} - -func TestOnCurveVerify(t *testing.T) { - numRing := 5 - ringSize := 10 - s := 5 - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) - ringSignature, err := Sign(m, rings, privkeys, s) - if err != nil { - t.Error("Failed to create Ring signature") - } - - valid := Verify(ringSignature, false) - if !valid { - t.Error("Incorrect, unmodified ringSignature should be valid") - } - - ringsModified := ringSignature.Ring - ringsModified[0][0].X = big.NewInt(1) - ringsModified[0][0].Y = big.NewInt(1) - valid = Verify(ringSignature, false) - if valid { - t.Error("Incorrect, modified ringSignature should be invalid") - } -} - -func TestOnCurveDeserialize(t *testing.T) { - numRing := 5 - ringSize := 10 - s := 5 - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) - ringSignature, err := Sign(m, rings, privkeys, s) - if err != nil { - t.Error("Failed to create Ring signature") - } - - sig, err := ringSignature.Serialize() - if err != nil { - t.Error("Failed to Serialize input Ring signature") - } - _, err = Deserialize(sig) - if err != nil { - t.Error("Failed to Deserialize") - } - - ringsModified := ringSignature.Ring - ringsModified[0][0].X = big.NewInt(1) - ringsModified[0][0].Y = big.NewInt(1) - - sig, err = ringSignature.Serialize() - if err != nil { - t.Error("Failed to Serialize input Ring signature") - } - _, err = Deserialize(sig) - assert.EqualError(t, err, "failed to deserialize, invalid ring signature") -} - -func TestCurveScalarMult(t *testing.T) { - curve := crypto.S256().(*secp256k1.BitCurve) - - x, y := curve.ScalarBaseMult(curve.Params().N.Bytes()) - if x == nil && y == nil { - fmt.Println("Scalar multiplication with base point returns nil when scalar is the scalar field") - } - - x2, y2 := curve.ScalarMult(new(big.Int).SetUint64(uint64(100)), new(big.Int).SetUint64(uint64(2)), curve.Params().N.Bytes()) - if x2 == nil && y2 == nil { - fmt.Println("Scalar multiplication with a point (not necessarily on curve) returns nil when scalar is the scalar field") - } -} - -func TestNilPointerDereferencePanic(t *testing.T) { - numRing := 5 - ringSize := 10 - s := 7 - rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) - - ringSig, err := Sign(m, rings, privkeys, s) - if err != nil { - fmt.Println("Failed to set up") - } - - ringSig.S[0][0] = curve.Params().N // change one sig to the scalar field - - sig, err := ringSig.Serialize() - if err != nil { - t.Error("Failed to Serialize input Ring signature") - } - - _ , err = Deserialize(sig) - // Should failed to verify Ring signature as the signature is invalid - assert.EqualError(t, err, "failed to deserialize, invalid ring signature") -} From 46ef5e10ad8766a80c0f0e1621b571872ed97fd2 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 4 Mar 2024 17:13:50 +1100 Subject: [PATCH 42/59] add testnet and mainnet node for latest code testing (#467) --- .github/workflows/ci.yml | 44 +++++++- .gitignore | 3 +- cicd/README.md | 2 +- .../module/region/container-definition.tpl | 2 +- cicd/devnet/terraform/module/region/ecs.tf | 2 +- cicd/mainnet/start.sh | 2 +- cicd/terraform/.env | 13 +++ cicd/terraform/iam.tf | 28 +++++ cicd/terraform/main.tf | 59 ++++++++++ .../module/region/container-definition.tpl | 44 ++++++++ cicd/terraform/module/region/ecs.tf | 96 ++++++++++++++++ cicd/terraform/module/region/efs.tf | 67 +++++++++++ cicd/terraform/module/region/main.tf | 103 +++++++++++++++++ cicd/terraform/module/region/rpc.tf | 104 ++++++++++++++++++ cicd/terraform/module/region/variables.tf | 50 +++++++++ cicd/terraform/s3.tf | 14 +++ cicd/terraform/variables.tf | 35 ++++++ cicd/testnet/start.sh | 4 +- 18 files changed, 663 insertions(+), 9 deletions(-) create mode 100644 cicd/terraform/.env create mode 100644 cicd/terraform/iam.tf create mode 100644 cicd/terraform/main.tf create mode 100644 cicd/terraform/module/region/container-definition.tpl create mode 100644 cicd/terraform/module/region/ecs.tf create mode 100644 cicd/terraform/module/region/efs.tf create mode 100644 cicd/terraform/module/region/main.tf create mode 100644 cicd/terraform/module/region/rpc.tf create mode 100644 cicd/terraform/module/region/variables.tf create mode 100644 cicd/terraform/s3.tf create mode 100644 cicd/terraform/variables.tf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f64086133d14..0aeeb8f0e3fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,7 +97,7 @@ jobs: needs: devnet_build_push environment: devnet env: - AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} steps: - uses: actions/checkout@v4 @@ -122,6 +122,48 @@ jobs: done aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager | head -n 10; + rpcnode_terraform_apply: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + needs: devnet_build_push + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + steps: + - uses: actions/checkout@v4 + - name: Terraform Apply + run: | + cd cicd/terraform + terraform init ${{ env.tf_init_cli_options }} + terraform apply ${{ env.tf_apply_cli_options }} + + testnet_dev-upgrade_node: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + needs: rpcnode_terraform_apply + environment: testnet + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + steps: + - uses: actions/checkout@v4 + - name: ECS Update + run: | + aws ecs update-service --region ap-southeast-1 --cluster testnet-xdcnode-cluster --service ecs-service-testnet-rpc1 --force-new-deployment --no-cli-pager | head -n 10; + + mainnet_dev-upgrade_node: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/dev-upgrade' && !startsWith(github.ref, 'refs/tags/') + needs: rpcnode_terraform_apply + environment: mainnet + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + steps: + - uses: actions/checkout@v4 + - name: ECS Update + run: | + aws ecs update-service --region ap-southeast-1 --cluster mainnet-xdcnode-cluster --service ecs-service-mainnet-rpc1 --force-new-deployment --no-cli-pager | head -n 10; devnet_send_notification: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 5548c132dd29..1a85f2f558a2 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,6 @@ profile.cov **/yarn-error.log coverage.txt go.sum -cicd/devnet/terraform/.terraform* +**/.terraform* cicd/devnet/tmp -.env cicd/devnet/terraform/node-config.json \ No newline at end of file diff --git a/cicd/README.md b/cicd/README.md index 77d22d538701..b5d61e4098ec 100644 --- a/cicd/README.md +++ b/cicd/README.md @@ -6,5 +6,5 @@ docker build -f cicd/Dockerfile . ``` ## Docker Run ``` -docker run -it -e NETWORK=devnet -e PRIVATE_KEYS=$KEY $IMAGE +docker run -it -e NETWORK=devnet -e PRIVATE_KEY=$KEY $IMAGE `` \ No newline at end of file diff --git a/cicd/devnet/terraform/module/region/container-definition.tpl b/cicd/devnet/terraform/module/region/container-definition.tpl index 270a6847fc5a..008e98522ac1 100644 --- a/cicd/devnet/terraform/module/region/container-definition.tpl +++ b/cicd/devnet/terraform/module/region/container-definition.tpl @@ -3,7 +3,7 @@ "name": "tfXdcNode", "image": "xinfinorg/${image_environment}:${image_tag}", "environment": [ - {"name": "PRIVATE_KEYS", "value": "${private_keys}"}, + {"name": "PRIVATE_KEY", "value": "${private_key}"}, {"name": "LOG_LEVEL", "value": "${log_level}"}, {"name": "NODE_NAME", "value": "${node_name}"}, {"name": "NETWORK", "value": "${chain_network}"} diff --git a/cicd/devnet/terraform/module/region/ecs.tf b/cicd/devnet/terraform/module/region/ecs.tf index 0f589f944728..8cfc43427e68 100644 --- a/cicd/devnet/terraform/module/region/ecs.tf +++ b/cicd/devnet/terraform/module/region/ecs.tf @@ -6,7 +6,7 @@ data template_file devnet_container_definition { image_environment = "${lookup(each.value, "imageEnvironment", "devnet")}" image_tag = "${lookup(each.value, "imageTag", "latest")}" node_name = "${each.key}" - private_keys = "${each.value.pk}" + private_key = "${each.value.pk}" cloudwatch_group = "tf-${each.key}" cloudwatch_region = "${var.region}" log_level = "${lookup(each.value, "logLevel", "${var.logLevel}")}" diff --git a/cicd/mainnet/start.sh b/cicd/mainnet/start.sh index 86054c02c211..35f11a5d3406 100755 --- a/cicd/mainnet/start.sh +++ b/cicd/mainnet/start.sh @@ -76,7 +76,7 @@ XDC --ethstats ${netstats} --gcmode archive \ --datadir /work/xdcchain --networkid 50 \ -port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \ --rpcport $rpc_port \ ---rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \ +--rpcapi admin,db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ --rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ diff --git a/cicd/terraform/.env b/cicd/terraform/.env new file mode 100644 index 000000000000..4eb6ca5a95c0 --- /dev/null +++ b/cicd/terraform/.env @@ -0,0 +1,13 @@ +log_level=3 + +# Ohio +us_east_2_start=0 +us_east_2_end=36 + +# Ireland +eu_west_1_start=37 +eu_west_1_end=72 + +# Sydney +ap_southeast_2_start=73 +ap_southeast_2_end=108 \ No newline at end of file diff --git a/cicd/terraform/iam.tf b/cicd/terraform/iam.tf new file mode 100644 index 000000000000..f5c5ee2fe08c --- /dev/null +++ b/cicd/terraform/iam.tf @@ -0,0 +1,28 @@ +# IAM policies +data "aws_iam_policy_document" "xdc_ecs_tasks_execution_role" { + statement { + actions = ["sts:AssumeRole"] + + principals { + type = "Service" + identifiers = ["ecs-tasks.amazonaws.com"] + } + } +} + +# Create the role +resource "aws_iam_role" "xdc_ecs_tasks_execution_role" { + name = "xdc-ecs-task-execution-role" + assume_role_policy = "${data.aws_iam_policy_document.xdc_ecs_tasks_execution_role.json}" +} + +# Attached the AWS managed policies to the new role +resource "aws_iam_role_policy_attachment" "xdc_ecs_tasks_execution_role" { + for_each = toset([ + "arn:aws:iam::aws:policy/AmazonElasticFileSystemClientFullAccess", + "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy", + "arn:aws:iam::aws:policy/AmazonElasticFileSystemsUtils" + ]) + role = aws_iam_role.xdc_ecs_tasks_execution_role.name + policy_arn = each.value +} diff --git a/cicd/terraform/main.tf b/cicd/terraform/main.tf new file mode 100644 index 000000000000..5df86c7c2bd1 --- /dev/null +++ b/cicd/terraform/main.tf @@ -0,0 +1,59 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.13.1" + } + } +} + +# Default +provider "aws" { + region = "us-east-1" +} + +# WARNING: APSE-1 will only be used to host rpc node +# Workaround to avoid conflicts with existing ecs cluster in existing regions +provider "aws" { + alias = "ap-southeast-1" + region = "ap-southeast-1" +} + +module "testnet-rpc" { + source = "./module/region" + region = "ap-southeast-1" + nodeKeys = local.rpcTestnetNodeKeys + enableFixedIp = true + logLevel = local.logLevel + xdc_ecs_tasks_execution_role_arn = aws_iam_role.xdc_ecs_tasks_execution_role.arn + + cpu = 1024 + memory = 4096 + + network = "testnet" + vpc_cidr = "10.1.0.0/16" + subnet_cidr = "10.1.0.0/20" + providers = { + aws = aws.ap-southeast-1 + } +} + +module "mainnet-rpc" { + source = "./module/region" + region = "ap-southeast-1" + nodeKeys = local.rpcMainnetNodeKeys + enableFixedIp = true + logLevel = local.logLevel + xdc_ecs_tasks_execution_role_arn = aws_iam_role.xdc_ecs_tasks_execution_role.arn + + cpu = 1024 + memory = 4096 + + network = "mainnet" + vpc_cidr = "10.2.0.0/16" + subnet_cidr = "10.2.0.0/20" + providers = { + aws = aws.ap-southeast-1 + } +} + diff --git a/cicd/terraform/module/region/container-definition.tpl b/cicd/terraform/module/region/container-definition.tpl new file mode 100644 index 000000000000..008e98522ac1 --- /dev/null +++ b/cicd/terraform/module/region/container-definition.tpl @@ -0,0 +1,44 @@ +[ + { + "name": "tfXdcNode", + "image": "xinfinorg/${image_environment}:${image_tag}", + "environment": [ + {"name": "PRIVATE_KEY", "value": "${private_key}"}, + {"name": "LOG_LEVEL", "value": "${log_level}"}, + {"name": "NODE_NAME", "value": "${node_name}"}, + {"name": "NETWORK", "value": "${chain_network}"} + ], + "essential": true, + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "${cloudwatch_group}", + "awslogs-region": "${cloudwatch_region}", + "awslogs-stream-prefix": "ecs" + } + }, + "portMappings": [ + { + "hostPort": 8555, + "protocol": "tcp", + "containerPort": 8555 + }, + { + "hostPort": 8545, + "protocol": "tcp", + "containerPort": 8545 + }, + { + "hostPort": 30303, + "protocol": "tcp", + "containerPort": 30303 + } + ], + "mountPoints": [ + { + "containerPath": "/work/xdcchain", + "sourceVolume": "efs" + } + ] + } +] \ No newline at end of file diff --git a/cicd/terraform/module/region/ecs.tf b/cicd/terraform/module/region/ecs.tf new file mode 100644 index 000000000000..d529cafb4702 --- /dev/null +++ b/cicd/terraform/module/region/ecs.tf @@ -0,0 +1,96 @@ +data template_file container_definition { + for_each = var.nodeKeys + template = "${file("${path.module}/container-definition.tpl")}" + + vars = { + image_environment = "${lookup(each.value, "imageEnvironment", "devnet")}" + image_tag = "${lookup(each.value, "imageTag", "latest")}" + node_name = "${each.key}" + private_key = "${each.value.pk}" + cloudwatch_group = "tf-${each.key}" + cloudwatch_region = "${var.region}" + log_level = "${lookup(each.value, "logLevel", "${var.logLevel}")}" + chain_network = var.network + } +} + +resource "aws_ecs_task_definition" "task_definition_group" { + for_each = var.nodeKeys + + family = "${var.network}-${each.key}" + requires_compatibilities = ["FARGATE"] + network_mode = "awsvpc" + container_definitions = data.template_file.container_definition[each.key].rendered + execution_role_arn = var.xdc_ecs_tasks_execution_role_arn + task_role_arn = var.xdc_ecs_tasks_execution_role_arn + + # New nodes will consume a lot more CPU usage than existing nodes. + # This is due to sync is resource heavy. Recommending set to below if doing sync: + # CPU = 2048, Memory = 4096 + # Please set it back to cpu 256 and memory of 2048 after sync is done to save the cost + # cpu = 256 + # memory = 2048 + cpu = var.cpu + memory = var.memory + volume { + name = "efs" + + efs_volume_configuration { + file_system_id = aws_efs_file_system.efs[each.key].id + root_directory = "/" + transit_encryption = "ENABLED" + authorization_config { + access_point_id = aws_efs_access_point.efs_access_point[each.key].id + iam = "DISABLED" + } + } + } + + tags = { + Name = "Tf${var.network}Ecs-${each.key}" + } +} + +data "aws_ecs_task_definition" "ecs_task_definition" { + for_each = var.nodeKeys + task_definition = aws_ecs_task_definition.task_definition_group[each.key].family +} + +# ECS cluster +resource "aws_ecs_cluster" "ecs_cluster" { + name = "${var.network}-xdcnode-cluster" + tags = { + Name = "Tf${var.network}EcsCluster" + } +} + + +resource "aws_ecs_service" "ecs_service" { + for_each = var.enableFixedIp ? {} : var.nodeKeys + name = "ecs-service-${each.key}" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = "${aws_ecs_task_definition.task_definition_group[each.key].family}:${max(aws_ecs_task_definition.task_definition_group[each.key].revision, data.aws_ecs_task_definition.ecs_task_definition[each.key].revision)}" + launch_type = "FARGATE" + scheduling_strategy = "REPLICA" + desired_count = 1 + force_new_deployment = true + deployment_minimum_healthy_percent = 0 + deployment_maximum_percent = 100 + + network_configuration { + subnets = [aws_subnet.subnet.id] + assign_public_ip = true + security_groups = [ + aws_default_security_group.xdcnode_security_group.id + ] + } + + deployment_circuit_breaker { + enable = true + rollback = false + } + + tags = { + Name = "Tf${var.network}EcsService-${each.key}" + } +} \ No newline at end of file diff --git a/cicd/terraform/module/region/efs.tf b/cicd/terraform/module/region/efs.tf new file mode 100644 index 000000000000..11b426ff3754 --- /dev/null +++ b/cicd/terraform/module/region/efs.tf @@ -0,0 +1,67 @@ + +# EFS +resource "aws_security_group" "efs_security_group" { + name = "Tf${var.network}EfsSecurityGroup" + description = "Allow HTTP in and out of ${var.network} EFS" + vpc_id = aws_vpc.vpc.id + + ingress { + from_port = 2049 + to_port = 2049 + protocol = "TCP" + security_groups = [aws_default_security_group.xdcnode_security_group.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + tags = { + Name = "Tf${var.network}Efs" + } +} + +resource "aws_efs_file_system" "efs" { + for_each = var.nodeKeys + creation_token = "efs-${each.key}" + performance_mode = "generalPurpose" + throughput_mode = "bursting" + encrypted = "true" + lifecycle_policy { + transition_to_ia = "AFTER_30_DAYS" + } + tags = { + Name = "Tf${var.network}Efs${each.key}" + } + } + +resource "aws_efs_mount_target" "efs_efs_mount_target" { + for_each = var.nodeKeys + file_system_id = aws_efs_file_system.efs[each.key].id + subnet_id = aws_subnet.subnet.id + security_groups = [aws_security_group.efs_security_group.id] +} + +resource "aws_efs_access_point" "efs_access_point" { + for_each = var.nodeKeys + file_system_id = aws_efs_file_system.efs[each.key].id + root_directory { + path = "/${each.key}/database" + creation_info { + owner_gid = 1001 + owner_uid = 1001 + permissions = 777 + } + } + posix_user { + gid = 1001 + uid = 1001 + secondary_gids = [0] + } + + tags = { + Name = "Tf${var.network}EfsAccessPoint${each.key}" + } +} \ No newline at end of file diff --git a/cicd/terraform/module/region/main.tf b/cicd/terraform/module/region/main.tf new file mode 100644 index 000000000000..5c6e0a47cd16 --- /dev/null +++ b/cicd/terraform/module/region/main.tf @@ -0,0 +1,103 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.13.1" + } + } +} + +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + instance_tenancy = "default" + enable_dns_hostnames = true + + tags = { + Name = "Tf${var.network}Vpc" + } +} + +resource "aws_subnet" "subnet" { + vpc_id = aws_vpc.vpc.id + cidr_block = var.subnet_cidr + map_public_ip_on_launch = true + + tags = { + Name = "Tf${var.network}VpcSubnet" + } +} + +resource "aws_internet_gateway" "gatewat" { + vpc_id = aws_vpc.vpc.id + + tags = { + Name = "Tf${var.network}Gateway" + } +} + +resource "aws_route_table" "route_table" { + vpc_id = aws_vpc.vpc.id + + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.gatewat.id + } + + tags = { + Name = "Tf${var.network}VpcRoutingTable" + } +} + +resource "aws_route_table_association" "route_table_association" { + subnet_id = aws_subnet.subnet.id + route_table_id = aws_route_table.route_table.id +} + +resource "aws_default_security_group" "xdcnode_security_group" { + vpc_id = aws_vpc.vpc.id + + ingress { + description = "listener port" + from_port = 30303 + to_port = 30303 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + description = "discovery port" + from_port = 30303 + to_port = 30303 + protocol = "udp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + description = "rpc port" + from_port = 8545 + to_port = 8545 + protocol = "tcp" + cidr_blocks = [var.vpc_cidr] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + tags = { + Name = "Tf${var.network}Node" + } +} + +# Logs +resource "aws_cloudwatch_log_group" "cloud_watch_group" { + for_each = var.nodeKeys + + name = "tf-${each.key}" + retention_in_days = 14 # Logs are only kept for 14 days + tags = { + Name = "Tf${var.network}CloudWatchGroup${each.key}" + } +} \ No newline at end of file diff --git a/cicd/terraform/module/region/rpc.tf b/cicd/terraform/module/region/rpc.tf new file mode 100644 index 000000000000..901b3b9c0bca --- /dev/null +++ b/cicd/terraform/module/region/rpc.tf @@ -0,0 +1,104 @@ +# Allocate an Elastic IP for the NLB +resource "aws_eip" "nlb_eip" { + domain = "vpc" +} + + +# Create a Network Load Balancer +resource "aws_lb" "rpc_node_nlb" { + count = var.enableFixedIp ? 1 : 0 + name = "${var.network}-rpc-node-nlb" + load_balancer_type = "network" + + enable_deletion_protection = false + + subnet_mapping { + subnet_id = aws_subnet.subnet.id + allocation_id = aws_eip.nlb_eip.id + } +} + +# Listener and Target Group for the rpc node container +resource "aws_lb_target_group" "rpc_node_tg_8545" { + count = var.enableFixedIp ? 1 : 0 + name = "${var.network}-rpc-node-tg" + port = 8545 + protocol = "TCP" + vpc_id = aws_vpc.vpc.id + target_type = "ip" +} + +resource "aws_lb_listener" "rpc_node_listener_8545" { + count = var.enableFixedIp ? 1 : 0 + load_balancer_arn = aws_lb.rpc_node_nlb[0].arn + port = 8545 + protocol = "TCP" + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.rpc_node_tg_8545[0].arn + } +} + +resource "aws_ecs_service" "rpc_node_ecs_service" { + for_each = var.enableFixedIp ? var.nodeKeys : {} + name = "ecs-service-${each.key}" + cluster = aws_ecs_cluster.ecs_cluster.id + task_definition = "${aws_ecs_task_definition.task_definition_group[each.key].family}:${max(aws_ecs_task_definition.task_definition_group[each.key].revision, data.aws_ecs_task_definition.ecs_task_definition[each.key].revision)}" + launch_type = "FARGATE" + scheduling_strategy = "REPLICA" + desired_count = 1 + force_new_deployment = true + deployment_minimum_healthy_percent = 0 + deployment_maximum_percent = 100 + + network_configuration { + subnets = [aws_subnet.subnet.id] + assign_public_ip = true + security_groups = [ + aws_default_security_group.xdcnode_security_group.id + ] + } + + deployment_circuit_breaker { + enable = true + rollback = false + } + + load_balancer { + target_group_arn = aws_lb_target_group.rpc_node_tg_8545[0].arn + container_name = "tfXdcNode" + container_port = 8545 + } + + depends_on = [ + aws_lb_listener.rpc_node_listener_8545 + ] + + tags = { + Name = "TfRpcNodeEcsService-${each.key}" + } +} + +# Target Group for port 30303 +resource "aws_lb_target_group" "rpc_node_tg_30303" { + count = var.enableFixedIp ? 1 : 0 + name = "${var.network}-rpc-node-tg-30303" + port = 30303 + protocol = "TCP" + vpc_id = aws_vpc.vpc.id + target_type = "ip" +} + +# Listener for port 30303 +resource "aws_lb_listener" "rpc_node_listener_30303" { + count = var.enableFixedIp ? 1 : 0 + load_balancer_arn = aws_lb.rpc_node_nlb[0].arn + port = 30303 + protocol = "TCP" + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.rpc_node_tg_30303[0].arn + } +} \ No newline at end of file diff --git a/cicd/terraform/module/region/variables.tf b/cicd/terraform/module/region/variables.tf new file mode 100644 index 000000000000..3f09785b8142 --- /dev/null +++ b/cicd/terraform/module/region/variables.tf @@ -0,0 +1,50 @@ +variable "region" { + description = "AWS region" + type = string +} + +variable "nodeKeys" { + description = "each miner's key" + type = map +} + +variable "logLevel" { + description = "containers log level" + type = string +} + +variable "xdc_ecs_tasks_execution_role_arn" { + description = "aws iam role resource arn" + type = string +} + +variable "enableFixedIp" { + description = "a flag to indicate whether fixed ip should be associated to the nodes. This is used for RPC node" + type = bool + default = false +} + +variable "network" { + description = "blockchain network" + type = string +} + +variable "cpu" { + description = "container cpu" + type = number +} + +variable "memory" { + description = "container memory" + type = number +} + +variable "vpc_cidr" { + description = "vpc cidr" + type = string +} + +variable "subnet_cidr" { + description = "subnet cidr" + type = string +} \ No newline at end of file diff --git a/cicd/terraform/s3.tf b/cicd/terraform/s3.tf new file mode 100644 index 000000000000..4968c852c33f --- /dev/null +++ b/cicd/terraform/s3.tf @@ -0,0 +1,14 @@ +# Bucket need to be created first. If first time run terraform init, need to comment out the below section +terraform { + backend "s3" { + bucket = "tf-xinfin-bucket" + key = "tf/terraform.tfstate" + region = "us-east-1" + encrypt = true + } +} + +data "aws_s3_object" "xdc_node_config" { + bucket = "tf-xinfin-bucket" + key = "node-config.json" +} diff --git a/cicd/terraform/variables.tf b/cicd/terraform/variables.tf new file mode 100644 index 000000000000..d952bc258cf4 --- /dev/null +++ b/cicd/terraform/variables.tf @@ -0,0 +1,35 @@ +locals { + /** + Load the nodes data from s3 + Below is the the format the config needs to follow: + {{Name of the node, in a pattern of 'xdc'+ number. i.e xdc50}}: { + pk: {{Value of the node private key}}, + ... any other configuration we want to pass. + } + Note: No `n` is allowed in the node name + **/ + predefinedNodesConfig = jsondecode(data.aws_s3_object.xdc_node_config.body) + envs = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] } + logLevel = local.envs["log_level"] + + # regions = [ + # { + # "name": "us-east-2", // Ohio + # "start": local.envs["us_east_2_start"], + # "end": local.envs["us_east_2_end"], + # } + # ] + + # keyNames = { + # for r in local.regions : + # r.name => [for i in range(r.start, r.end+1) : "xdc${i}"] + # } + + # nodeKeys = { + # for r in local.regions : + # r.name => { for i in local.keyNames[r.name]: i => local.predefinedNodesConfig[i] } + # } + + rpcTestnetNodeKeys = { "testnet-rpc1": local.predefinedNodesConfig["testnet-rpc1"]} // we hardcode the rpc to a single node for now + rpcMainnetNodeKeys = { "mainnet-rpc1": local.predefinedNodesConfig["mainnet-rpc1"]} // we hardcode the rpc to a single node for now +} diff --git a/cicd/testnet/start.sh b/cicd/testnet/start.sh index 665c0637fc9e..d5f9a0f443fc 100755 --- a/cicd/testnet/start.sh +++ b/cicd/testnet/start.sh @@ -78,9 +78,9 @@ XDC --ethstats ${netstats} --gcmode archive \ --datadir /work/xdcchain --networkid 51 \ -port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \ --rpcport $rpc_port \ ---rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \ +--rpcapi admin,db,eth,debug,net,shh,txpool,personal,web3,XDPoS \ --rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ ---periodicprofile --debugdatadir /work/xdcchain \ +--debugdatadir /work/xdcchain \ --ws --wsaddr=0.0.0.0 --wsport $ws_port \ --wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log From 7bd39e6bf9109e40934f86695973ccad8c4a0887 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Sun, 3 Mar 2024 22:05:24 +0800 Subject: [PATCH 43/59] rpc: add http.TimeoutHandler for http server --- rpc/http.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpc/http.go b/rpc/http.go index e43597a992ac..e7b90acbe339 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -237,11 +237,12 @@ func NewHTTPServer(cors []string, vhosts []string, srv *Server, writeTimeout tim // Wrap the CORS-handler within a host-handler handler := newCorsHandler(srv, cors) handler = newVHostHandler(vhosts, handler) + handler = http.TimeoutHandler(handler, writeTimeout, `{"error":"http server timeout"}`) log.Info("NewHTTPServer", "writeTimeout", writeTimeout) return &http.Server{ Handler: handler, ReadTimeout: 5 * time.Second, - WriteTimeout: writeTimeout, + WriteTimeout: writeTimeout + time.Second, IdleTimeout: 120 * time.Second, } } From 07d40a0038878b3008f31c3c87c72d654c1f895a Mon Sep 17 00:00:00 2001 From: Banana-J Date: Mon, 4 Mar 2024 22:05:19 +1100 Subject: [PATCH 44/59] fix: add lock for haserror variable in timeout.go (#443) Co-authored-by: wjrjerome --- consensus/XDPoS/engines/engine_v2/timeout.go | 24 ++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index d03f2d656a16..4331ba3ecd2f 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -123,6 +123,8 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time var wg sync.WaitGroup wg.Add(len(signatures)) + + var mutex sync.Mutex var haveError error signedTimeoutObj := types.TimeoutSigHash(&types.TimeoutForSign{ @@ -134,15 +136,19 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time go func(sig types.Signature) { defer wg.Done() verified, _, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochMasterNodes) - if err != nil { - log.Error("[verifyTC] Error while verfying TC message signatures", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures), "Error", err) - haveError = fmt.Errorf("error while verfying TC message signatures, %s", err) - return - } - if !verified { - log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures)) - haveError = fmt.Errorf("fail to verify TC due to signature mis-match") - return + if err != nil || !verified { + log.Error("[verifyTC] Error or verification failure", "Signature", sig, "Error", err) + mutex.Lock() // Lock before accessing haveError + if haveError == nil { + if err != nil { + log.Error("[verifyTC] Error while verfying TC message signatures", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures), "Error", err) + haveError = fmt.Errorf("error while verifying TC message signatures, %s", err) + } else { + log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures)) + haveError = fmt.Errorf("fail to verify TC due to signature mis-match") + } + } + mutex.Unlock() // Unlock after modifying haveError } }(signature) } From 753729c28cb852797ab90d906609f07625847b63 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 4 Mar 2024 22:06:47 +1100 Subject: [PATCH 45/59] Disable xdcx related tx creation (#430) * stop create xdcx tx * refactor disable flag * disable miner only --- common/constants.go | 1 + common/constants/constants.go.devnet | 1 + common/constants/constants.go.testnet | 1 + miner/worker.go | 45 +++++++++++++-------------- params/config.go | 9 +++--- 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/common/constants.go b/common/constants.go index 349706679b97..c923eeea8da0 100644 --- a/common/constants.go +++ b/common/constants.go @@ -45,6 +45,7 @@ var TIPXDCX = big.NewInt(38383838) var TIPXDCXLending = big.NewInt(38383838) var TIPXDCXCancellationFee = big.NewInt(38383838) var TIPXDCXCancellationFeeTestnet = big.NewInt(38383838) +var TIPXDCXDISABLE = big.NewInt(99999999900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index d5df25b477ed..e80dda351fd6 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -45,6 +45,7 @@ var TIPXDCX = big.NewInt(225000) var TIPXDCXLending = big.NewInt(225000) var TIPXDCXCancellationFee = big.NewInt(225000) var TIPXDCXCancellationFeeTestnet = big.NewInt(225000) +var TIPXDCXDISABLE = big.NewInt(15894900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) diff --git a/common/constants/constants.go.testnet b/common/constants/constants.go.testnet index 5b173297feee..9a0125e701b8 100644 --- a/common/constants/constants.go.testnet +++ b/common/constants/constants.go.testnet @@ -45,6 +45,7 @@ var TIPXDCX = big.NewInt(23779191) var TIPXDCXLending = big.NewInt(23779191) var TIPXDCXCancellationFee = big.NewInt(23779191) var TIPXDCXCancellationFeeTestnet = big.NewInt(23779191) +var TIPXDCXDISABLE = big.NewInt(99999999900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) diff --git a/miner/worker.go b/miner/worker.go index bc3982094e87..6967ae5207eb 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -652,7 +652,7 @@ func (self *worker) commitNewWork() { log.Warn("Can't find coinbase account wallet", "coinbase", self.coinbase, "err", err) return } - if self.config.XDPoS != nil && self.chain.Config().IsTIPXDCX(header.Number) { + if self.config.XDPoS != nil && self.chain.Config().IsTIPXDCXMiner(header.Number) { XDCX := self.eth.GetXDCX() XDCXLending := self.eth.GetXDCXLending() if XDCX != nil && header.Number.Uint64() > self.config.XDPoS.Epoch { @@ -710,8 +710,13 @@ func (self *worker) commitNewWork() { if XDCX.IsSDKNode() { self.chain.AddMatchingResult(tradingTransaction.Hash(), tradingMatchingResults) } + // force adding trading, lending transaction to this block + if tradingTransaction != nil { + specialTxs = append(specialTxs, tradingTransaction) + } } } + if len(lendingInput) > 0 { // lending transaction lendingBatch := &lendingstate.TxLendingBatch{ @@ -735,6 +740,9 @@ func (self *worker) commitNewWork() { if XDCX.IsSDKNode() { self.chain.AddLendingResult(lendingTransaction.Hash(), lendingMatchingResults) } + if lendingTransaction != nil { + specialTxs = append(specialTxs, lendingTransaction) + } } } @@ -756,32 +764,23 @@ func (self *worker) commitNewWork() { if XDCX.IsSDKNode() { self.chain.AddFinalizedTrades(lendingFinalizedTradeTransaction.Hash(), updatedTrades) } + if lendingFinalizedTradeTransaction != nil { + specialTxs = append(specialTxs, lendingFinalizedTradeTransaction) + } } } } + XDCxStateRoot := work.tradingState.IntermediateRoot() + LendingStateRoot := work.lendingState.IntermediateRoot() + txData := append(XDCxStateRoot.Bytes(), LendingStateRoot.Bytes()...) + tx := types.NewTransaction(work.state.GetNonce(self.coinbase), common.HexToAddress(common.TradingStateAddr), big.NewInt(0), txMatchGasLimit, big.NewInt(0), txData) + txStateRoot, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, tx, self.config.ChainId) + if err != nil { + log.Error("Fail to create tx state root", "error", err) + return + } + specialTxs = append(specialTxs, txStateRoot) } - - // force adding trading, lending transaction to this block - if tradingTransaction != nil { - specialTxs = append(specialTxs, tradingTransaction) - } - if lendingTransaction != nil { - specialTxs = append(specialTxs, lendingTransaction) - } - if lendingFinalizedTradeTransaction != nil { - specialTxs = append(specialTxs, lendingFinalizedTradeTransaction) - } - - XDCxStateRoot := work.tradingState.IntermediateRoot() - LendingStateRoot := work.lendingState.IntermediateRoot() - txData := append(XDCxStateRoot.Bytes(), LendingStateRoot.Bytes()...) - tx := types.NewTransaction(work.state.GetNonce(self.coinbase), common.HexToAddress(common.TradingStateAddr), big.NewInt(0), txMatchGasLimit, big.NewInt(0), txData) - txStateRoot, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, tx, self.config.ChainId) - if err != nil { - log.Error("Fail to create tx state root", "error", err) - return - } - specialTxs = append(specialTxs, txStateRoot) } work.commitTransactions(self.mux, feeCapacity, txs, specialTxs, self.chain, self.coinbase) // compute uncles for the new block. diff --git a/params/config.go b/params/config.go index 0904b54786f7..0a46f710f345 100644 --- a/params/config.go +++ b/params/config.go @@ -597,11 +597,10 @@ func (c *ChainConfig) IsTIPNoHalvingMNReward(num *big.Int) bool { return isForked(common.TIPNoHalvingMNReward, num) } func (c *ChainConfig) IsTIPXDCX(num *big.Int) bool { - if common.IsTestnet { - return isForked(common.TIPXDCXTestnet, num) - } else { - return isForked(common.TIPXDCX, num) - } + return isForked(common.TIPXDCX, num) +} +func (c *ChainConfig) IsTIPXDCXMiner(num *big.Int) bool { + return isForked(common.TIPXDCX, num) && !isForked(common.TIPXDCXDISABLE, num) } func (c *ChainConfig) IsTIPXDCXLending(num *big.Int) bool { From bbe08ac6e6da32408e15d738cd34b43ca8e9a505 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 6 Mar 2024 14:10:08 +0800 Subject: [PATCH 46/59] core/state: add function GetStorageRoot --- core/state/state_object.go | 4 ++++ core/state/statedb.go | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/core/state/state_object.go b/core/state/state_object.go index 6d50461f1587..a5c7ce0bc964 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -393,6 +393,10 @@ func (self *stateObject) Nonce() uint64 { return self.data.Nonce } +func (self *stateObject) Root() common.Hash { + return self.data.Root +} + // Never called, but must be present to allow stateObject to be used // as a vm.Account interface that also satisfies the vm.ContractRef // interface. Interfaces are awesome. diff --git a/core/state/statedb.go b/core/state/statedb.go index 07bcf7596b54..8b374fda6532 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -221,6 +221,16 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 { return 0 } +// GetStorageRoot retrieves the storage root from the given address or empty +// if object not found. +func (self *StateDB) GetStorageRoot(addr common.Address) common.Hash { + stateObject := self.getStateObject(addr) + if stateObject != nil { + return stateObject.Root() + } + return common.Hash{} +} + func (self *StateDB) GetCode(addr common.Address) []byte { stateObject := self.getStateObject(addr) if stateObject != nil { From ece58f00b51fa31d76e934d468ab7e42a86db518 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 6 Mar 2024 14:11:15 +0800 Subject: [PATCH 47/59] ethapi: add method eth_getAccountInfo --- core/state/statedb.go | 30 ++++++++++++++++++++++++++++++ internal/ethapi/api.go | 18 ++++++++++++++++++ internal/jsre/deps/web3.js | 8 ++++++++ 3 files changed, 56 insertions(+) diff --git a/core/state/statedb.go b/core/state/statedb.go index 8b374fda6532..4bedebe8b3c3 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -83,6 +83,14 @@ type StateDB struct { lock sync.Mutex } +type AccountInfo struct { + CodeSize int + Nonce uint64 + Balance *big.Int + CodeHash common.Hash + StorageHash common.Hash +} + func (self *StateDB) SubRefund(gas uint64) { self.journal = append(self.journal, refundChange{ prev: self.refund}) @@ -262,6 +270,28 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash { return common.BytesToHash(stateObject.CodeHash()) } +func (self *StateDB) GetAccountInfo(addr common.Address) *AccountInfo { + result := AccountInfo{} + + stateObject := self.getStateObject(addr) + if stateObject == nil { + result.Balance = common.Big0 + return &result + } + + if stateObject.code != nil { + result.CodeSize = len(stateObject.code) + } else { + result.CodeSize, _ = self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash())) + } + result.Nonce = stateObject.Nonce() + result.Balance = stateObject.Balance() + result.CodeHash = common.BytesToHash(stateObject.CodeHash()) + result.StorageHash = stateObject.Root() + + return &result +} + func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash { stateObject := self.getStateObject(addr) if stateObject != nil { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 242584e863d6..519d4490ab24 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -623,6 +623,24 @@ func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Addres return code, state.Error() } +// GetAccountInfo returns the information at the given address in the state for the given block number. +func (s *PublicBlockChainAPI) GetAccountInfo(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (map[string]interface{}, error) { + state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return nil, err + } + info := state.GetAccountInfo(address) + result := map[string]interface{}{ + "address": address, + "balance": (*hexutil.Big)(info.Balance), + "codeSize": info.CodeSize, + "codeHash": info.CodeHash, + "nonce": info.Nonce, + "storageHash": info.StorageHash, + } + return result, nil +} + // GetStorageAt returns the storage from the state at the given address, key and // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // numbers are also allowed. diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js index 95b0d1aaf734..5c7236ed1442 100644 --- a/internal/jsre/deps/web3.js +++ b/internal/jsre/deps/web3.js @@ -5329,6 +5329,13 @@ var methods = function () { inputFormatter: [formatters.inputAddressFormatter, formatters.inputDefaultBlockNumberFormatter] }); + var getAccountInfo = new Method({ + name: 'getAccountInfo', + call: 'eth_getAccountInfo', + params: 2, + inputFormatter: [formatters.inputAddressFormatter, formatters.inputDefaultBlockNumberFormatter] + }); + var getBlock = new Method({ name: 'getBlock', call: blockCall, @@ -5513,6 +5520,7 @@ var methods = function () { getBalance, getStorageAt, getCode, + getAccountInfo, getBlock, getBlockSigners, getStakerROI, From ae10d7090cea81f7137ef52779172cddcb726e23 Mon Sep 17 00:00:00 2001 From: Wanwiset Peerapatanapokin Date: Thu, 7 Mar 2024 10:40:55 +0400 Subject: [PATCH 48/59] Rollback privacy (#477) Rollback the previous test removal or privacy package on devnet. This reverts commit 4f79e535b37dc85144e2d38c2f2570b0ce4305b2. This reverts commit 5a2722c268ec4f03f3117b1af2dee2411af4b13a. --- core/vm/contracts.go | 42 + core/vm/privacy/bulletproof.go | 1400 +++++++++++++++++++++++++++ core/vm/privacy/bulletproof.json | 1 + core/vm/privacy/bulletproof_test.go | 527 ++++++++++ core/vm/privacy/ringct.go | 632 ++++++++++++ core/vm/privacy/ringct_test.go | 324 +++++++ 6 files changed, 2926 insertions(+) create mode 100644 core/vm/privacy/bulletproof.go create mode 100644 core/vm/privacy/bulletproof.json create mode 100644 core/vm/privacy/bulletproof_test.go create mode 100644 core/vm/privacy/ringct.go create mode 100644 core/vm/privacy/ringct_test.go diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 1f03559e2a87..067bc7018ab4 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -22,6 +22,8 @@ import ( "errors" "math/big" + "github.com/XinFinOrg/XDPoSChain/core/vm/privacy" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -61,6 +63,8 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{6}): &bn256AddByzantium{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{}, common.BytesToAddress([]byte{8}): &bn256PairingByzantium{}, + common.BytesToAddress([]byte{30}): &ringSignatureVerifier{}, + common.BytesToAddress([]byte{40}): &bulletproofVerifier{}, common.BytesToAddress([]byte{41}): &XDCxLastPrice{}, common.BytesToAddress([]byte{42}): &XDCxEpochPrice{}, } @@ -77,6 +81,8 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{30}): &ringSignatureVerifier{}, + common.BytesToAddress([]byte{40}): &bulletproofVerifier{}, common.BytesToAddress([]byte{41}): &XDCxLastPrice{}, common.BytesToAddress([]byte{42}): &XDCxEpochPrice{}, } @@ -420,6 +426,42 @@ func runBn256Pairing(input []byte) ([]byte, error) { return false32Byte, nil } +type ringSignatureVerifier struct{} +type bulletproofVerifier struct{} + +func (c *bulletproofVerifier) RequiredGas(input []byte) uint64 { + //the gas should depends on the ringsize + return 100000 +} + +func (c *ringSignatureVerifier) RequiredGas(input []byte) uint64 { + //the gas should depends on the ringsize + return 100000 +} + +func (c *ringSignatureVerifier) Run(proof []byte) ([]byte, error) { + der, err := privacy.Deserialize(proof) + if err != nil { + return []byte{}, errors.New("Fail to deserialize proof") + } + if !privacy.Verify(der, false) { + return []byte{}, errors.New("Fail to verify ring signature") + } + return []byte{}, nil +} + +func (c *bulletproofVerifier) Run(proof []byte) ([]byte, error) { + mrp := new(privacy.MultiRangeProof) + if mrp.Deserialize(proof) != nil { + return []byte{}, errors.New("failed to deserialize bulletproofs") + } + + if !privacy.MRPVerify(mrp) { + return []byte{}, errors.New("failed to verify bulletproof") + } + return []byte{}, nil +} + // bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve // conforming to Istanbul consensus rules. type bn256PairingIstanbul struct{} diff --git a/core/vm/privacy/bulletproof.go b/core/vm/privacy/bulletproof.go new file mode 100644 index 000000000000..f620d230b2ca --- /dev/null +++ b/core/vm/privacy/bulletproof.go @@ -0,0 +1,1400 @@ +package privacy + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "errors" + "fmt" + "math" + "math/big" + "strconv" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/log" + + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/btcsuite/btcd/btcec" +) + +type Bulletproof struct { + proofData []byte +} + +var EC CryptoParams +var VecLength = 512 // support maximum 8 spending value, each 64 bit (gwei is unit) +var curve elliptic.Curve = crypto.S256() + +/* +Implementation of BulletProofs +*/ +type ECPoint struct { + X, Y *big.Int +} + +func (p *ECPoint) toECPubKey() *ecdsa.PublicKey { + return &ecdsa.PublicKey{curve, p.X, p.Y} +} + +func toECPoint(key *ecdsa.PublicKey) *ECPoint { + return &ECPoint{key.X, key.Y} +} + +// Equal returns true if points p (self) and p2 (arg) are the same. +func (p ECPoint) Equal(p2 ECPoint) bool { + if p.X.Cmp(p2.X) == 0 && p2.Y.Cmp(p2.Y) == 0 { + return true + } + return false +} + +// Mult multiplies point p by scalar s and returns the resulting point +func (p ECPoint) Mult(s *big.Int) ECPoint { + modS := new(big.Int).Mod(s, EC.N) + X, Y := EC.C.ScalarMult(p.X, p.Y, modS.Bytes()) + return ECPoint{X, Y} +} + +// Add adds points p and p2 and returns the resulting point +func (p ECPoint) Add(p2 ECPoint) ECPoint { + X, Y := EC.C.Add(p.X, p.Y, p2.X, p2.Y) + return ECPoint{X, Y} +} + +// Neg returns the additive inverse of point p +func (p ECPoint) Neg() ECPoint { + negY := new(big.Int).Neg(p.Y) + modValue := negY.Mod(negY, EC.C.Params().P) // mod P is fine here because we're describing a curve point + return ECPoint{p.X, modValue} +} + +type CryptoParams struct { + C elliptic.Curve // curve + KC *btcec.KoblitzCurve // curve + BPG []ECPoint // slice of gen 1 for BP + BPH []ECPoint // slice of gen 2 for BP + N *big.Int // scalar prime + U ECPoint // a point that is a fixed group element with an unknown discrete-log relative to g,h + V int // Vector length + G ECPoint // G value for commitments of a single value + H ECPoint // H value for commitments of a single value +} + +func (c CryptoParams) Zero() ECPoint { + return ECPoint{big.NewInt(0), big.NewInt(0)} +} + +func check(e error) { + if e != nil { + panic(e) + } +} + +/* +Vector Pedersen Commitment +Given an array of values, we commit the array with different generators +for each element and for each randomness. +*/ +func VectorPCommit(value []*big.Int) (ECPoint, []*big.Int) { + R := make([]*big.Int, EC.V) + + commitment := EC.Zero() + + for i := 0; i < EC.V; i++ { + r, err := rand.Int(rand.Reader, EC.N) + check(err) + + R[i] = r + + modValue := new(big.Int).Mod(value[i], EC.N) + + // mG, rH + lhsX, lhsY := EC.C.ScalarMult(EC.BPG[i].X, EC.BPG[i].Y, modValue.Bytes()) + rhsX, rhsY := EC.C.ScalarMult(EC.BPH[i].X, EC.BPH[i].Y, r.Bytes()) + + commitment = commitment.Add(ECPoint{lhsX, lhsY}).Add(ECPoint{rhsX, rhsY}) + } + + return commitment, R +} + +/* +Two Vector P Commit +Given an array of values, we commit the array with different generators +for each element and for each randomness. +*/ +func TwoVectorPCommit(a []*big.Int, b []*big.Int) ECPoint { + if len(a) != len(b) { + log.Debug("TwoVectorPCommit: Arrays not of the same length", "len(a)", len(a), "len(b)", len(b)) + } + + commitment := EC.Zero() + + for i := 0; i < EC.V; i++ { + commitment = commitment.Add(EC.BPG[i].Mult(a[i])).Add(EC.BPH[i].Mult(b[i])) + } + + return commitment +} + +/* +Vector Pedersen Commitment with Gens +Given an array of values, we commit the array with different generators +for each element and for each randomness. +We also pass in the Generators we want to use +*/ +func TwoVectorPCommitWithGens(G, H []ECPoint, a, b []*big.Int) ECPoint { + if len(G) != len(H) || len(G) != len(a) || len(a) != len(b) { + log.Debug("TwoVectorPCommitWithGens: Arrays not of the same length", "len(G)", len(G), "len(H)", len(H), "len(a)", len(a), "len(b)", len(b)) + } + + commitment := EC.Zero() + + for i := 0; i < len(G); i++ { + modA := new(big.Int).Mod(a[i], EC.N) + modB := new(big.Int).Mod(b[i], EC.N) + + commitment = commitment.Add(G[i].Mult(modA)).Add(H[i].Mult(modB)) + } + + return commitment +} + +// The length here always has to be a power of two +func InnerProduct(a []*big.Int, b []*big.Int) *big.Int { + if len(a) != len(b) { + log.Debug("InnerProduct: Arrays not of the same length", "len(a)", len(a), "len(b", len(b)) + } + + c := big.NewInt(0) + + for i := range a { + tmp1 := new(big.Int).Mul(a[i], b[i]) + c = new(big.Int).Add(c, new(big.Int).Mod(tmp1, EC.N)) + } + + return new(big.Int).Mod(c, EC.N) +} + +func VectorAdd(v []*big.Int, w []*big.Int) []*big.Int { + if len(v) != len(w) { + log.Debug("VectorAdd: Arrays not of the same length", "len(v)", len(v), "len(w)", len(w)) + } + result := make([]*big.Int, len(v)) + + for i := range v { + result[i] = new(big.Int).Mod(new(big.Int).Add(v[i], w[i]), EC.N) + } + + return result +} + +func VectorHadamard(v, w []*big.Int) []*big.Int { + if len(v) != len(w) { + log.Debug("VectorHadamard: Arrays not of the same length", "len(v)", len(v), "len(w)", len(w)) + } + + result := make([]*big.Int, len(v)) + + for i := range v { + result[i] = new(big.Int).Mod(new(big.Int).Mul(v[i], w[i]), EC.N) + } + + return result +} + +func VectorAddScalar(v []*big.Int, s *big.Int) []*big.Int { + result := make([]*big.Int, len(v)) + + for i := range v { + result[i] = new(big.Int).Mod(new(big.Int).Add(v[i], s), EC.N) + } + + return result +} + +func ScalarVectorMul(v []*big.Int, s *big.Int) []*big.Int { + result := make([]*big.Int, len(v)) + + for i := range v { + result[i] = new(big.Int).Mod(new(big.Int).Mul(v[i], s), EC.N) + } + + return result +} + +func HashPointsToBytes(points []ECPoint) []byte { + input := []byte{} + + for index := 0; index < len(points); index++ { + pointInByte := append(PadTo32Bytes(points[index].X.Bytes()), PadTo32Bytes(points[index].Y.Bytes())...) + input = append(input, pointInByte...) + } + + return crypto.Keccak256(input) +} + +/* +InnerProd Proof +This stores the argument values +*/ +type InnerProdArg struct { + L []ECPoint + R []ECPoint + A *big.Int + B *big.Int + + Challenges []*big.Int +} + +func GenerateNewParams(G, H []ECPoint, x *big.Int, L, R, P ECPoint) ([]ECPoint, []ECPoint, ECPoint) { + nprime := len(G) / 2 + + Gprime := make([]ECPoint, nprime) + Hprime := make([]ECPoint, nprime) + + xinv := new(big.Int).ModInverse(x, EC.N) + + // Gprime = xinv * G[:nprime] + x*G[nprime:] + // Hprime = x * H[:nprime] + xinv*H[nprime:] + + for i := range Gprime { + //fmt.Printf("i: %d && i+nprime: %d\n", i, i+nprime) + Gprime[i] = G[i].Mult(xinv).Add(G[i+nprime].Mult(x)) + Hprime[i] = H[i].Mult(x).Add(H[i+nprime].Mult(xinv)) + } + + x2 := new(big.Int).Mod(new(big.Int).Mul(x, x), EC.N) + xinv2 := new(big.Int).ModInverse(x2, EC.N) + + Pprime := L.Mult(x2).Add(P).Add(R.Mult(xinv2)) // x^2 * L + P + xinv^2 * R + + return Gprime, Hprime, Pprime +} + +/* + Inner Product Argument + +Proves that =c +This is a building block for BulletProofs +*/ +func InnerProductProveSub(proof InnerProdArg, G, H []ECPoint, a []*big.Int, b []*big.Int, u ECPoint, P ECPoint) InnerProdArg { + if len(a) == 1 { + // Prover sends a & b + proof.A = a[0] + proof.B = b[0] + return proof + } + + curIt := int(math.Log2(float64(len(a)))) - 1 + + nprime := len(a) / 2 + cl := InnerProduct(a[:nprime], b[nprime:]) // either this line + cr := InnerProduct(a[nprime:], b[:nprime]) // or this line + L := TwoVectorPCommitWithGens(G[nprime:], H[:nprime], a[:nprime], b[nprime:]).Add(u.Mult(cl)) + R := TwoVectorPCommitWithGens(G[:nprime], H[nprime:], a[nprime:], b[:nprime]).Add(u.Mult(cr)) + + proof.L[curIt] = L + proof.R[curIt] = R + + // prover sends L & R and gets a challenge + // LvalInBytes := append(PadTo32Bytes(L.X.Bytes()), PadTo32Bytes(L.Y.Bytes())...) + // RvalInBytes := append(PadTo32Bytes(R.X.Bytes()), PadTo32Bytes(R.Y.Bytes())...) + // input := append(LvalInBytes, RvalInBytes...) + // s256 := crypto.Keccak256(input) + s256 := HashPointsToBytes([]ECPoint{L, R}) + + x := new(big.Int).SetBytes(s256[:]) + + proof.Challenges[curIt] = x + + Gprime, Hprime, Pprime := GenerateNewParams(G, H, x, L, R, P) + + xinv := new(big.Int).ModInverse(x, EC.N) + + // or these two lines + aprime := VectorAdd( + ScalarVectorMul(a[:nprime], x), + ScalarVectorMul(a[nprime:], xinv)) + bprime := VectorAdd( + ScalarVectorMul(b[:nprime], xinv), + ScalarVectorMul(b[nprime:], x)) + + return InnerProductProveSub(proof, Gprime, Hprime, aprime, bprime, u, Pprime) +} + +// rpresult.IPP = InnerProductProve(left, right, that, P, EC.U, EC.BPG, HPrime) +func InnerProductProve(a []*big.Int, b []*big.Int, c *big.Int, P, U ECPoint, G, H []ECPoint) InnerProdArg { + loglen := int(math.Log2(float64(len(a)))) + + challenges := make([]*big.Int, loglen+1) + Lvals := make([]ECPoint, loglen) + Rvals := make([]ECPoint, loglen) + + runningProof := InnerProdArg{ + Lvals, + Rvals, + big.NewInt(0), + big.NewInt(0), + challenges} + + // randomly generate an x value from public data + // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) + // x := crypto.Keccak256(input) + x := HashPointsToBytes([]ECPoint{P}) + + runningProof.Challenges[loglen] = new(big.Int).SetBytes(x[:]) + + Pprime := P.Add(U.Mult(new(big.Int).Mul(new(big.Int).SetBytes(x[:]), c))) + + ux := U.Mult(new(big.Int).SetBytes(x[:])) + //fmt.Printf("Prover Pprime value to run sub off of: %s\n", Pprime) + + return InnerProductProveSub(runningProof, G, H, a, b, ux, Pprime) +} + +/* + Inner Product Verify + +Given a inner product proof, verifies the correctness of the proof +Since we're using the Fiat-Shamir transform, we need to verify all x hash computations, +all g' and h' computations +P : the Pedersen commitment we are verifying is a commitment to the innner product +ipp : the proof +*/ +func InnerProductVerify(c *big.Int, P, U ECPoint, G, H []ECPoint, ipp InnerProdArg) bool { + //fmt.Println("Verifying Inner Product Argument") + //fmt.Printf("Commitment Value: %s \n", P) + // s1 := sha256.Sum256([]byte(P.X.String() + P.Y.String())) + + // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) + // s1 := crypto.Keccak256(input) + s1 := HashPointsToBytes([]ECPoint{P}) + + chal1 := new(big.Int).SetBytes(s1[:]) + ux := U.Mult(chal1) + curIt := len(ipp.Challenges) - 1 + + if ipp.Challenges[curIt].Cmp(chal1) != 0 { + log.Info("Initial Challenge Failed") + return false + } + + curIt -= 1 + + Gprime := G + Hprime := H + Pprime := P.Add(ux.Mult(c)) // line 6 from protocol 1 + //fmt.Printf("New Commitment value with u^cx: %s \n", Pprime) + + for curIt >= 0 { + Lval := ipp.L[curIt] + Rval := ipp.R[curIt] + + // prover sends L & R and gets a challenge + // s256 := sha256.Sum256([]byte( + // Lval.X.String() + Lval.Y.String() + + // Rval.X.String() + Rval.Y.String())) + // LvalInBytes := append(PadTo32Bytes(Lval.X.Bytes()), PadTo32Bytes(Lval.Y.Bytes())...) + // RvalInBytes := append(PadTo32Bytes(Rval.X.Bytes()), PadTo32Bytes(Rval.Y.Bytes())...) + // input := append(LvalInBytes, RvalInBytes...) + // s256 := crypto.Keccak256(input) + s256 := HashPointsToBytes([]ECPoint{Lval, Rval}) + + chal2 := new(big.Int).SetBytes(s256[:]) + + if ipp.Challenges[curIt].Cmp(chal2) != 0 { + log.Info("Challenge verification failed", "index", strconv.Itoa(curIt)) + return false + } + + Gprime, Hprime, Pprime = GenerateNewParams(Gprime, Hprime, chal2, Lval, Rval, Pprime) + curIt -= 1 + } + ccalc := new(big.Int).Mod(new(big.Int).Mul(ipp.A, ipp.B), EC.N) + + Pcalc1 := Gprime[0].Mult(ipp.A) + Pcalc2 := Hprime[0].Mult(ipp.B) + Pcalc3 := ux.Mult(ccalc) + Pcalc := Pcalc1.Add(Pcalc2).Add(Pcalc3) + + return Pprime.Equal(Pcalc) +} + +/* Inner Product Verify Fast +Given a inner product proof, verifies the correctness of the proof. Does the same as above except +we replace n separate exponentiations with a single multi-exponentiation. +*/ + +func InnerProductVerifyFast(c *big.Int, P, U ECPoint, G, H []ECPoint, ipp InnerProdArg) bool { + // input := append(PadTo32Bytes(P.X.Bytes()), PadTo32Bytes(P.Y.Bytes())...) + // s1 := crypto.Keccak256(input) + s1 := HashPointsToBytes([]ECPoint{P}) + + chal1 := new(big.Int).SetBytes(s1[:]) + ux := U.Mult(chal1) + curIt := len(ipp.Challenges) - 1 + + // check all challenges + if ipp.Challenges[curIt].Cmp(chal1) != 0 { + log.Debug("Initial Challenge Failed") + return false + } + + for j := curIt - 1; j >= 0; j-- { + Lval := ipp.L[j] + Rval := ipp.R[j] + + // prover sends L & R and gets a challenge + // LvalInBytes := append(PadTo32Bytes(Lval.X.Bytes()), PadTo32Bytes(Lval.Y.Bytes())...) + // RvalInBytes := append(PadTo32Bytes(Rval.X.Bytes()), PadTo32Bytes(Rval.Y.Bytes())...) + // input := append(LvalInBytes, RvalInBytes...) + // s256 := crypto.Keccak256(input) + s256 := HashPointsToBytes([]ECPoint{Lval, Rval}) + + chal2 := new(big.Int).SetBytes(s256[:]) + + if ipp.Challenges[j].Cmp(chal2) != 0 { + log.Debug("Challenge verification failed", "index", strconv.Itoa(j)) + return false + } + } + // begin computing + + curIt -= 1 + Pprime := P.Add(ux.Mult(c)) // line 6 from protocol 1 + + tmp1 := EC.Zero() + for j := curIt; j >= 0; j-- { + x2 := new(big.Int).Exp(ipp.Challenges[j], big.NewInt(2), EC.N) + x2i := new(big.Int).ModInverse(x2, EC.N) + //fmt.Println(tmp1) + tmp1 = ipp.L[j].Mult(x2).Add(ipp.R[j].Mult(x2i)).Add(tmp1) + //fmt.Println(tmp1) + } + rhs := Pprime.Add(tmp1) + //rhs=P+c*ux + L*xw+R*x2i 公式29 + + sScalars := make([]*big.Int, EC.V) + invsScalars := make([]*big.Int, EC.V) + + for i := 0; i < EC.V; i++ { + si := big.NewInt(1) + for j := curIt; j >= 0; j-- { + // original challenge if the jth bit of i is 1, inverse challenge otherwise + chal := ipp.Challenges[j] + if big.NewInt(int64(i)).Bit(j) == 0 { + chal = new(big.Int).ModInverse(chal, EC.N) + } + // fmt.Printf("Challenge raised to value: %d\n", chal) + si = new(big.Int).Mod(new(big.Int).Mul(si, chal), EC.N) + } + //fmt.Printf("Si value: %d\n", si) + sScalars[i] = si + invsScalars[i] = new(big.Int).ModInverse(si, EC.N) + } + + ccalc := new(big.Int).Mod(new(big.Int).Mul(ipp.A, ipp.B), EC.N) + lhs := TwoVectorPCommitWithGens(G, H, ScalarVectorMul(sScalars, ipp.A), ScalarVectorMul(invsScalars, ipp.B)).Add(ux.Mult(ccalc)) + + if !rhs.Equal(lhs) { + log.Info("IPVerify - Final Commitment checking failed") + return false + } + + return true +} + +// from here: https://play.golang.org/p/zciRZvD0Gr with a fix +func PadLeft(str, pad string, l int) string { + strCopy := str + for len(strCopy) < l { + strCopy = pad + strCopy + } + + return strCopy +} + +func STRNot(str string) string { + result := "" + + for _, i := range str { + if i == '0' { + result += "1" + } else { + result += "0" + } + } + return result +} + +func StrToBigIntArray(str string) []*big.Int { + result := make([]*big.Int, len(str)) + + for i := range str { + t, success := new(big.Int).SetString(string(str[i]), 10) + if success { + result[i] = t + } + } + + return result +} + +func reverse(l []*big.Int) []*big.Int { + result := make([]*big.Int, len(l)) + + for i := range l { + result[i] = l[len(l)-i-1] + } + + return result +} + +func PowerVector(l int, base *big.Int) []*big.Int { + result := make([]*big.Int, l) + + for i := 0; i < l; i++ { + result[i] = new(big.Int).Exp(base, big.NewInt(int64(i)), EC.N) + } + + return result +} + +func RandVector(l int) []*big.Int { + result := make([]*big.Int, l) + + for i := 0; i < l; i++ { + x, err := rand.Int(rand.Reader, EC.N) + check(err) + result[i] = x + } + + return result +} + +func VectorSum(y []*big.Int) *big.Int { + result := big.NewInt(0) + + for _, j := range y { + result = new(big.Int).Mod(new(big.Int).Add(result, j), EC.N) + } + + return result +} + +type RangeProof struct { + Comm ECPoint + A ECPoint + S ECPoint + T1 ECPoint + T2 ECPoint + Tau *big.Int + Th *big.Int + Mu *big.Int + IPP InnerProdArg + + // challenges + Cy *big.Int + Cz *big.Int + Cx *big.Int +} + +/* +Delta is a helper function that is used in the range proof +\delta(y, z) = (z-z^2)<1^n, y^n> - z^3<1^n, 2^n> +*/ + +func Delta(y []*big.Int, z *big.Int) *big.Int { + result := big.NewInt(0) + + // (z-z^2)<1^n, y^n> + z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N) + t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N) + t2 := new(big.Int).Mod(new(big.Int).Mul(t1, VectorSum(y)), EC.N) + + // z^3<1^n, 2^n> + z3 := new(big.Int).Mod(new(big.Int).Mul(z2, z), EC.N) + po2sum := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(EC.V)), EC.N), big.NewInt(1)) + t3 := new(big.Int).Mod(new(big.Int).Mul(z3, po2sum), EC.N) + + result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) + + return result +} + +// Calculates (aL - z*1^n) + sL*x +func CalculateL(aL, sL []*big.Int, z, x *big.Int) []*big.Int { + result := make([]*big.Int, len(aL)) + + tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z)) + tmp2 := ScalarVectorMul(sL, x) + + result = VectorAdd(tmp1, tmp2) + + return result +} + +func CalculateR(aR, sR, y, po2 []*big.Int, z, x *big.Int) []*big.Int { + if len(aR) != len(sR) || len(aR) != len(y) || len(y) != len(po2) { + log.Info("CalculateR: Arrays not of the same length") + } + + result := make([]*big.Int, len(aR)) + + z2 := new(big.Int).Exp(z, big.NewInt(2), EC.N) + tmp11 := VectorAddScalar(aR, z) + tmp12 := ScalarVectorMul(sR, x) + tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12)) + tmp2 := ScalarVectorMul(po2, z2) + + result = VectorAdd(tmp1, tmp2) + + return result +} + +// Calculates (aL - z*1^n) + sL*x +func CalculateLMRP(aL, sL []*big.Int, z, x *big.Int) []*big.Int { + result := make([]*big.Int, len(aL)) + + tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z)) + tmp2 := ScalarVectorMul(sL, x) + + result = VectorAdd(tmp1, tmp2) + + return result +} + +func CalculateRMRP(aR, sR, y, zTimesTwo []*big.Int, z, x *big.Int) []*big.Int { + if len(aR) != len(sR) || len(aR) != len(y) || len(y) != len(zTimesTwo) { + log.Info("CalculateRMRP: Arrays not of the same length") + } + + result := make([]*big.Int, len(aR)) + + tmp11 := VectorAddScalar(aR, z) + tmp12 := ScalarVectorMul(sR, x) + tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12)) + + result = VectorAdd(tmp1, zTimesTwo) + + return result +} + +/* +DeltaMRP is a helper function that is used in the multi range proof +\delta(y, z) = (z-z^2)<1^n, y^n> - \sum_j z^3+j<1^n, 2^n> +*/ + +func DeltaMRP(y []*big.Int, z *big.Int, m int) *big.Int { + result := big.NewInt(0) + + // (z-z^2)<1^n, y^n> + z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N) + t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N) + t2 := new(big.Int).Mod(new(big.Int).Mul(t1, VectorSum(y)), EC.N) + + // \sum_j z^3+j<1^n, 2^n> + // <1^n, 2^n> = 2^n - 1 + po2sum := new(big.Int).Sub( + new(big.Int).Exp( + big.NewInt(2), big.NewInt(int64(EC.V/m)), EC.N), big.NewInt(1)) + t3 := big.NewInt(0) + + for j := 0; j < m; j++ { + zp := new(big.Int).Exp(z, big.NewInt(3+int64(j)), EC.N) + tmp1 := new(big.Int).Mod(new(big.Int).Mul(zp, po2sum), EC.N) + t3 = new(big.Int).Mod(new(big.Int).Add(t3, tmp1), EC.N) + } + + result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N) + + return result +} + +type MultiRangeProof struct { + Comms []ECPoint + A ECPoint + S ECPoint + T1 ECPoint + T2 ECPoint + Tau *big.Int + Th *big.Int + Mu *big.Int + IPP InnerProdArg + + // challenges + Cy *big.Int + Cz *big.Int + Cx *big.Int +} + +func serializePointArray(pa []ECPoint, serializeSize bool) []byte { + ret := []byte{} + + if serializeSize { + size := make([]byte, 4) + binary.BigEndian.PutUint32(size, uint32(len(pa))) + ret = append(ret, size[:]...) + } + + for i := 0; i < len(pa); i++ { + sp := SerializeCompressed(pa[i].toECPubKey()) + ret = append(ret, sp[:]...) + } + return ret +} + +func deserializePointArray(input []byte, numPoint uint32) ([]ECPoint, error) { + if len(input) <= 4 { + return []ECPoint{}, errors.New("input data invalid") + } + numPointSize := uint32(0) + if numPoint == 0 { + numPointSize = 4 + numPoint = binary.BigEndian.Uint32(input[0:4]) + } + if uint32(len(input)) < (numPointSize + 33*numPoint) { + return []ECPoint{}, errors.New("input data too short") + } + ret := make([]ECPoint, numPoint) + offset := numPointSize + for i := 0; i < int(numPoint); i++ { + compressed := DeserializeCompressed(curve, input[offset:offset+33]) + if compressed == nil { + return ret, errors.New("invalid input data") + } + ret[i] = *toECPoint(compressed) + offset += 33 + } + return ret, nil +} + +func (ipp *InnerProdArg) Serialize() []byte { + proof := []byte{} + + spa := serializePointArray(ipp.L, false) + proof = append(proof, spa[:]...) + + spa = serializePointArray(ipp.R, false) + proof = append(proof, spa[:]...) + + if ipp.A.Cmp(big.NewInt(0)) < 0 { + ipp.A.Mod(ipp.A, EC.N) + } + sp := PadTo32Bytes(ipp.A.Bytes()) + proof = append(proof, sp[:]...) + + sp = PadTo32Bytes(ipp.B.Bytes()) + proof = append(proof, sp[:]...) + + for i := 0; i < len(ipp.Challenges); i++ { + sp = PadTo32Bytes(ipp.Challenges[i].Bytes()) + proof = append(proof, sp[:]...) + } + + return proof +} + +func (ipp *InnerProdArg) Deserialize(proof []byte, numChallenges int) error { + if len(proof) <= 12 { + return errors.New("proof data too short") + } + offset := 0 + L, err := deserializePointArray(proof[:], uint32(numChallenges)-1) + if err != nil { + return err + } + ipp.L = append(ipp.L, L[:]...) + offset += len(L) * 33 + + R, err := deserializePointArray(proof[offset:], uint32(numChallenges)-1) + if err != nil { + return err + } + + ipp.R = append(ipp.R, R[:]...) + offset += len(R) * 33 + + if len(proof) <= offset+64+4 { + return errors.New("proof data too short") + } + + ipp.A = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + ipp.B = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + if len(proof) <= (offset + 32*numChallenges) { + return errors.New("input data too short") + } + for i := 0; i < numChallenges; i++ { + ipp.Challenges = append(ipp.Challenges, new(big.Int).SetBytes(proof[offset:offset+32])) + offset += 32 + } + return nil +} + +func (mrp *MultiRangeProof) Serialize() []byte { + proof := []byte{} + + serializedPA := serializePointArray(mrp.Comms, true) + proof = append(proof, serializedPA[:]...) + + sp := SerializeCompressed(mrp.A.toECPubKey()) + proof = append(proof, sp[:]...) + + sp = SerializeCompressed(mrp.S.toECPubKey()) + proof = append(proof, sp[:]...) + + sp = SerializeCompressed(mrp.T1.toECPubKey()) + proof = append(proof, sp[:]...) + + sp = SerializeCompressed(mrp.T2.toECPubKey()) + proof = append(proof, sp[:]...) + + //Tau, Th, Mu + sp = PadTo32Bytes(mrp.Tau.Bytes()) + proof = append(proof, sp[:]...) + + if mrp.Th.Cmp(big.NewInt(0)) < 0 { + mrp.Th.Mod(mrp.Th, EC.N) + } + sp = PadTo32Bytes(mrp.Th.Bytes()) + proof = append(proof, sp[:]...) + + sp = PadTo32Bytes(mrp.Mu.Bytes()) + proof = append(proof, sp[:]...) + + //challenges + sp = mrp.IPP.Serialize() + proof = append(proof, sp[:]...) + + //challenges + sp = PadTo32Bytes(mrp.Cy.Bytes()) + proof = append(proof, sp[:]...) + + sp = PadTo32Bytes(mrp.Cz.Bytes()) + proof = append(proof, sp[:]...) + + sp = PadTo32Bytes(mrp.Cx.Bytes()) + proof = append(proof, sp[:]...) + + return proof +} + +func (mrp *MultiRangeProof) Deserialize(proof []byte) error { + Cs, err := deserializePointArray(proof[:], 0) + if err != nil { + return err + } + mrp.Comms = append(mrp.Comms, Cs[:]...) + + offset := 4 + len(Cs)*33 + + if len(proof) <= offset+4+4*33+6*32 { + return errors.New("invalid input data") + } + compressed := DeserializeCompressed(curve, proof[offset:offset+33]) + if compressed == nil { + return errors.New("failed to decode A") + } + offset += 33 + mrp.A = *toECPoint(compressed) + + compressed = DeserializeCompressed(curve, proof[offset:offset+33]) + if compressed == nil { + return errors.New("failed to decode S") + } + offset += 33 + mrp.S = *toECPoint(compressed) + + compressed = DeserializeCompressed(curve, proof[offset:offset+33]) + if compressed == nil { + return errors.New("failed to decode T2") + } + offset += 33 + mrp.T1 = *toECPoint(compressed) + + compressed = DeserializeCompressed(curve, proof[offset:offset+33]) + if compressed == nil { + return errors.New("failed to decode T2") + } + offset += 33 + mrp.T2 = *toECPoint(compressed) + + mrp.Tau = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + mrp.Th = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + mrp.Mu = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + numChallenges := int(math.Log2(float64(len(mrp.Comms)*bitsPerValue))) + 1 + mrp.IPP.Deserialize(proof[offset:], numChallenges) + offset += len(mrp.IPP.L)*33 + len(mrp.IPP.R)*33 + len(mrp.IPP.Challenges)*32 + 2*32 + + mrp.Cy = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + mrp.Cz = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + mrp.Cx = new(big.Int).SetBytes(proof[offset : offset+32]) + offset += 32 + + return nil +} + +func pedersenCommitment(gamma *big.Int, value *big.Int) ECPoint { + return EC.G.Mult(value).Add(EC.H.Mult(gamma)) +} + +var MAX_64_BITS = new(big.Int).SetUint64(0xFFFFFFFFFFFFFFFF) + +/* +MultiRangeProof Prove +Takes in a list of values and provides an aggregate +range proof for all the values. +changes: + + all values are concatenated + r(x) is computed differently + tau_x calculation is different + delta calculation is different + +{(g, h \in G, \textbf{V} \in G^m ; \textbf{v, \gamma} \in Z_p^m) : + + V_j = h^{\gamma_j}g^{v_j} \wedge v_j \in [0, 2^n - 1] \forall j \in [1, m]} +*/ +var bitsPerValue = 64 + +func MRPProve(values []*big.Int) (MultiRangeProof, error) { + var acceptedInputNumber bool + + MRPResult := MultiRangeProof{} + + m := len(values) + + if m == 1 || m == 2 || m == 4 || m == 8 { + acceptedInputNumber = true + } + + if !acceptedInputNumber { + return MultiRangeProof{}, errors.New("Value number is not supported - just 1, 2, 4, 8") + } + + EC = genECPrimeGroupKey(m * bitsPerValue) + + // we concatenate the binary representation of the values + + PowerOfTwos := PowerVector(bitsPerValue, big.NewInt(2)) + + Comms := make([]ECPoint, m) + gammas := make([]*big.Int, m) + aLConcat := make([]*big.Int, EC.V) + aRConcat := make([]*big.Int, EC.V) + + for j := range values { + v := values[j] + if v.Cmp(big.NewInt(0)) == -1 { + return MultiRangeProof{}, errors.New("Value is below range! Not proving") + } + + if v.Cmp(MAX_64_BITS) == 1 { + return MultiRangeProof{}, errors.New("Value is above range! Not proving") + } + + if v.Cmp(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bitsPerValue)), EC.N)) == 1 { + return MultiRangeProof{}, errors.New("Value is above range! Not proving") + } + + gamma, err := rand.Int(rand.Reader, EC.N) + + check(err) + Comms[j] = pedersenCommitment(gamma, v) + gammas[j] = gamma + + // break up v into its bitwise representation + aL := reverse(StrToBigIntArray(PadLeft(fmt.Sprintf("%b", v), "0", bitsPerValue))) + aR := VectorAddScalar(aL, big.NewInt(-1)) + + for i := range aR { + aLConcat[bitsPerValue*j+i] = aL[i] + aRConcat[bitsPerValue*j+i] = aR[i] + } + } + + //compare aL, aR + MRPResult.Comms = Comms + + alpha, err := rand.Int(rand.Reader, EC.N) + check(err) + + A := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, aLConcat, aRConcat).Add(EC.H.Mult(alpha)) + MRPResult.A = A + + // fmt.Println("Ec.V %+v", EC.V) + sL := RandVector(EC.V) + sR := RandVector(EC.V) + + rho, err := rand.Int(rand.Reader, EC.N) + check(err) + + S := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, sL, sR).Add(EC.H.Mult(rho)) + MRPResult.S = S + + // input := append(PadTo32Bytes(A.X.Bytes()), PadTo32Bytes(A.Y.Bytes())...) + // chal1s256 := crypto.Keccak256(input) + chal1s256 := HashPointsToBytes(append(Comms, A)) + + // chal1s256 := sha256.Sum256([]byte(A.X.String() + A.Y.String())) + cy := new(big.Int).SetBytes(chal1s256[:]) + MRPResult.Cy = cy + + // input = append(PadTo32Bytes(S.X.Bytes()), PadTo32Bytes(S.Y.Bytes())...) + // chal2s256 := crypto.Keccak256(input) + chal2s256 := HashPointsToBytes(append(Comms, A, S)) + + // chal2s256 := sha256.Sum256([]byte(S.X.String() + S.Y.String())) + cz := new(big.Int).SetBytes(chal2s256[:]) + MRPResult.Cz = cz + + zPowersTimesTwoVec := make([]*big.Int, EC.V) + for j := 0; j < m; j++ { + zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) + for i := 0; i < bitsPerValue; i++ { + zPowersTimesTwoVec[j*bitsPerValue+i] = new(big.Int).Mod(new(big.Int).Mul(PowerOfTwos[i], zp), EC.N) + } + } + + PowerOfCY := PowerVector(EC.V, cy) + // fmt.Println(PowerOfCY) + l0 := VectorAddScalar(aLConcat, new(big.Int).Neg(cz)) + l1 := sL + r0 := VectorAdd( + VectorHadamard( + PowerOfCY, + VectorAddScalar(aRConcat, cz)), + zPowersTimesTwoVec) + r1 := VectorHadamard(sR, PowerOfCY) + + //calculate t0 + vz2 := big.NewInt(0) + z2 := new(big.Int).Mod(new(big.Int).Mul(cz, cz), EC.N) + PowerOfCZ := PowerVector(m, cz) + for j := 0; j < m; j++ { + vz2 = new(big.Int).Add(vz2, + new(big.Int).Mul( + PowerOfCZ[j], + new(big.Int).Mul(values[j], z2))) + vz2 = new(big.Int).Mod(vz2, EC.N) + } + + t0 := new(big.Int).Mod(new(big.Int).Add(vz2, DeltaMRP(PowerOfCY, cz, m)), EC.N) + + t1 := new(big.Int).Mod(new(big.Int).Add(InnerProduct(l1, r0), InnerProduct(l0, r1)), EC.N) + t2 := InnerProduct(l1, r1) + + // given the t_i values, we can generate commitments to them + tau1, err := rand.Int(rand.Reader, EC.N) + check(err) + tau2, err := rand.Int(rand.Reader, EC.N) + check(err) + + T1 := pedersenCommitment(tau1, t1) // EC.G.Mult(t1).Add(EC.H.Mult(tau1)) //commitment to t1 + T2 := pedersenCommitment(tau2, t2) //EC.G.Mult(t2).Add(EC.H.Mult(tau2)) //commitment to t2 + + MRPResult.T1 = T1 + MRPResult.T2 = T2 + + // t1Byte := append(PadTo32Bytes(T1.X.Bytes()), PadTo32Bytes(T1.Y.Bytes())...) + // t2Byte := append(PadTo32Bytes(T2.X.Bytes()), PadTo32Bytes(T2.Y.Bytes())...) + // input = append(t1Byte, t2Byte...) + // chal3s256 := crypto.Keccak256(input) + chal3s256 := HashPointsToBytes(append(Comms, A, S, T1, T2)) + + // chal3s256 := sha256.Sum256([]byte(T1.X.String() + T1.Y.String() + T2.X.String() + T2.Y.String())) + cx := new(big.Int).SetBytes(chal3s256[:]) + + MRPResult.Cx = cx + + left := CalculateLMRP(aLConcat, sL, cz, cx) + right := CalculateRMRP(aRConcat, sR, PowerOfCY, zPowersTimesTwoVec, cz, cx) + + thatPrime := new(big.Int).Mod( // t0 + t1*x + t2*x^2 + new(big.Int).Add(t0, new(big.Int).Add(new(big.Int).Mul(t1, cx), new(big.Int).Mul(new(big.Int).Mul(cx, cx), t2))), EC.N) + + that := InnerProduct(left, right) // NOTE: BP Java implementation calculates this from the t_i + + // thatPrime and that should be equal + if thatPrime.Cmp(that) != 0 { + fmt.Println("Proving -- Uh oh! Two diff ways to compute same value not working") + fmt.Printf("\tthatPrime = %s\n", thatPrime.String()) + fmt.Printf("\tthat = %s \n", that.String()) + } + + MRPResult.Th = that + + vecRandomnessTotal := big.NewInt(0) + for j := 0; j < m; j++ { + zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) + tmp1 := new(big.Int).Mul(gammas[j], zp) + vecRandomnessTotal = new(big.Int).Mod(new(big.Int).Add(vecRandomnessTotal, tmp1), EC.N) + } + //fmt.Println(vecRandomnessTotal) + taux1 := new(big.Int).Mod(new(big.Int).Mul(tau2, new(big.Int).Mul(cx, cx)), EC.N) + taux2 := new(big.Int).Mod(new(big.Int).Mul(tau1, cx), EC.N) + taux := new(big.Int).Mod(new(big.Int).Add(taux1, new(big.Int).Add(taux2, vecRandomnessTotal)), EC.N) + + MRPResult.Tau = taux + + mu := new(big.Int).Mod(new(big.Int).Add(alpha, new(big.Int).Mul(rho, cx)), EC.N) + MRPResult.Mu = mu + + HPrime := make([]ECPoint, len(EC.BPH)) + + for i := range HPrime { + HPrime[i] = EC.BPH[i].Mult(new(big.Int).ModInverse(PowerOfCY[i], EC.N)) + } + + P := TwoVectorPCommitWithGens(EC.BPG, HPrime, left, right) + //fmt.Println(P) + + MRPResult.IPP = InnerProductProve(left, right, that, P, EC.U, EC.BPG, HPrime) + + return MRPResult, nil +} + +/* +MultiRangeProof Verify +Takes in a MultiRangeProof and verifies its correctness +*/ +func MRPVerify(mrp *MultiRangeProof) bool { + m := len(mrp.Comms) + EC = genECPrimeGroupKey(m * bitsPerValue) + + //changes: + // check 1 changes since it includes all commitments + // check 2 commitment generation is also different + + // verify the challenges + // input := append(PadTo32Bytes(mrp.A.X.Bytes()), PadTo32Bytes(mrp.A.Y.Bytes())...) + // chal1s256 := crypto.Keccak256(input) + chal1s256 := HashPointsToBytes(append(mrp.Comms, mrp.A)) + + cy := new(big.Int).SetBytes(chal1s256[:]) + + if cy.Cmp(mrp.Cy) != 0 { + log.Debug("MRPVerify challenge failed!", "Cy", common.Bytes2Hex(mrp.Cy.Bytes())) + return false + } + + // input = append(PadTo32Bytes(mrp.S.X.Bytes()), PadTo32Bytes(mrp.S.Y.Bytes())...) + // chal2s256 := crypto.Keccak256(input) + chal2s256 := HashPointsToBytes(append(mrp.Comms, mrp.A, mrp.S)) + + // chal2s256 := sha256.Sum256([]byte(mrp.S.X.String() + mrp.S.Y.String())) + cz := new(big.Int).SetBytes(chal2s256[:]) + if cz.Cmp(mrp.Cz) != 0 { + log.Debug("MRPVerify challenge failed!", "Cz", common.Bytes2Hex(mrp.Cz.Bytes())) + return false + } + + // t1Byte := append(PadTo32Bytes(mrp.T1.X.Bytes()), PadTo32Bytes(mrp.T1.Y.Bytes())...) + // t2Byte := append(PadTo32Bytes(mrp.T2.X.Bytes()), PadTo32Bytes(mrp.T2.Y.Bytes())...) + // input = append(t1Byte, t2Byte...) + // chal3s256 := crypto.Keccak256(input) + chal3s256 := HashPointsToBytes(append(mrp.Comms, mrp.A, mrp.S, mrp.T1, mrp.T2)) + + // chal3s256 := sha256.Sum256([]byte(T1.X.String() + T1.Y.String() + T2.X.String() + T2.Y.String())) + // cx := new(big.Int).SetBytes(chal3s256[:]) + //chal3s256 := sha256.Sum256([]byte(mrp.T1.X.String() + mrp.T1.Y.String() + mrp.T2.X.String() + mrp.T2.Y.String())) + + cx := new(big.Int).SetBytes(chal3s256[:]) + + if cx.Cmp(mrp.Cx) != 0 { + log.Debug("MRPVerify challenge failed!", "Cx", common.Bytes2Hex(mrp.Cx.Bytes())) + return false + } + + // given challenges are correct, very range proof + PowersOfY := PowerVector(EC.V, cy) + + // t_hat * G + tau * H + lhs := pedersenCommitment(mrp.Tau, mrp.Th) //EC.G.Mult(mrp.Th).Add(EC.H.Mult(mrp.Tau)) + + // z^2 * \bold{z}^m \bold{V} + delta(y,z) * G + x * T1 + x^2 * T2 + CommPowers := EC.Zero() + PowersOfZ := PowerVector(m, cz) + z2 := new(big.Int).Mod(new(big.Int).Mul(cz, cz), EC.N) + + for j := 0; j < m; j++ { + CommPowers = CommPowers.Add(mrp.Comms[j].Mult(new(big.Int).Mul(z2, PowersOfZ[j]))) + } + + // TODO i need to change how to calculate the commitment here also ? + // need to compare and double check with privacy-client lib + rhs := EC.G.Mult(DeltaMRP(PowersOfY, cz, m)).Add( + mrp.T1.Mult(cx)).Add( + mrp.T2.Mult(new(big.Int).Mul(cx, cx))).Add(CommPowers) + + if !lhs.Equal(rhs) { + log.Debug("Rangeproof failed") + return false + } + + tmp1 := EC.Zero() + zneg := new(big.Int).Mod(new(big.Int).Neg(cz), EC.N) + for i := range EC.BPG { + tmp1 = tmp1.Add(EC.BPG[i].Mult(zneg)) + } + + PowerOfTwos := PowerVector(bitsPerValue, big.NewInt(2)) + tmp2 := EC.Zero() + // generate h' + HPrime := make([]ECPoint, len(EC.BPH)) + + for i := range HPrime { + mi := new(big.Int).ModInverse(PowersOfY[i], EC.N) + HPrime[i] = EC.BPH[i].Mult(mi) + } + + for j := 0; j < m; j++ { + for i := 0; i < bitsPerValue; i++ { + val1 := new(big.Int).Mul(cz, PowersOfY[j*bitsPerValue+i]) + zp := new(big.Int).Exp(cz, big.NewInt(2+int64(j)), EC.N) + val2 := new(big.Int).Mod(new(big.Int).Mul(zp, PowerOfTwos[i]), EC.N) + tmp2 = tmp2.Add(HPrime[j*bitsPerValue+i].Mult(new(big.Int).Add(val1, val2))) + } + } + + // without subtracting this value should equal muCH + l[i]G[i] + r[i]H'[i] + // we want to make sure that the innerproduct checks out, so we subtract it + P := mrp.A.Add(mrp.S.Mult(cx)).Add(tmp1).Add(tmp2).Add(EC.H.Mult(mrp.Mu).Neg()) + //fmt.Println(P) + + if !InnerProductVerifyFast(mrp.Th, P, EC.U, EC.BPG, HPrime, mrp.IPP) { + log.Debug("Range proof failed!") + return false + } + + return true +} + +// NewECPrimeGroupKey returns the curve (field), +// Generator 1 x&y, Generator 2 x&y, order of the generators +func NewECPrimeGroupKey(n int) CryptoParams { + curValue := btcec.S256().Gx + s256 := sha256.New() + gen1Vals := make([]ECPoint, n) + gen2Vals := make([]ECPoint, n) + u := ECPoint{big.NewInt(0), big.NewInt(0)} + cg := ECPoint{} + ch := ECPoint{} + + j := 0 + confirmed := 0 + for confirmed < (2*n + 3) { + s256.Write(new(big.Int).Add(curValue, big.NewInt(int64(j))).Bytes()) + + potentialXValue := make([]byte, 33) + binary.LittleEndian.PutUint32(potentialXValue, 2) + for i, elem := range s256.Sum(nil) { + potentialXValue[i+1] = elem + } + + gen2, err := btcec.ParsePubKey(potentialXValue, btcec.S256()) + if err == nil { + if confirmed == 2*n { // once we've generated all g and h values then assign this to u + u = ECPoint{gen2.X, gen2.Y} + //fmt.Println("Got that U value") + } else if confirmed == 2*n+1 { + cg = ECPoint{gen2.X, gen2.Y} + + } else if confirmed == 2*n+2 { + ch = ECPoint{gen2.X, gen2.Y} + } else { + if confirmed%2 == 0 { + gen1Vals[confirmed/2] = ECPoint{gen2.X, gen2.Y} + //fmt.Println("new G Value") + } else { + gen2Vals[confirmed/2] = ECPoint{gen2.X, gen2.Y} + //fmt.Println("new H value") + } + } + confirmed += 1 + } + j += 1 + } + + return CryptoParams{ + btcec.S256(), + btcec.S256(), + gen1Vals, + gen2Vals, + btcec.S256().N, + u, + n, + cg, + ch} +} + +func genECPrimeGroupKey(n int) CryptoParams { + // curValue := btcec.S256().Gx + // s256 := sha256.New() + gen1Vals := make([]ECPoint, n) + gen2Vals := make([]ECPoint, n) + // u := ECPoint{big.NewInt(0), big.NewInt(0)} + hx, _ := new(big.Int).SetString("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0", 16) + hy, _ := new(big.Int).SetString("31d3c6863973926e049e637cb1b5f40a36dac28af1766968c30c2313f3a38904", 16) + ch := ECPoint{hx, hy} + + gx, _ := new(big.Int).SetString("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16) + gy, _ := new(big.Int).SetString("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16) + cg := ECPoint{gx, gy} + + i := 0 + for i < n { + gen2Vals[i] = ch.Mult( + big.NewInt(int64(i*2 + 1)), + ) + gen1Vals[i] = cg.Mult( + big.NewInt(int64(i*2 + 2)), + ) + i++ + } + + u := cg.Mult( + big.NewInt(int64(n + 3)), + ) + + return CryptoParams{ + btcec.S256(), + btcec.S256(), + gen1Vals, + gen2Vals, + btcec.S256().N, + u, + n, + ch, + cg} +} + +func init() { + // just need the base parameter N, P, G, H for this init, ignore everything else + EC = CryptoParams{ + btcec.S256(), + btcec.S256(), + make([]ECPoint, VecLength), + make([]ECPoint, VecLength), + btcec.S256().N, + ECPoint{big.NewInt(0), big.NewInt(0)}, + VecLength, + ECPoint{big.NewInt(0), big.NewInt(0)}, + ECPoint{big.NewInt(0), big.NewInt(0)}} +} diff --git a/core/vm/privacy/bulletproof.json b/core/vm/privacy/bulletproof.json new file mode 100644 index 000000000000..263dd7927099 --- /dev/null +++ b/core/vm/privacy/bulletproof.json @@ -0,0 +1 @@ +{"Ipp":{"L":[{"x":"7be22788517d8c7e149727c5397cf7b2d6713546dee4f974f3239a1f53e64f44","y":"b18c08695e40b801ace64f00bf7010245dcf972cad7ce1e97e441fa68575bb72"},{"x":"29956e85820c1ede4f6ec987b3c20cbf7c0fc0aa6a8cd823e73105fc681c1880","y":"228e434578a40d247a2b5f1c2a7a5154716fb06e98d8087b3dce9cc85429b72c"},{"x":"b39703b5680857d7629a39374451baae59459418c23f44f522838efd952e69db","y":"60033aa9807a0c081c530c30292eca71f61e17433513ead9caadce7997d97981"},{"x":"074cd428559b2bf9c0a6d54479254bdeedc26e655b7f0d822aec9a6d1f1068b3","y":"915958a8e24fefe8ec5e6338661567c8e6178d03ac35422284701bf2ab37d592"},{"x":"80d27e070c49f6d41d49254295a9e70ebaf82d8780c93f1c48f018609e3822dd","y":"5c5e365b72835ff1e24ff3a130e1cc332e36d891ba383752e469a667ecf5db01"},{"x":"6e9c63c3188c29d593417b5cb96ab0f149d905a194ace7a3e523d15f8f8922cd","y":"a91566f2cae911542a0658f63451b4a87cea5c668ab4220c974a640442021da6"}],"R":[{"x":"34f512b1fe0989ce77f8cb5b3a95dd31b55c38d4b6278e4923d6cf224d59c421","y":"8757d403eb39a181418db28f742f0ff3f59991929800c424ebce792f27353335"},{"x":"900bc03a4e26a61bb11de434ee150970a22dce77320b26fea1b172dbdfef5b10","y":"de9d1b833decc36f84ae4ce1be87414674ebef7b58009563f35a722347eb76d7"},{"x":"0ca13ff9d0a929b417a165ceb42c532dd54c34e8f1e9083208630dadafdb5ee4","y":"0bba4e3ac3b20cde8074d4683742f924bdb070a590c1d9d7c8f239748359015c"},{"x":"c29728cbb8393b65f8f224e587eccee9d332d2936b36bb460a9f9df720ef5add","y":"88295fbbe05f25534a43ac528a890b9a26ab3bf95a63e4803168d56b430190ce"},{"x":"4cd804793a0169e0bfe1206907cec530a733efb7cca86f08bff8d04bfb82394b","y":"453594ec2090934e75348db0db070c79b6302cf289d1907adbc198493fee417e"},{"x":"9d9a42ba4b30cc68841ce6528f0d4ffa7990b35fc533f7cb0e4bf0d26cc9c6b6","y":"d6dbdb179262c2bc1fc734d4e20a2c54c5e0b8b8d0c48ee630e146f4cef1f486"}],"A":"-2d23144bda297742e1eafb40911ccdbf2f5e950e8b73b4eaf8f78b857d5c7a93","B":"162325c6d1160e0c033135e32b57351c17ef79755cf4f19c6d6c0ba8117e8d09","Challenges":["139fb4a37d387933e43987862d67297c65af5302c6995bfe7181646dcfc169f1","d43aa9b8fdd97b12e3c4bfabf4c36c59dabb08ace9d5da221f676cc7244000e0","3013fdc8605a4dd479898c2b9abbab45ed9b9676267d8c86a0b7b737448c3d8e","b7a16a537783cd7a9e5bddad4536754a6b16a152fedd2df46a36f76a51cd66d9","5408a38e71aa569b6ddc2ccc84a3b2fa29253905072bf8ed5035406cda89e6fa","1ab61f234a5982567e51af82efd118ebcb44befabfaa5f07183471508e3f4208","3e666d903ccd1312a9a83506b298dfac2c2f11f796087d91104487a577802faa"]},"Comms":["71a29edddcf580c0ab4c992f34df4bcd7f23079bf29c07267e354c0e1b0c7b7d0d7d7f55259df2570dc37a2b6719eb58b2195f06163d29b7aca710cbf9b66560"],"A":"e7d28764b37d275ee1b0cc589ab2aa46cc60a406d6cb0def47dcb5fc49b3279dc65b2eb160328db9dc5953a263a5eba0855b51c66a5e23ac0a3b997d060643ed","S":"78aa79b16298d8c7cc587c8fe521d13b9c768256a8842e74e82970507efea3b9735e4324787071ded1e049c888aee40fe85cff0fff4ffdc4d2d3dc2a17ac306a","cy":"3e38e29bf805dc855b687f969a1a854a0c23fddb8ff1f480079929518e8df1f6","cz":"d794749e1c1442762943a40422a6f9bd284f938fce652c382fea05a0e33ca68f","T1":"45e56a5bc134b7b19d53670d57b45874a01442b58a853dd0960f4a7ec1a093f59be5d93e35a55f2006f03cf9c31801942d9a8739a9408c67d93c4dff6c9f7346","T2":"ad380e6564ae44cd6f91656e66c031cfe21aa47cc511ae51e4a2a8604bd181cd181be8eb1b8e0de71458eed4ce538667a392cc0e7c4e85ab7f4cff54161c8087","cx":"75327f647ab5124eefa92e51f769cdb64a24fe5f88a059b002f2d590c9236894","Th":"-ee380a27a0b95643f9a3a5a975c9b35638ba2a7105a36c7ffd90acdd16029323","Tau":"38059dcf3dfc1da9646f3dc66c8b1487c3375577e751e8341b2addd45ef06306","Mu":"e648b63a1857d1e9723b01ac3151c29fd723845f8aa2a7d6ae867feba8db53e5"} \ No newline at end of file diff --git a/core/vm/privacy/bulletproof_test.go b/core/vm/privacy/bulletproof_test.go new file mode 100644 index 000000000000..8088ac98e9d3 --- /dev/null +++ b/core/vm/privacy/bulletproof_test.go @@ -0,0 +1,527 @@ +package privacy + +import ( + "crypto/rand" + "encoding/json" + "fmt" + "io" + "math/big" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestInnerProductProveLen1(t *testing.T) { + fmt.Println("TestInnerProductProve1") + EC = genECPrimeGroupKey(1) + a := make([]*big.Int, 1) + b := make([]*big.Int, 1) + + a[0] = big.NewInt(1) + + b[0] = big.NewInt(1) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductProveLen2(t *testing.T) { + fmt.Println("TestInnerProductProve2") + EC = genECPrimeGroupKey(2) + a := make([]*big.Int, 2) + b := make([]*big.Int, 2) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + + b[0] = big.NewInt(1) + b[1] = big.NewInt(1) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + fmt.Println("P after two vector commitment with gen ", P) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductProveLen4(t *testing.T) { + fmt.Println("TestInnerProductProve4") + EC = genECPrimeGroupKey(4) + a := make([]*big.Int, 4) + b := make([]*big.Int, 4) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + a[2] = big.NewInt(1) + a[3] = big.NewInt(1) + + b[0] = big.NewInt(1) + b[1] = big.NewInt(1) + b[2] = big.NewInt(1) + b[3] = big.NewInt(1) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductProveLen8(t *testing.T) { + fmt.Println("TestInnerProductProve8") + EC = genECPrimeGroupKey(8) + a := make([]*big.Int, 8) + b := make([]*big.Int, 8) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + a[2] = big.NewInt(1) + a[3] = big.NewInt(1) + a[4] = big.NewInt(1) + a[5] = big.NewInt(1) + a[6] = big.NewInt(1) + a[7] = big.NewInt(1) + + b[0] = big.NewInt(2) + b[1] = big.NewInt(2) + b[2] = big.NewInt(2) + b[3] = big.NewInt(2) + b[4] = big.NewInt(2) + b[5] = big.NewInt(2) + b[6] = big.NewInt(2) + b[7] = big.NewInt(2) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductProveLen64Rand(t *testing.T) { + fmt.Println("TestInnerProductProveLen64Rand") + EC = genECPrimeGroupKey(64) + a := RandVector(64) + b := RandVector(64) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerify(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + fmt.Printf("Values Used: \n\ta = %s\n\tb = %s\n", a, b) + } + +} + +func TestInnerProductVerifyFastLen1(t *testing.T) { + fmt.Println("TestInnerProductProve1") + EC = genECPrimeGroupKey(1) + a := make([]*big.Int, 1) + b := make([]*big.Int, 1) + + a[0] = big.NewInt(2) + + b[0] = big.NewInt(2) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductVerifyFastLen2(t *testing.T) { + fmt.Println("TestInnerProductProve2") + EC = genECPrimeGroupKey(2) + a := make([]*big.Int, 2) + b := make([]*big.Int, 2) + + a[0] = big.NewInt(2) + a[1] = big.NewInt(3) + + b[0] = big.NewInt(2) + b[1] = big.NewInt(3) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductVerifyFastLen4(t *testing.T) { + fmt.Println("TestInnerProductProve4") + EC = genECPrimeGroupKey(4) + a := make([]*big.Int, 4) + b := make([]*big.Int, 4) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + a[2] = big.NewInt(1) + a[3] = big.NewInt(1) + + b[0] = big.NewInt(1) + b[1] = big.NewInt(1) + b[2] = big.NewInt(1) + b[3] = big.NewInt(1) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductVerifyFastLen8(t *testing.T) { + fmt.Println("TestInnerProductProve8") + EC = genECPrimeGroupKey(8) + a := make([]*big.Int, 8) + b := make([]*big.Int, 8) + + a[0] = big.NewInt(1) + a[1] = big.NewInt(1) + a[2] = big.NewInt(1) + a[3] = big.NewInt(1) + a[4] = big.NewInt(1) + a[5] = big.NewInt(1) + a[6] = big.NewInt(1) + a[7] = big.NewInt(1) + + b[0] = big.NewInt(2) + b[1] = big.NewInt(2) + b[2] = big.NewInt(2) + b[3] = big.NewInt(2) + b[4] = big.NewInt(2) + b[5] = big.NewInt(2) + b[6] = big.NewInt(2) + b[7] = big.NewInt(2) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + } +} + +func TestInnerProductVerifyFastLen64Rand(t *testing.T) { + fmt.Println("TestInnerProductProveLen64Rand") + EC = genECPrimeGroupKey(64) + a := RandVector(64) + b := RandVector(64) + + c := InnerProduct(a, b) + + P := TwoVectorPCommitWithGens(EC.BPG, EC.BPH, a, b) + + ipp := InnerProductProve(a, b, c, P, EC.U, EC.BPG, EC.BPH) + + if InnerProductVerifyFast(c, P, EC.U, EC.BPG, EC.BPH, ipp) { + fmt.Println("Inner Product Proof correct") + } else { + t.Error("Inner Product Proof incorrect") + fmt.Printf("Values Used: \n\ta = %s\n\tb = %s\n", a, b) + } + +} + +func TestMRPProveZERO(t *testing.T) { + + mRangeProof, _ := MRPProve([]*big.Int{ + new(big.Int).SetInt64(0), + }) + mv := MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") +} + +func TestMRPProve_MAX_2_POW_64(t *testing.T) { + + mRangeProof, _ := MRPProve([]*big.Int{ + new(big.Int).SetUint64(0xFFFFFFFFFFFFFFFF), + }) + mv := MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") +} + +func TestMRPProveOutOfSupportedRange(t *testing.T) { + + value, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFF", 16) + _, err := MRPProve([]*big.Int{ + value, + }) + assert.NotNil(t, err, " MRProof incorrect") +} + +func TestMRPProve_RANDOM(t *testing.T) { + + mRangeProof, _ := MRPProve(Rand64Vector(1)) + mv := MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") + + mRangeProof, _ = MRPProve(Rand64Vector(2)) + mv = MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") + + mRangeProof, _ = MRPProve(Rand64Vector(4)) + mv = MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") + + mRangeProof, _ = MRPProve(Rand64Vector(8)) + mv = MRPVerify(&mRangeProof) + assert.Equal(t, mv, true, " MRProof incorrect") +} + +func Rand64Vector(l int) []*big.Int { + result := make([]*big.Int, l) + + for i := 0; i < l; i++ { + x, err := rand.Int(rand.Reader, big.NewInt(0xFFFFFFFFFFFFFFF)) + check(err) + result[i] = x + } + + return result +} + +func TestMRPProveValueNumberNotSupported(t *testing.T) { + + _, err := MRPProve(Rand64Vector(3)) + assert.NotNil(t, err, "MRProof incorrect - accepted 3 inputs") + + _, err = MRPProve(Rand64Vector(5)) + assert.NotNil(t, err, "MRProof incorrect - accepted 5 inputs") + + _, err = MRPProve(Rand64Vector(6)) + assert.NotNil(t, err, "MRProof incorrect - accepted 6 inputs") + + _, err = MRPProve(Rand64Vector(7)) + assert.NotNil(t, err, "MRProof incorrect - accepted 7 inputs") + + _, err = MRPProve(Rand64Vector(10)) + assert.NotNil(t, err, "MRProof incorrect - accepted 10 inputs") + + _, err = MRPProve(Rand64Vector(1)) + assert.Nil(t, err, "MRProof incorrect - not accepted 1 inputs") + + _, err = MRPProve(Rand64Vector(2)) + assert.Nil(t, err, "MRProof incorrect - not accepted 2 inputs") + + _, err = MRPProve(Rand64Vector(4)) + fmt.Println(err) + assert.Nil(t, err, "MRProof incorrect - not accepted 4 inputs") + + _, err = MRPProve(Rand64Vector(8)) + assert.Nil(t, err, "MRProof incorrect - not accepted 8 inputs") +} + +type Point struct { + x string + y string +} + +type IPP struct { + L []map[string]string `json:"L"` + R []map[string]string `json:"R"` + A string `json:"A"` + B string `json:"B"` + Challenges []string `json:"Challenges"` +} + +type BulletProof struct { + Comms []string `json:"Comms"` + A string `json:"A"` + S string `json:"S"` + Cx string `json:"Cx"` + Cy string `json:"Cy"` + Cz string `json:"Cz"` + T1 string `json:"T1"` + T2 string `json:"T2"` + Th string `json:"Th"` + Tau string `json:"Tau"` + Mu string `json:"Mu"` + Ipp IPP `json:"Ipp"` +} + +func parseTestData(filePath string) MultiRangeProof { + jsonFile, err := os.Open(filePath) + + if err != nil { + fmt.Println(err) + } + + defer jsonFile.Close() + + byteValue, _ := io.ReadAll(jsonFile) + + // we initialize our Users array + // var result map[string]interface{} + result := BulletProof{} + + json.Unmarshal([]byte(byteValue), &result) + + fmt.Println("result ", result.Tau) + fmt.Println("result ", result.Th) + fmt.Println("result.Ipp ", result.Ipp) + + ipp := result.Ipp + + proof := MultiRangeProof{ + Comms: MapECPointFromHex(result.Comms, ECPointFromHex), + A: ECPointFromHex(result.A), + S: ECPointFromHex(result.S), + T1: ECPointFromHex(result.T1), + T2: ECPointFromHex(result.T2), + Th: bigIFromHex(result.Th), + Tau: bigIFromHex(result.Tau), + Mu: bigIFromHex(result.Mu), + Cx: bigIFromHex(result.Cx), + Cy: bigIFromHex(result.Cy), + Cz: bigIFromHex(result.Cz), + IPP: InnerProdArg{ + L: MapECPoint(ipp.L, ECPointFromPoint), + R: MapECPoint(ipp.R, ECPointFromPoint), + A: bigIFromHex(ipp.A), + B: bigIFromHex(ipp.B), + Challenges: MapBigI(ipp.Challenges, bigIFromHex), + }, + } + + fmt.Println(proof) + return proof +} + +/* +* +Utils for parsing data from json +*/ +func MapBigI(list []string, f func(string) *big.Int) []*big.Int { + result := make([]*big.Int, len(list)) + + for i, item := range list { + result[i] = f(item) + } + return result +} + +func MapECPointFromHex(list []string, f func(string) ECPoint) []ECPoint { + result := make([]ECPoint, len(list)) + + for i, item := range list { + result[i] = f(item) + } + return result +} + +func MapECPoint(list []map[string]string, f func(Point) ECPoint) []ECPoint { + result := make([]ECPoint, len(list)) + + for i, item := range list { + result[i] = f(Point{ + x: item["x"], + y: item["y"], + }) + } + return result +} + +func bigIFromHex(hex string) *big.Int { + tmp, _ := new(big.Int).SetString(hex, 16) + return tmp +} + +func ECPointFromHex(hex string) ECPoint { + Px, _ := new(big.Int).SetString(hex[:64], 16) + Py, _ := new(big.Int).SetString(hex[64:], 16) + P := ECPoint{Px, Py} + return P +} + +func ECPointFromPoint(ecpoint Point) ECPoint { + Px, _ := new(big.Int).SetString(ecpoint.x, 16) + Py, _ := new(big.Int).SetString(ecpoint.y, 16) + P := ECPoint{Px, Py} + return P +} + +func TestMRPGeneration(t *testing.T) { + values := make([]*big.Int, 2) + values[0] = big.NewInt(1000) + values[1] = big.NewInt(100000) + mrp, err := MRPProve(values) + if err != nil { + t.Error("failed to generate bulletproof") + } + + v := MRPVerify(&mrp) + serilizedBp := mrp.Serialize() + + newMRP := new(MultiRangeProof) + if newMRP.Deserialize(serilizedBp) != nil { + t.Error("failed to deserialized bulletproof") + } + + v = v && MRPVerify(newMRP) + + if !v { + t.Error("failed to verify bulletproof") + } +} diff --git a/core/vm/privacy/ringct.go b/core/vm/privacy/ringct.go new file mode 100644 index 000000000000..93c97c56cee1 --- /dev/null +++ b/core/vm/privacy/ringct.go @@ -0,0 +1,632 @@ +package privacy + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "math/big" + + "github.com/XinFinOrg/XDPoSChain/common" + + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" + + "github.com/XinFinOrg/XDPoSChain/log" +) + +// These constants define the lengths of serialized public keys. +const ( + PubKeyBytesLenCompressed = 33 + PubKeyBytesLenUncompressed = 65 + PubKeyBytesLenHybrid = 65 +) + +const ( + pubkeyCompressed byte = 0x2 // y_bit + x coord + pubkeyUncompressed byte = 0x4 // x coord + y coord + pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord +) + +// The proof contains pretty much stuffs +// The proof contains pretty much stuffs +// Ring size rs: 1 byte => proof[0] +// num input: number of real inputs: 1 byte => proof[1] +// List of inputs/UTXO index typed uint64 => total size = rs * numInput * 8 = proof[0]*proof[1]*8 +// List of key images: total size = numInput * 33 = proof[1] * 33 +// number of output n: 1 byte +// List of output => n * 130 bytes +// transaction fee: uint256 => 32 byte +// ringCT proof size ctSize: uint16 => 2 byte +// ringCT proof: ctSize bytes +// bulletproofs: bp +type PrivateSendVerifier struct { + proof []byte + //ringCT RingCT +} + +type Ring []*ecdsa.PublicKey + +type RingSignature struct { + NumRing int + Size int // size of ring + M [32]byte // message + C *big.Int // ring signature value, 1 element + S [][]*big.Int // ring signature values: [NumRing][Size] + Ring []Ring // array of rings of pubkeys: [NumRing] + I []*ecdsa.PublicKey // key images, size = the number of rings [NumRing] + Curve elliptic.Curve + SerializedRing []byte //temporary memory stored the raw ring ct used in case of verifying ringCT with message verification +} + +func (p *PrivateSendVerifier) verify() bool { + return false +} + +func (p *PrivateSendVerifier) deserialize() { + +} + +// helper function, returns type of v +func typeof(v interface{}) string { + return fmt.Sprintf("%T", v) +} + +func isOdd(a *big.Int) bool { + return a.Bit(0) == 1 +} + +// SerializeCompressed serializes a public key in a 33-byte compressed format. +func SerializeCompressed(p *ecdsa.PublicKey) []byte { + b := make([]byte, 0, PubKeyBytesLenCompressed) + format := pubkeyCompressed + if isOdd(p.Y) { + format |= 0x1 + } + b = append(b, format) + return append(b, PadTo32Bytes(p.X.Bytes())...) +} + +func DeserializeCompressed(curve elliptic.Curve, b []byte) *ecdsa.PublicKey { + x := new(big.Int).SetBytes(b[1:33]) + // Y = +-sqrt(x^3 + B) + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + x3.Add(x3, curve.Params().B) + + // now calculate sqrt mod p of x2 + B + // This code used to do a full sqrt based on tonelli/shanks, + // but this was replaced by the algorithms referenced in + // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 + PPlus1Div4 := new(big.Int).Add(curve.Params().P, big.NewInt(1)) + PPlus1Div4 = PPlus1Div4.Div(PPlus1Div4, big.NewInt(4)) + y := new(big.Int).Exp(x3, PPlus1Div4, curve.Params().P) + ybit := b[0]%2 == 1 + if ybit != isOdd(y) { + y.Sub(curve.Params().P, y) + } + if ybit != isOdd(y) { + return nil + } + return &ecdsa.PublicKey{curve, x, y} +} + +// bytes returns the public key ring as a byte slice. +func (r Ring) Bytes() (b []byte) { + for _, pub := range r { + b = append(b, PadTo32Bytes(pub.X.Bytes())...) + b = append(b, PadTo32Bytes(pub.Y.Bytes())...) + } + return +} + +func PadTo32Bytes(in []byte) []byte { + padded := make([]byte, 32) + if len(in) >= 32 { + copy(padded, in) + } else { + copy(padded[32-len(in):], in) + } + + return padded +} + +// converts the signature to a byte array +// this is the format that will be used when passing EVM bytecode +func (r *RingSignature) Serialize() ([]byte, error) { + sig := []byte{} + // add size and message + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(r.NumRing)) + sig = append(sig, b[:]...) // 8 bytes + + b = make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(r.Size)) + sig = append(sig, b[:]...) // 8 bytes + + sig = append(sig, PadTo32Bytes(r.M[:])...) // 32 bytes + sig = append(sig, PadTo32Bytes(r.C.Bytes())...) // 32 bytes + + for k := 0; k < r.NumRing; k++ { + // 96 bytes each iteration + for i := 0; i < r.Size; i++ { + sig = append(sig, PadTo32Bytes(r.S[k][i].Bytes())...) + } + } + for k := 0; k < r.NumRing; k++ { + // 96 bytes each iteration + for i := 0; i < r.Size; i++ { + rb := SerializeCompressed(r.Ring[k][i]) + sig = append(sig, rb...) + } + } + + for k := 0; k < r.NumRing; k++ { + // 64 bytes + rb := SerializeCompressed(r.I[k]) + sig = append(sig, rb...) + } + + if len(sig) != 8+8+32+32+(32+33)*r.NumRing*r.Size+33*r.NumRing { + return []byte{}, errors.New("Could not serialize ring signature") + } + + return sig, nil +} + +func computeSignatureSize(numRing int, ringSize int) int { + const MaxInt = int(^uint(0) >> 1) + + if numRing < 0 || ringSize < 0 { + return -1 + } + + // Calculate term and check for overflow + + term := numRing * ringSize * 65 + + if term < 0 || term < numRing || term < ringSize { + return -1 + } + + return 8 + 8 + 32 + 32 + numRing*ringSize*32 + numRing*ringSize*33 + numRing*33 +} + +// deserializes the byteified signature into a RingSignature struct +func Deserialize(r []byte) (*RingSignature, error) { + if len(r) < 16 { + return nil, errors.New("Failed to deserialize ring signature") + } + offset := 0 + sig := new(RingSignature) + numRing := r[offset : offset+8] + offset += 8 + size := r[offset : offset+8] + offset += 8 + + size_uint := binary.BigEndian.Uint64(size) + size_int := int(size_uint) + sig.Size = size_int + + size_uint = binary.BigEndian.Uint64(numRing) + size_int = int(size_uint) + sig.NumRing = size_int + + if len(r) != computeSignatureSize(sig.NumRing, sig.Size) { + return nil, fmt.Errorf("incorrect ring size, len r: %d, sig.NumRing: %d sig.Size: %d", len(r), sig.NumRing, sig.Size) + } + + m := r[offset : offset+32] + offset += 32 + + var m_byte [32]byte + copy(m_byte[:], m) + + sig.M = m_byte + sig.C = new(big.Int).SetBytes(r[offset : offset+32]) + offset += 32 + + sig.S = make([][]*big.Int, sig.NumRing) + for i := 0; i < sig.NumRing; i++ { + sig.S[i] = make([]*big.Int, sig.Size) + for j := 0; j < sig.Size; j++ { + sig.S[i][j] = new(big.Int).SetBytes(r[offset : offset+32]) + offset += 32 + } + } + + sig.Curve = crypto.S256() + + sig.Ring = make([]Ring, sig.NumRing) + for i := 0; i < sig.NumRing; i++ { + sig.Ring[i] = make([]*ecdsa.PublicKey, sig.Size) + for j := 0; j < sig.Size; j++ { + compressedKey := r[offset : offset+33] + offset += 33 + compressedPubKey := DeserializeCompressed(sig.Curve, compressedKey) + sig.Ring[i][j] = compressedPubKey + } + } + + sig.I = make([]*ecdsa.PublicKey, sig.NumRing) + for i := 0; i < sig.NumRing; i++ { + compressedKey := r[offset : offset+33] + offset += 33 + compressedPubKey := DeserializeCompressed(sig.Curve, compressedKey) + sig.I[i] = compressedPubKey + } + + sig.SerializedRing = r + + if !Verify(sig, false) { + return nil, errors.New("failed to deserialize, invalid ring signature") + } + + return sig, nil +} + +// takes public key ring and places the public key corresponding to `privkey` in index s of the ring +// returns a key ring of type []*ecdsa.PublicKey +func GenKeyRing(ring []*ecdsa.PublicKey, privkey *ecdsa.PrivateKey, s int) ([]*ecdsa.PublicKey, error) { + size := len(ring) + 1 + new_ring := make([]*ecdsa.PublicKey, size) + pubkey := privkey.Public().(*ecdsa.PublicKey) + + if s > len(ring) { + return nil, errors.New("index s out of bounds") + } + + new_ring[s] = pubkey + for i := 1; i < size; i++ { + idx := (i + s) % size + new_ring[idx] = ring[i-1] + } + + return new_ring, nil +} + +// creates a ring with size specified by `size` and places the public key corresponding to `privkey` in index s of the ring +// returns a new key ring of type []*ecdsa.PublicKey +func GenNewKeyRing(size int, privkey *ecdsa.PrivateKey, s int) ([]*ecdsa.PublicKey, error) { + ring := make([]*ecdsa.PublicKey, size) + pubkey := privkey.Public().(*ecdsa.PublicKey) + + if s >= len(ring) { + return nil, errors.New("index s out of bounds") + } + + ring[s] = pubkey + + for i := 1; i < size; i++ { + idx := (i + s) % size + priv, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + + pub := priv.Public() + ring[idx] = pub.(*ecdsa.PublicKey) + } + + return ring, nil +} + +// calculate key image I = x * H_p(P) where H_p is a hash function that returns a point +// H_p(P) = sha3(P) * G +func GenKeyImage(privkey *ecdsa.PrivateKey) *ecdsa.PublicKey { + pubkey := privkey.Public().(*ecdsa.PublicKey) + image := new(ecdsa.PublicKey) + + // calculate sha3(P) + h_x, h_y := HashPoint(pubkey) + + // calculate H_p(P) = x * sha3(P) * G + i_x, i_y := privkey.Curve.ScalarMult(h_x, h_y, privkey.D.Bytes()) + + image.X = i_x + image.Y = i_y + return image +} + +func HashPoint(p *ecdsa.PublicKey) (*big.Int, *big.Int) { + input := append(PadTo32Bytes(p.X.Bytes()), PadTo32Bytes(p.Y.Bytes())...) + log.Info("HashPoint", "input ", common.Bytes2Hex(input)) + hash := crypto.Keccak256(input) + log.Info("HashPoint", "hash ", common.Bytes2Hex(hash)) + return p.Curve.ScalarBaseMult(hash[:]) +} + +// create ring signature from list of public keys given inputs: +// msg: byte array, message to be signed +// ring: array of *ecdsa.PublicKeys to be included in the ring +// privkey: *ecdsa.PrivateKey of signer +// s: index of signer in ring +func Sign(m [32]byte, rings []Ring, privkeys []*ecdsa.PrivateKey, s int) (*RingSignature, error) { + numRing := len(rings) + if numRing < 1 { + return nil, errors.New("there is no ring to make signature") + } + // check ringsize > 1 + ringsize := len(rings[0]) + if ringsize < 2 { + return nil, errors.New("size of ring less than two") + } else if s >= ringsize || s < 0 { + return nil, errors.New("secret index out of range of ring size") + } + + // setup + //pubkey := privkey.Public().(*ecdsa.PublicKey) + pubkeys := make([]*ecdsa.PublicKey, numRing) + for i := 0; i < numRing; i++ { + pubkeys[i] = &privkeys[i].PublicKey + } + //cast to BitCurve used in go-eth since elliptic.Curve.Add() and elliptic.Curve.ScalarMult() is deprecated + curve := pubkeys[0].Curve.(*secp256k1.BitCurve) + sig := new(RingSignature) + sig.Size = ringsize + sig.NumRing = numRing + sig.M = m + sig.Ring = rings + sig.Curve = curve + + // check that key at index s is indeed the signer + for i := 0; i < numRing; i++ { + if rings[i][s] != pubkeys[i] { + return nil, errors.New("secret index in ring is not signer") + } + } + + // generate key image + images := make([]*ecdsa.PublicKey, numRing) + for i := 0; i < numRing; i++ { + images[i] = GenKeyImage(privkeys[i]) + } + sig.I = images + + // start at c[1] + // pick random scalar u (glue value), calculate c[1] = H(m, u*G) where H is a hash function and G is the base point of the curve + C := make([]*big.Int, ringsize) + S := make([][]*big.Int, numRing) + for i := 0; i < numRing; i++ { + S[i] = make([]*big.Int, ringsize) + } + + //Initialize S except S[..][s] + for i := 0; i < numRing; i++ { + for j := 0; j < ringsize; j++ { + if j != s { + randomGenerated, err := rand.Int(rand.Reader, curve.Params().P) + if err != nil { + return nil, err + } + S[i][j] = randomGenerated + } + } + } + + L := make([][]*ecdsa.PublicKey, numRing) + R := make([][]*ecdsa.PublicKey, numRing) + for i := 0; i < numRing; i++ { + L[i] = make([]*ecdsa.PublicKey, ringsize) + R[i] = make([]*ecdsa.PublicKey, ringsize) + } + alpha := make([]*big.Int, numRing) + + var l []byte + //compute L[i][s], R[i][s], i = 0..numRing + for i := 0; i < numRing; i++ { + randomGenerated, err := rand.Int(rand.Reader, curve.Params().P) + if err != nil { + return nil, err + } + alpha[i] = randomGenerated + // start at secret index s/PI + // compute L_s = u*G + l_x, l_y := curve.ScalarBaseMult(PadTo32Bytes(alpha[i].Bytes())) + L[i][s] = &ecdsa.PublicKey{curve, l_x, l_y} + lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) + l = append(l, lT...) + // compute R_s = u*H_p(P[s]) + h_x, h_y := HashPoint(pubkeys[i]) + r_x, r_y := curve.ScalarMult(h_x, h_y, PadTo32Bytes(alpha[i].Bytes())) + R[i][s] = &ecdsa.PublicKey{curve, r_x, r_y} + rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) + l = append(l, rT...) + } + + // concatenate m and u*G and calculate c[s+1] = H(m, L_s, R_s) + C_j := crypto.Keccak256(append(m[:], l...)) + idx := s + 1 + if idx == ringsize { + idx = 0 + } + if idx == 0 { + C[0] = new(big.Int).SetBytes(C_j[:]) + } else { + C[idx] = new(big.Int).SetBytes(C_j[:]) + } + for idx != s { + var l []byte + for j := 0; j < numRing; j++ { + // calculate L[j][idx] = s[j][idx]*G + c[idx]*Ring[j][idx] + px, py := curve.ScalarMult(rings[j][idx].X, rings[j][idx].Y, PadTo32Bytes(C[idx].Bytes())) // px, py = c_i*P_i + sx, sy := curve.ScalarBaseMult(PadTo32Bytes(S[j][idx].Bytes())) // sx, sy = s[n-1]*G + if px == nil || py == nil || sx == nil || sy == nil { + return nil, errors.New("Could not create ring signature") + } + l_x, l_y := curve.Add(sx, sy, px, py) + L[j][idx] = &ecdsa.PublicKey{curve, l_x, l_y} + lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) + l = append(l, lT...) + + // calculate R[j][idx] = s[j][idx]*H_p(Ring[j][idx]) + c[idx]*I[j] + px, py = curve.ScalarMult(images[j].X, images[j].Y, C[idx].Bytes()) // px, py = c_i*I + hx, hy := HashPoint(rings[j][idx]) + sx, sy = curve.ScalarMult(hx, hy, S[j][idx].Bytes()) // sx, sy = s[n-1]*H_p(P_i) + if px == nil || py == nil || sx == nil || sy == nil { + return nil, errors.New("Could not create ring signature") + } + r_x, r_y := curve.Add(sx, sy, px, py) + R[j][idx] = &ecdsa.PublicKey{curve, r_x, r_y} + rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) + l = append(l, rT...) + } + + idx++ + if idx == ringsize { + idx = 0 + } + + var ciIdx int + if idx == 0 { + ciIdx = 0 + } else { + ciIdx = idx + } + cSha := crypto.Keccak256(append(PadTo32Bytes(m[:]), l...)) + C[ciIdx] = new(big.Int).SetBytes(cSha[:]) + } + + //compute S[j][s] = alpha[j] - c[s] * privkeys[j], privkeys[j] = private key corresponding to key image I[j] + for j := 0; j < numRing; j++ { + cx := C[s] + // close ring by finding S[j][s] = (alpha[j] - c[s]*privkeys[s] ) mod P where k[s] is the private key and P is the order of the curve + S[j][s] = new(big.Int).Mod(new(big.Int).Sub(alpha[j], new(big.Int).Mul(cx, privkeys[j].D)), curve.Params().N) + } + + // everything ok, add values to signature + sig.S = S + sig.C = C[0] + sig.NumRing = numRing + sig.Size = ringsize + sig.C = C[0] + + return sig, nil +} + +// verify ring signature contained in RingSignature struct +// returns true if a valid signature, false otherwise +func Verify(sig *RingSignature, verifyMes bool) bool { + // setup + rings := sig.Ring + ringsize := sig.Size + numRing := sig.NumRing + S := sig.S + C := make([]*big.Int, ringsize+1) + C[0] = sig.C + //cast to BitCurve used in go-eth since elliptic.Curve.Add() and elliptic.Curve.ScalarMult() is deprecated + curve := sig.Curve.(*secp256k1.BitCurve) + image := sig.I + + //check on curve + for i := 0; i < numRing; i++ { + onCurve := curve.IsOnCurve(image[i].X, image[i].Y) + if !onCurve { + return false + } + for j := 0; j < ringsize; j++ { + onCurve := curve.IsOnCurve(rings[i][j].X, rings[i][j].Y) + if !onCurve { + return false + } + } + } + + // calculate c[i+1] = H(m, s[i]*G + c[i]*P[i]) + // and c[0] = H)(m, s[n-1]*G + c[n-1]*P[n-1]) where n is the ring size + //log.Info("C", "0", common.Bytes2Hex(C[0].Bytes())) + for j := 0; j < ringsize; j++ { + var l []byte + for i := 0; i < numRing; i++ { + // Validate S[i][j] and C[j] + if !isValidScalar(S[i][j], curve) || !isValidScalar(C[j], curve) { + return false // Or handle the error as required + } + + // calculate L[i][j] = s[i][j]*G + c[j]*Ring[i][j] + px, py := curve.ScalarMult(rings[i][j].X, rings[i][j].Y, C[j].Bytes()) // px, py = c_i*P_i + sx, sy := curve.ScalarBaseMult(S[i][j].Bytes()) // sx, sy = s[i]*G + if px == nil || py == nil || sx == nil || sy == nil { + return false + } + l_x, l_y := curve.Add(sx, sy, px, py) + lT := append(PadTo32Bytes(l_x.Bytes()), PadTo32Bytes(l_y.Bytes())...) + //log.Info("L[i][j]", "i", i, "j", j, "L", common.Bytes2Hex(lT)) + l = append(l, lT...) + + // calculate R_i = s[i][j]*H_p(Ring[i][j]) + c[j]*I[j] + px, py = curve.ScalarMult(image[i].X, image[i].Y, C[j].Bytes()) // px, py = c[i]*I + hx, hy := HashPoint(rings[i][j]) + + // Validate S[i][j], hx, and hy + if !isValidScalar(S[i][j], curve) || !isValidScalar(hx, curve) || !isValidScalar(hy, curve) { + return false // Or handle the error as required + } + //log.Info("H[i][j]", "i", i, "j", j, "x.input", common.Bytes2Hex(rings[i][j].X.Bytes()), "y.input", common.Bytes2Hex(rings[i][j].Y.Bytes())) + //log.Info("H[i][j]", "i", i, "j", j, "x", common.Bytes2Hex(hx.Bytes()), "y", common.Bytes2Hex(hy.Bytes())) + sx, sy = curve.ScalarMult(hx, hy, S[i][j].Bytes()) // sx, sy = s[i]*H_p(P[i]) + if px == nil || py == nil || sx == nil || sy == nil { + return false + } + r_x, r_y := curve.Add(sx, sy, px, py) + rT := append(PadTo32Bytes(r_x.Bytes()), PadTo32Bytes(r_y.Bytes())...) + //log.Info("R[i][j]", "i", i, "j", j, "L", common.Bytes2Hex(rT)) + l = append(l, rT...) + } + + // calculate c[i+1] = H(m, L_i, R_i) + //cj_mes := append(PadTo32Bytes(sig.M[:]), l...) + C_j := crypto.Keccak256(append(PadTo32Bytes(sig.M[:]), l...)) + //log.Info("C hash input", "j", j + 1, "C_input", common.Bytes2Hex(cj_mes)) + + /*if j == ringsize-1 { + C[0] = new(big.Int).SetBytes(C_j[:]) + } else {*/ + C[j+1] = new(big.Int).SetBytes(C_j[:]) + //log.Info("C", "j", j + 1, "C", common.Bytes2Hex(C[j + 1].Bytes())) + //} + } + + return bytes.Equal(sig.C.Bytes(), C[ringsize].Bytes()) +} + +func isValidScalar(scalar *big.Int, curve elliptic.Curve) bool { + return scalar.Sign() >= 0 && scalar.Cmp(curve.Params().N) < 0 +} + +func Link(sig_a *RingSignature, sig_b *RingSignature) bool { + for i := 0; i < len(sig_a.I); i++ { + for j := 0; j < len(sig_b.I); j++ { + if sig_a.I[i].X == sig_b.I[j].X && sig_a.I[i].Y == sig_b.I[j].Y { + return true + } + } + } + return false +} + +// function returns(mutiple rings, private keys, message, error) +func GenerateMultiRingParams(numRing int, ringSize int, s int) (rings []Ring, privkeys []*ecdsa.PrivateKey, m [32]byte, err error) { + for i := 0; i < numRing; i++ { + privkey, err := crypto.GenerateKey() + if err != nil { + return nil, nil, [32]byte{}, err + } + privkeys = append(privkeys, privkey) + + ring, err := GenNewKeyRing(ringSize, privkey, s) + if err != nil { + return nil, nil, [32]byte{}, err + } + rings = append(rings, ring) + } + + _, err = rand.Read(m[:]) + if err != nil { + return nil, nil, [32]byte{}, err + } + return rings, privkeys, m, nil +} diff --git a/core/vm/privacy/ringct_test.go b/core/vm/privacy/ringct_test.go new file mode 100644 index 000000000000..3e5b771c0bce --- /dev/null +++ b/core/vm/privacy/ringct_test.go @@ -0,0 +1,324 @@ +package privacy + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/stretchr/testify/assert" + + "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1" +) + +func TestSign(t *testing.T) { + /*for i := 14; i < 15; i++ { + for j := 14; j < 15; j++ { + for k := 0; k <= j; k++ {*/ + numRing := 5 + ringSize := 10 + s := 9 + fmt.Println("Generate random ring parameter ") + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + + fmt.Println("numRing ", numRing) + fmt.Println("ringSize ", ringSize) + fmt.Println("index of real one ", s) + + fmt.Println("Ring ", rings) + fmt.Println("privkeys ", privkeys) + fmt.Println("m ", m) + + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("Failed to create Ring signature") + } + + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + + deserializedSig, err := Deserialize(sig) + if err != nil { + t.Error("Failed to Deserialize Ring signature") + } + verified := Verify(deserializedSig, false) + + if !verified { + t.Error("Failed to verify Ring signature") + } + +} + +func TestDeserialize(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 5 + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("Failed to create Ring signature") + } + + // A normal signature. + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + + // Modify the serialized signature s.t. + // the new signature passes the length check + // but triggers buffer overflow in Deserialize(). + // ringSize: 10 -> 56759212534490939 + // len(sig): 3495 -> 3804 + // 80 + 5 * (56759212534490939*65 + 33) = 18446744073709551616 + 3804 + bs := make([]byte, 8) + binary.BigEndian.PutUint64(bs, 56759212534490939) + for i := 0; i < 8; i++ { + sig[i+8] = bs[i] + } + tail := make([]byte, 3804-len(sig)) + sig = append(sig, tail...) + + _, err = Deserialize(sig) + assert.EqualError(t, err, "incorrect ring size, len r: 3804, sig.NumRing: 5 sig.Size: 56759212534490939") +} + +func TestVerify1(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 7 + + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + if err != nil { + t.Error("fail to generate rings") + } + + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("fail to create ring signature") + } + + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("fail to serialize input ring signature") + } + + deserializedSig, err := Deserialize(sig) + if err != nil { + t.Error("fail to deserialize ring signature") + } + + assert.True(t, Verify(deserializedSig, false), "Verify should return true") +} + +func TestDeserialize2(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 7 + + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + if err != nil { + t.Error("fail to generate rings") + } + + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("fail to create ring signature") + } + + // change one sig to the scalar field + ringSignature.S[0][0] = curve.Params().N + + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("fail to serialize input ring signature") + } + + _, err = Deserialize(sig) + assert.EqualError(t, err, "failed to deserialize, invalid ring signature") +} + +func TestPadTo32Bytes(t *testing.T) { + arr := [44]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34} + + // test input slice is longer than 32 bytes + assert.True(t, bytes.Equal(PadTo32Bytes(arr[0:]), arr[0:32]), "Test PadTo32Bytes longer than 32 bytes #1") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[1:]), arr[1:33]), "Test PadTo32Bytes longer than 32 bytes #2") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[2:]), arr[2:34]), "Test PadTo32Bytes longer than 32 bytes #3") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[3:]), arr[3:35]), "Test PadTo32Bytes longer than 32 bytes #4") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[4:]), arr[4:36]), "Test PadTo32Bytes longer than 32 bytes #5") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[5:]), arr[5:37]), "Test PadTo32Bytes longer than 32 bytes #6") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[6:]), arr[6:38]), "Test PadTo32Bytes longer than 32 bytes #7") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[7:]), arr[7:39]), "Test PadTo32Bytes longer than 32 bytes #8") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[8:]), arr[8:40]), "Test PadTo32Bytes longer than 32 bytes #9") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[9:]), arr[9:41]), "Test PadTo32Bytes longer than 32 bytes #10") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:]), arr[10:42]), "Test PadTo32Bytes longer than 32 bytes #11") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[11:]), arr[11:43]), "Test PadTo32Bytes longer than 32 bytes #12") + + // test input slice is equal 32 bytes + assert.True(t, bytes.Equal(PadTo32Bytes(arr[0:32]), arr[0:32]), "Test PadTo32Bytes equal 32 bytes #1") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[1:33]), arr[1:33]), "Test PadTo32Bytes equal 32 bytes #2") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[2:34]), arr[2:34]), "Test PadTo32Bytes equal 32 bytes #3") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[3:35]), arr[3:35]), "Test PadTo32Bytes equal 32 bytes #4") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[4:36]), arr[4:36]), "Test PadTo32Bytes equal 32 bytes #5") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[5:37]), arr[5:37]), "Test PadTo32Bytes equal 32 bytes #6") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[6:38]), arr[6:38]), "Test PadTo32Bytes equal 32 bytes #7") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[7:39]), arr[7:39]), "Test PadTo32Bytes equal 32 bytes #8") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[8:40]), arr[8:40]), "Test PadTo32Bytes equal 32 bytes #9") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[9:41]), arr[9:41]), "Test PadTo32Bytes equal 32 bytes #10") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:42]), arr[10:42]), "Test PadTo32Bytes equal 32 bytes #11") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[11:43]), arr[11:43]), "Test PadTo32Bytes equal 32 bytes #12") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[12:44]), arr[12:44]), "Test PadTo32Bytes equal 32 bytes #13") + + // test input slice is shorter than 32 bytes + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:32]), arr[0:32]), "Test PadTo32Bytes shorter than 32 bytes #1") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:33]), arr[1:33]), "Test PadTo32Bytes shorter than 32 bytes #2") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:34]), arr[2:34]), "Test PadTo32Bytes shorter than 32 bytes #3") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:35]), arr[3:35]), "Test PadTo32Bytes shorter than 32 bytes #4") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:36]), arr[4:36]), "Test PadTo32Bytes shorter than 32 bytes #5") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:37]), arr[5:37]), "Test PadTo32Bytes shorter than 32 bytes #6") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:38]), arr[6:38]), "Test PadTo32Bytes shorter than 32 bytes #7") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:39]), arr[7:39]), "Test PadTo32Bytes shorter than 32 bytes #8") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:40]), arr[8:40]), "Test PadTo32Bytes shorter than 32 bytes #9") + assert.True(t, bytes.Equal(PadTo32Bytes(arr[10:41]), arr[9:41]), "Test PadTo32Bytes shorter than 32 bytes #10") +} + +func TestCurveAddNegative(t *testing.T) { + curve := crypto.S256().(*secp256k1.BitCurve) + + x1, y1 := curve.ScalarBaseMult(new(big.Int).SetUint64(uint64(2)).Bytes()) + fmt.Printf("Point(%x, %x)\n", x1, y1) + + x2 := x1 + y2 := new(big.Int).Neg(y1) // negative of point (x1,y1) + + x3, y3 := curve.Add(x1, y1, x2, y2) + fmt.Printf("Output is Point(%x, %x)\n", x3, y3) + + x0 := new(big.Int).SetUint64(uint64(0)) + y0 := new(big.Int).SetUint64(uint64(0)) // infinity + + if (x3.Cmp(x0) == 0) && (y3.Cmp(y0) == 0) { + // fmt.Printf("Correct, add negative of self should yield (0,0)") + } else { + t.Error("Incorrect, add negative of self did not yield (0,0)") + } +} + +func TestCurveAddZero(t *testing.T) { + // curve := crypto.S256() + curve := crypto.S256().(*secp256k1.BitCurve) + + x1, y1 := curve.ScalarBaseMult(new(big.Int).SetUint64(uint64(1)).Bytes()) + fmt.Printf("Point(%x, %x)\n", x1, y1) + + x0 := new(big.Int).SetUint64(uint64(0)) + y0 := new(big.Int).SetUint64(uint64(0)) // infinity + fmt.Printf("Is point (%d,%d) on the curve: %t \n", x0, y0, curve.IsOnCurve(x0, y0)) + + x2, y2 := curve.Add(x1, y1, x0, y0) + fmt.Printf("Output is Point(%x, %x)\n", x2, y2) + + if (x1.Cmp(x2) == 0) && (y1.Cmp(y2) == 0) { + // fmt.Printf("Correct, Point on curve is the same after Zero addition\n") + } else { + t.Error("Incorrect, Point on curve changed after Zero addition\n") + } +} + +func TestOnCurveVerify(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 5 + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("Failed to create Ring signature") + } + + valid := Verify(ringSignature, false) + if !valid { + t.Error("Incorrect, unmodified ringSignature should be valid") + } + + ringsModified := ringSignature.Ring + ringsModified[0][0].X = big.NewInt(1) + ringsModified[0][0].Y = big.NewInt(1) + valid = Verify(ringSignature, false) + if valid { + t.Error("Incorrect, modified ringSignature should be invalid") + } +} + +func TestOnCurveDeserialize(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 5 + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + ringSignature, err := Sign(m, rings, privkeys, s) + if err != nil { + t.Error("Failed to create Ring signature") + } + + sig, err := ringSignature.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + _, err = Deserialize(sig) + if err != nil { + t.Error("Failed to Deserialize") + } + + ringsModified := ringSignature.Ring + ringsModified[0][0].X = big.NewInt(1) + ringsModified[0][0].Y = big.NewInt(1) + + sig, err = ringSignature.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + _, err = Deserialize(sig) + assert.EqualError(t, err, "failed to deserialize, invalid ring signature") +} + +func TestCurveScalarMult(t *testing.T) { + curve := crypto.S256().(*secp256k1.BitCurve) + + x, y := curve.ScalarBaseMult(curve.Params().N.Bytes()) + if x == nil && y == nil { + fmt.Println("Scalar multiplication with base point returns nil when scalar is the scalar field") + } + + x2, y2 := curve.ScalarMult(new(big.Int).SetUint64(uint64(100)), new(big.Int).SetUint64(uint64(2)), curve.Params().N.Bytes()) + if x2 == nil && y2 == nil { + fmt.Println("Scalar multiplication with a point (not necessarily on curve) returns nil when scalar is the scalar field") + } +} + +func TestNilPointerDereferencePanic(t *testing.T) { + numRing := 5 + ringSize := 10 + s := 7 + rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s) + + ringSig, err := Sign(m, rings, privkeys, s) + if err != nil { + fmt.Println("Failed to set up") + } + + ringSig.S[0][0] = curve.Params().N // change one sig to the scalar field + + sig, err := ringSig.Serialize() + if err != nil { + t.Error("Failed to Serialize input Ring signature") + } + + _ , err = Deserialize(sig) + // Should failed to verify Ring signature as the signature is invalid + assert.EqualError(t, err, "failed to deserialize, invalid ring signature") +} From 6ba7c2670325f27107c4267c597bccf375980b2a Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 7 Mar 2024 19:56:31 +0800 Subject: [PATCH 49/59] common/lru: add generic LRU implementation (#26162) --- common/lru/basiclru.go | 223 +++++++++++++++++++++++++++++++ common/lru/basiclru_test.go | 255 ++++++++++++++++++++++++++++++++++++ common/lru/blob_lru.go | 84 ++++++++++++ common/lru/blob_lru_test.go | 155 ++++++++++++++++++++++ common/lru/lru.go | 95 ++++++++++++++ 5 files changed, 812 insertions(+) create mode 100644 common/lru/basiclru.go create mode 100644 common/lru/basiclru_test.go create mode 100644 common/lru/blob_lru.go create mode 100644 common/lru/blob_lru_test.go create mode 100644 common/lru/lru.go diff --git a/common/lru/basiclru.go b/common/lru/basiclru.go new file mode 100644 index 000000000000..a429157fe50a --- /dev/null +++ b/common/lru/basiclru.go @@ -0,0 +1,223 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package lru implements generically-typed LRU caches. +package lru + +// BasicLRU is a simple LRU cache. +// +// This type is not safe for concurrent use. +// The zero value is not valid, instances must be created using NewCache. +type BasicLRU[K comparable, V any] struct { + list *list[K] + items map[K]cacheItem[K, V] + cap int +} + +type cacheItem[K any, V any] struct { + elem *listElem[K] + value V +} + +// NewBasicLRU creates a new LRU cache. +func NewBasicLRU[K comparable, V any](capacity int) BasicLRU[K, V] { + if capacity <= 0 { + capacity = 1 + } + c := BasicLRU[K, V]{ + items: make(map[K]cacheItem[K, V]), + list: newList[K](), + cap: capacity, + } + return c +} + +// Add adds a value to the cache. Returns true if an item was evicted to store the new item. +func (c *BasicLRU[K, V]) Add(key K, value V) (evicted bool) { + item, ok := c.items[key] + if ok { + // Already exists in cache. + item.value = value + c.items[key] = item + c.list.moveToFront(item.elem) + return false + } + + var elem *listElem[K] + if c.Len() >= c.cap { + elem = c.list.removeLast() + delete(c.items, elem.v) + evicted = true + } else { + elem = new(listElem[K]) + } + + // Store the new item. + // Note that, if another item was evicted, we re-use its list element here. + elem.v = key + c.items[key] = cacheItem[K, V]{elem, value} + c.list.pushElem(elem) + return evicted +} + +// Contains reports whether the given key exists in the cache. +func (c *BasicLRU[K, V]) Contains(key K) bool { + _, ok := c.items[key] + return ok +} + +// Get retrieves a value from the cache. This marks the key as recently used. +func (c *BasicLRU[K, V]) Get(key K) (value V, ok bool) { + item, ok := c.items[key] + if !ok { + return value, false + } + c.list.moveToFront(item.elem) + return item.value, true +} + +// GetOldest retrieves the least-recently-used item. +// Note that this does not update the item's recency. +func (c *BasicLRU[K, V]) GetOldest() (key K, value V, ok bool) { + lastElem := c.list.last() + if lastElem == nil { + return key, value, false + } + key = lastElem.v + item := c.items[key] + return key, item.value, true +} + +// Len returns the current number of items in the cache. +func (c *BasicLRU[K, V]) Len() int { + return len(c.items) +} + +// Peek retrieves a value from the cache, but does not mark the key as recently used. +func (c *BasicLRU[K, V]) Peek(key K) (value V, ok bool) { + item, ok := c.items[key] + return item.value, ok +} + +// Purge empties the cache. +func (c *BasicLRU[K, V]) Purge() { + c.list.init() + for k := range c.items { + delete(c.items, k) + } +} + +// Remove drops an item from the cache. Returns true if the key was present in cache. +func (c *BasicLRU[K, V]) Remove(key K) bool { + item, ok := c.items[key] + if ok { + delete(c.items, key) + c.list.remove(item.elem) + } + return ok +} + +// RemoveOldest drops the least recently used item. +func (c *BasicLRU[K, V]) RemoveOldest() (key K, value V, ok bool) { + lastElem := c.list.last() + if lastElem == nil { + return key, value, false + } + + key = lastElem.v + item := c.items[key] + delete(c.items, key) + c.list.remove(lastElem) + return key, item.value, true +} + +// Keys returns all keys in the cache. +func (c *BasicLRU[K, V]) Keys() []K { + keys := make([]K, 0, len(c.items)) + return c.list.appendTo(keys) +} + +// list is a doubly-linked list holding items of type he. +// The zero value is not valid, use newList to create lists. +type list[T any] struct { + root listElem[T] +} + +type listElem[T any] struct { + next *listElem[T] + prev *listElem[T] + v T +} + +func newList[T any]() *list[T] { + l := new(list[T]) + l.init() + return l +} + +// init reinitializes the list, making it empty. +func (l *list[T]) init() { + l.root.next = &l.root + l.root.prev = &l.root +} + +// push adds an element to the front of the list. +func (l *list[T]) pushElem(e *listElem[T]) { + e.prev = &l.root + e.next = l.root.next + l.root.next = e + e.next.prev = e +} + +// moveToFront makes 'node' the head of the list. +func (l *list[T]) moveToFront(e *listElem[T]) { + e.prev.next = e.next + e.next.prev = e.prev + l.pushElem(e) +} + +// remove removes an element from the list. +func (l *list[T]) remove(e *listElem[T]) { + e.prev.next = e.next + e.next.prev = e.prev + e.next, e.prev = nil, nil +} + +// removeLast removes the last element of the list. +func (l *list[T]) removeLast() *listElem[T] { + last := l.last() + if last != nil { + l.remove(last) + } + return last +} + +// last returns the last element of the list, or nil if the list is empty. +func (l *list[T]) last() *listElem[T] { + e := l.root.prev + if e == &l.root { + return nil + } + return e +} + +// appendTo appends all list elements to a slice. +func (l *list[T]) appendTo(slice []T) []T { + for e := l.root.prev; e != &l.root; e = e.prev { + slice = append(slice, e.v) + } + return slice +} diff --git a/common/lru/basiclru_test.go b/common/lru/basiclru_test.go new file mode 100644 index 000000000000..29812bda157a --- /dev/null +++ b/common/lru/basiclru_test.go @@ -0,0 +1,255 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package lru + +import ( + crand "crypto/rand" + "fmt" + "io" + "math/rand" + "testing" +) + +// Some of these test cases were adapted +// from https://github.com/hashicorp/golang-lru/blob/master/simplelru/lru_test.go + +func TestBasicLRU(t *testing.T) { + cache := NewBasicLRU[int, int](128) + + for i := 0; i < 256; i++ { + cache.Add(i, i) + } + if cache.Len() != 128 { + t.Fatalf("bad len: %v", cache.Len()) + } + + // Check that Keys returns least-recent key first. + keys := cache.Keys() + if len(keys) != 128 { + t.Fatal("wrong Keys() length", len(keys)) + } + for i, k := range keys { + v, ok := cache.Peek(k) + if !ok { + t.Fatalf("expected key %d be present", i) + } + if v != k { + t.Fatalf("expected %d == %d", k, v) + } + if v != i+128 { + t.Fatalf("wrong value at key %d: %d, want %d", i, v, i+128) + } + } + + for i := 0; i < 128; i++ { + _, ok := cache.Get(i) + if ok { + t.Fatalf("%d should be evicted", i) + } + } + for i := 128; i < 256; i++ { + _, ok := cache.Get(i) + if !ok { + t.Fatalf("%d should not be evicted", i) + } + } + + for i := 128; i < 192; i++ { + ok := cache.Remove(i) + if !ok { + t.Fatalf("%d should be in cache", i) + } + ok = cache.Remove(i) + if ok { + t.Fatalf("%d should not be in cache", i) + } + _, ok = cache.Get(i) + if ok { + t.Fatalf("%d should be deleted", i) + } + } + + // Request item 192. + cache.Get(192) + // It should be the last item returned by Keys(). + for i, k := range cache.Keys() { + if (i < 63 && k != i+193) || (i == 63 && k != 192) { + t.Fatalf("out of order key: %v", k) + } + } + + cache.Purge() + if cache.Len() != 0 { + t.Fatalf("bad len: %v", cache.Len()) + } + if _, ok := cache.Get(200); ok { + t.Fatalf("should contain nothing") + } +} + +func TestBasicLRUAddExistingKey(t *testing.T) { + cache := NewBasicLRU[int, int](1) + + cache.Add(1, 1) + cache.Add(1, 2) + + v, _ := cache.Get(1) + if v != 2 { + t.Fatal("wrong value:", v) + } +} + +// This test checks GetOldest and RemoveOldest. +func TestBasicLRUGetOldest(t *testing.T) { + cache := NewBasicLRU[int, int](128) + for i := 0; i < 256; i++ { + cache.Add(i, i) + } + + k, _, ok := cache.GetOldest() + if !ok { + t.Fatalf("missing") + } + if k != 128 { + t.Fatalf("bad: %v", k) + } + + k, _, ok = cache.RemoveOldest() + if !ok { + t.Fatalf("missing") + } + if k != 128 { + t.Fatalf("bad: %v", k) + } + + k, _, ok = cache.RemoveOldest() + if !ok { + t.Fatalf("missing oldest item") + } + if k != 129 { + t.Fatalf("wrong oldest item: %v", k) + } +} + +// Test that Add returns true/false if an eviction occurred +func TestBasicLRUAddReturnValue(t *testing.T) { + cache := NewBasicLRU[int, int](1) + if cache.Add(1, 1) { + t.Errorf("first add shouldn't have evicted") + } + if !cache.Add(2, 2) { + t.Errorf("second add should have evicted") + } +} + +// This test verifies that Contains doesn't change item recency. +func TestBasicLRUContains(t *testing.T) { + cache := NewBasicLRU[int, int](2) + cache.Add(1, 1) + cache.Add(2, 2) + if !cache.Contains(1) { + t.Errorf("1 should be in the cache") + } + cache.Add(3, 3) + if cache.Contains(1) { + t.Errorf("Contains should not have updated recency of 1") + } +} + +// Test that Peek doesn't update recent-ness +func TestBasicLRUPeek(t *testing.T) { + cache := NewBasicLRU[int, int](2) + cache.Add(1, 1) + cache.Add(2, 2) + if v, ok := cache.Peek(1); !ok || v != 1 { + t.Errorf("1 should be set to 1") + } + cache.Add(3, 3) + if cache.Contains(1) { + t.Errorf("should not have updated recent-ness of 1") + } +} + +func BenchmarkLRU(b *testing.B) { + var ( + capacity = 1000 + indexes = make([]int, capacity*20) + keys = make([]string, capacity) + values = make([][]byte, capacity) + ) + for i := range indexes { + indexes[i] = rand.Intn(capacity) + } + for i := range keys { + b := make([]byte, 32) + crand.Read(b) + keys[i] = string(b) + crand.Read(b) + values[i] = b + } + + var sink []byte + + b.Run("Add/BasicLRU", func(b *testing.B) { + cache := NewBasicLRU[int, int](capacity) + for i := 0; i < b.N; i++ { + cache.Add(i, i) + } + }) + b.Run("Get/BasicLRU", func(b *testing.B) { + cache := NewBasicLRU[string, []byte](capacity) + for i := 0; i < capacity; i++ { + index := indexes[i] + cache.Add(keys[index], values[index]) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + k := keys[indexes[i%len(indexes)]] + v, ok := cache.Get(k) + if ok { + sink = v + } + } + }) + + // // vs. github.com/hashicorp/golang-lru/simplelru + // b.Run("Add/simplelru.LRU", func(b *testing.B) { + // cache, _ := simplelru.NewLRU(capacity, nil) + // for i := 0; i < b.N; i++ { + // cache.Add(i, i) + // } + // }) + // b.Run("Get/simplelru.LRU", func(b *testing.B) { + // cache, _ := simplelru.NewLRU(capacity, nil) + // for i := 0; i < capacity; i++ { + // index := indexes[i] + // cache.Add(keys[index], values[index]) + // } + // + // b.ResetTimer() + // for i := 0; i < b.N; i++ { + // k := keys[indexes[i%len(indexes)]] + // v, ok := cache.Get(k) + // if ok { + // sink = v.([]byte) + // } + // } + // }) + + fmt.Fprintln(io.Discard, sink) +} diff --git a/common/lru/blob_lru.go b/common/lru/blob_lru.go new file mode 100644 index 000000000000..c9b33985032c --- /dev/null +++ b/common/lru/blob_lru.go @@ -0,0 +1,84 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package lru + +import ( + "math" + "sync" +) + +// blobType is the type constraint for values stored in SizeConstrainedCache. +type blobType interface { + ~[]byte | ~string +} + +// SizeConstrainedCache is a cache where capacity is in bytes (instead of item count). When the cache +// is at capacity, and a new item is added, older items are evicted until the size +// constraint is met. +// +// OBS: This cache assumes that items are content-addressed: keys are unique per content. +// In other words: two Add(..) with the same key K, will always have the same value V. +type SizeConstrainedCache[K comparable, V blobType] struct { + size uint64 + maxSize uint64 + lru BasicLRU[K, V] + lock sync.Mutex +} + +// NewSizeConstrainedCache creates a new size-constrained LRU cache. +func NewSizeConstrainedCache[K comparable, V blobType](maxSize uint64) *SizeConstrainedCache[K, V] { + return &SizeConstrainedCache[K, V]{ + size: 0, + maxSize: maxSize, + lru: NewBasicLRU[K, V](math.MaxInt), + } +} + +// Add adds a value to the cache. Returns true if an eviction occurred. +// OBS: This cache assumes that items are content-addressed: keys are unique per content. +// In other words: two Add(..) with the same key K, will always have the same value V. +// OBS: The value is _not_ copied on Add, so the caller must not modify it afterwards. +func (c *SizeConstrainedCache[K, V]) Add(key K, value V) (evicted bool) { + c.lock.Lock() + defer c.lock.Unlock() + + // Unless it is already present, might need to evict something. + // OBS: If it is present, we still call Add internally to bump the recentness. + if !c.lru.Contains(key) { + targetSize := c.size + uint64(len(value)) + for targetSize > c.maxSize { + evicted = true + _, v, ok := c.lru.RemoveOldest() + if !ok { + // list is now empty. Break + break + } + targetSize -= uint64(len(v)) + } + c.size = targetSize + } + c.lru.Add(key, value) + return evicted +} + +// Get looks up a key's value from the cache. +func (c *SizeConstrainedCache[K, V]) Get(key K) (V, bool) { + c.lock.Lock() + defer c.lock.Unlock() + + return c.lru.Get(key) +} diff --git a/common/lru/blob_lru_test.go b/common/lru/blob_lru_test.go new file mode 100644 index 000000000000..ca1b0ddd742a --- /dev/null +++ b/common/lru/blob_lru_test.go @@ -0,0 +1,155 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package lru + +import ( + "encoding/binary" + "fmt" + "testing" +) + +type testKey [8]byte + +func mkKey(i int) (key testKey) { + binary.LittleEndian.PutUint64(key[:], uint64(i)) + return key +} + +func TestSizeConstrainedCache(t *testing.T) { + lru := NewSizeConstrainedCache[testKey, []byte](100) + var want uint64 + // Add 11 items of 10 byte each. First item should be swapped out + for i := 0; i < 11; i++ { + k := mkKey(i) + v := fmt.Sprintf("value-%04d", i) + lru.Add(k, []byte(v)) + want += uint64(len(v)) + if want > 100 { + want = 100 + } + if have := lru.size; have != want { + t.Fatalf("size wrong, have %d want %d", have, want) + } + } + // Zero:th should be evicted + { + k := mkKey(0) + if _, ok := lru.Get(k); ok { + t.Fatalf("should be evicted: %v", k) + } + } + // Elems 1-11 should be present + for i := 1; i < 11; i++ { + k := mkKey(i) + want := fmt.Sprintf("value-%04d", i) + have, ok := lru.Get(k) + if !ok { + t.Fatalf("missing key %v", k) + } + if string(have) != want { + t.Fatalf("wrong value, have %v want %v", have, want) + } + } +} + +// This test adds inserting an element exceeding the max size. +func TestSizeConstrainedCacheOverflow(t *testing.T) { + lru := NewSizeConstrainedCache[testKey, []byte](100) + + // Add 10 items of 10 byte each, filling the cache + for i := 0; i < 10; i++ { + k := mkKey(i) + v := fmt.Sprintf("value-%04d", i) + lru.Add(k, []byte(v)) + } + // Add one single large elem. We expect it to swap out all entries. + { + k := mkKey(1337) + v := make([]byte, 200) + lru.Add(k, v) + } + // Elems 0-9 should be missing + for i := 1; i < 10; i++ { + k := mkKey(i) + if _, ok := lru.Get(k); ok { + t.Fatalf("should be evicted: %v", k) + } + } + // The size should be accurate + if have, want := lru.size, uint64(200); have != want { + t.Fatalf("size wrong, have %d want %d", have, want) + } + // Adding one small item should swap out the large one + { + i := 0 + k := mkKey(i) + v := fmt.Sprintf("value-%04d", i) + lru.Add(k, []byte(v)) + if have, want := lru.size, uint64(10); have != want { + t.Fatalf("size wrong, have %d want %d", have, want) + } + } +} + +// This checks what happens when inserting the same k/v multiple times. +func TestSizeConstrainedCacheSameItem(t *testing.T) { + lru := NewSizeConstrainedCache[testKey, []byte](100) + + // Add one 10 byte-item 10 times. + k := mkKey(0) + v := fmt.Sprintf("value-%04d", 0) + for i := 0; i < 10; i++ { + lru.Add(k, []byte(v)) + } + + // The size should be accurate. + if have, want := lru.size, uint64(10); have != want { + t.Fatalf("size wrong, have %d want %d", have, want) + } +} + +// This tests that empty/nil values are handled correctly. +func TestSizeConstrainedCacheEmpties(t *testing.T) { + lru := NewSizeConstrainedCache[testKey, []byte](100) + + // This test abuses the lru a bit, using different keys for identical value(s). + for i := 0; i < 10; i++ { + lru.Add(testKey{byte(i)}, []byte{}) + lru.Add(testKey{byte(255 - i)}, nil) + } + + // The size should not count, only the values count. So this could be a DoS + // since it basically has no cap, and it is intentionally overloaded with + // different-keyed 0-length values. + if have, want := lru.size, uint64(0); have != want { + t.Fatalf("size wrong, have %d want %d", have, want) + } + + for i := 0; i < 10; i++ { + if v, ok := lru.Get(testKey{byte(i)}); !ok { + t.Fatalf("test %d: expected presence", i) + } else if v == nil { + t.Fatalf("test %d, v is nil", i) + } + + if v, ok := lru.Get(testKey{byte(255 - i)}); !ok { + t.Fatalf("test %d: expected presence", i) + } else if v != nil { + t.Fatalf("test %d, v is not nil", i) + } + } +} diff --git a/common/lru/lru.go b/common/lru/lru.go new file mode 100644 index 000000000000..45965adb0dfc --- /dev/null +++ b/common/lru/lru.go @@ -0,0 +1,95 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package lru + +import "sync" + +// Cache is a LRU cache. +// This type is safe for concurrent use. +type Cache[K comparable, V any] struct { + cache BasicLRU[K, V] + mu sync.Mutex +} + +// NewCache creates an LRU cache. +func NewCache[K comparable, V any](capacity int) *Cache[K, V] { + return &Cache[K, V]{cache: NewBasicLRU[K, V](capacity)} +} + +// Add adds a value to the cache. Returns true if an item was evicted to store the new item. +func (c *Cache[K, V]) Add(key K, value V) (evicted bool) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.cache.Add(key, value) +} + +// Contains reports whether the given key exists in the cache. +func (c *Cache[K, V]) Contains(key K) bool { + c.mu.Lock() + defer c.mu.Unlock() + + return c.cache.Contains(key) +} + +// Get retrieves a value from the cache. This marks the key as recently used. +func (c *Cache[K, V]) Get(key K) (value V, ok bool) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.cache.Get(key) +} + +// Len returns the current number of items in the cache. +func (c *Cache[K, V]) Len() int { + c.mu.Lock() + defer c.mu.Unlock() + + return c.cache.Len() +} + +// Peek retrieves a value from the cache, but does not mark the key as recently used. +func (c *Cache[K, V]) Peek(key K) (value V, ok bool) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.cache.Peek(key) +} + +// Purge empties the cache. +func (c *Cache[K, V]) Purge() { + c.mu.Lock() + defer c.mu.Unlock() + + c.cache.Purge() +} + +// Remove drops an item from the cache. Returns true if the key was present in cache. +func (c *Cache[K, V]) Remove(key K) bool { + c.mu.Lock() + defer c.mu.Unlock() + + return c.cache.Remove(key) +} + +// Keys returns all keys of items currently in the LRU. +func (c *Cache[K, V]) Keys() []K { + c.mu.Lock() + defer c.mu.Unlock() + + return c.cache.Keys() +} From 0e36b5be4210c9c604097792a8c1d32ec218d216 Mon Sep 17 00:00:00 2001 From: Wang Gerui Date: Tue, 5 Mar 2024 00:31:14 +0800 Subject: [PATCH 50/59] fix typo in EIPs --- core/vm/eips.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 77dc30a9100d..d096d5a3a8ef 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -29,7 +29,7 @@ import ( // defined jump tables are not polluted. func EnableEIP(eipNum int, jt *JumpTable) error { switch eipNum { - case 3898: + case 3198: enable3198(jt) case 2200: enable2200(jt) From 968440ce3de7e36b6ab85e8a1ec85ab3e7e5704f Mon Sep 17 00:00:00 2001 From: geruiwang Date: Thu, 7 Mar 2024 21:44:37 +0800 Subject: [PATCH 51/59] core/vm: implement EIP-3855: PUSH0 instruction. --- common/constants.go | 1 + common/constants/constants.go.devnet | 1 + common/constants/constants.go.testnet | 1 + core/vm/eips.go | 19 +++++++++++++++++++ core/vm/interpreter.go | 2 ++ core/vm/jump_table.go | 7 +++++++ core/vm/opcodes.go | 27 +++++++++++++++------------ params/config.go | 11 +++++++++-- 8 files changed, 55 insertions(+), 14 deletions(-) diff --git a/common/constants.go b/common/constants.go index c923eeea8da0..803443a7814c 100644 --- a/common/constants.go +++ b/common/constants.go @@ -49,6 +49,7 @@ var TIPXDCXDISABLE = big.NewInt(99999999900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) +var ShanghaiBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(38383838) var IsTestnet bool = false diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index e80dda351fd6..2bbd3ac06a73 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -49,6 +49,7 @@ var TIPXDCXDISABLE = big.NewInt(15894900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) +var ShanghaiBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(0) var IsTestnet bool = false diff --git a/common/constants/constants.go.testnet b/common/constants/constants.go.testnet index 9a0125e701b8..3b2811ade92b 100644 --- a/common/constants/constants.go.testnet +++ b/common/constants/constants.go.testnet @@ -49,6 +49,7 @@ var TIPXDCXDISABLE = big.NewInt(99999999900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) +var ShanghaiBlock = big.NewInt(9999999999) var TIPXDCXTestnet = big.NewInt(23779191) var IsTestnet bool = false diff --git a/core/vm/eips.go b/core/vm/eips.go index d096d5a3a8ef..48ffb0d6917e 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -29,6 +29,8 @@ import ( // defined jump tables are not polluted. func EnableEIP(eipNum int, jt *JumpTable) error { switch eipNum { + case 3855: + enable3855(jt) case 3198: enable3198(jt) case 2200: @@ -112,3 +114,20 @@ func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ callContext.stack.push(baseFee) return nil, nil } + +// enable3855 applies EIP-3855 (PUSH0 opcode) +func enable3855(jt *JumpTable) { + // New opcode + jt[PUSH0] = &operation{ + execute: opPush0, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } +} + +// opPush0 implements the PUSH0 opcode +func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + callContext.stack.push(new(uint256.Int)) + return nil, nil +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 29fc4a2cb763..23cc5a03fe3d 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -93,6 +93,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // If jump table was not initialised we set the default one. if cfg.JumpTable == nil { switch { + case evm.chainRules.IsShanghai: + cfg.JumpTable = &shanghaiInstructionSet case evm.chainRules.IsMerge: cfg.JumpTable = &mergeInstructionSet case evm.chainRules.IsLondon: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 298ec63ab3db..2302a439df6e 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -53,11 +53,18 @@ var ( berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() mergeInstructionSet = newMergeInstructionSet() + shanghaiInstructionSet = newShanghaiInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +func newShanghaiInstructionSet() JumpTable { + instructionSet := newMergeInstructionSet() + enable3855(&instructionSet) // PUSH0 instruction + return instructionSet +} + func newMergeInstructionSet() JumpTable { instructionSet := newLondonInstructionSet() instructionSet[PREVRANDAO] = &operation{ diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 3b123f3a4994..969354cf596f 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -108,18 +108,19 @@ const ( // 0x50 range - 'storage' and execution. const ( - POP OpCode = 0x50 + iota - MLOAD - MSTORE - MSTORE8 - SLOAD - SSTORE - JUMP - JUMPI - PC - MSIZE - GAS - JUMPDEST + POP OpCode = 0x50 + MLOAD OpCode = 0x51 + MSTORE OpCode = 0x52 + MSTORE8 OpCode = 0x53 + SLOAD OpCode = 0x54 + SSTORE OpCode = 0x55 + JUMP OpCode = 0x56 + JUMPI OpCode = 0x57 + PC OpCode = 0x58 + MSIZE OpCode = 0x59 + GAS OpCode = 0x5a + JUMPDEST OpCode = 0x5b + PUSH0 OpCode = 0x5f ) // 0x60 range - pushes. @@ -300,6 +301,7 @@ var opCodeToString = [256]string{ MSIZE: "MSIZE", GAS: "GAS", JUMPDEST: "JUMPDEST", + PUSH0: "PUSH0", // 0x60 range - push. PUSH1: "PUSH1", @@ -462,6 +464,7 @@ var stringToOp = map[string]OpCode{ "MSIZE": MSIZE, "GAS": GAS, "JUMPDEST": JUMPDEST, + "PUSH0": PUSH0, "PUSH1": PUSH1, "PUSH2": PUSH2, "PUSH3": PUSH3, diff --git a/params/config.go b/params/config.go index 0a46f710f345..933d5f2980a9 100644 --- a/params/config.go +++ b/params/config.go @@ -498,7 +498,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %v Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %v ShanghaiBlock: %v Engine: %v}", c.ChainId, c.HomesteadBlock, c.DAOForkBlock, @@ -512,6 +512,7 @@ func (c *ChainConfig) String() string { common.BerlinBlock, common.LondonBlock, common.MergeBlock, + common.ShanghaiBlock, engine, ) } @@ -574,6 +575,11 @@ func (c *ChainConfig) IsMerge(num *big.Int) bool { return isForked(common.MergeBlock, num) } +// IsShanghai returns whether num is either equal to the Shanghai fork block or greater. +func (c *ChainConfig) IsShanghai(num *big.Int) bool { + return isForked(common.ShanghaiBlock, num) +} + func (c *ChainConfig) IsTIP2019(num *big.Int) bool { return isForked(common.TIP2019Block, num) } @@ -742,7 +748,7 @@ type Rules struct { IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool - IsMerge bool + IsMerge, IsShanghai bool } func (c *ChainConfig) Rules(num *big.Int) Rules { @@ -763,5 +769,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsBerlin: c.IsBerlin(num), IsLondon: c.IsLondon(num), IsMerge: c.IsMerge(num), + IsShanghai: c.IsShanghai(num), } } From 863d4f14e307a5e0e8add31de9a3f59ad21ba187 Mon Sep 17 00:00:00 2001 From: geruiwang Date: Thu, 7 Mar 2024 22:31:19 +0800 Subject: [PATCH 52/59] eth/tracers/js: fix isPush for push0 (#28520) Fixes so that `push0` opcode is correctly reported as `true` by the `IsPush` function --------- Co-authored-by: Martin Holst Swende --- core/asm/asm_test.go | 80 ++++++++++++++++++-------------------------- core/vm/opcodes.go | 6 +--- 2 files changed, 33 insertions(+), 53 deletions(-) diff --git a/core/asm/asm_test.go b/core/asm/asm_test.go index 92b26b67a5ca..cd7520ec6394 100644 --- a/core/asm/asm_test.go +++ b/core/asm/asm_test.go @@ -22,53 +22,37 @@ import ( "encoding/hex" ) -// Tests disassembling the instructions for valid evm code -func TestInstructionIteratorValid(t *testing.T) { - cnt := 0 - script, _ := hex.DecodeString("61000000") - - it := NewInstructionIterator(script) - for it.Next() { - cnt++ - } - - if err := it.Error(); err != nil { - t.Errorf("Expected 2, but encountered error %v instead.", err) - } - if cnt != 2 { - t.Errorf("Expected 2, but got %v instead.", cnt) - } -} - -// Tests disassembling the instructions for invalid evm code -func TestInstructionIteratorInvalid(t *testing.T) { - cnt := 0 - script, _ := hex.DecodeString("6100") - - it := NewInstructionIterator(script) - for it.Next() { - cnt++ - } - - if it.Error() == nil { - t.Errorf("Expected an error, but got %v instead.", cnt) - } -} - -// Tests disassembling the instructions for empty evm code -func TestInstructionIteratorEmpty(t *testing.T) { - cnt := 0 - script, _ := hex.DecodeString("") - - it := NewInstructionIterator(script) - for it.Next() { - cnt++ - } - - if err := it.Error(); err != nil { - t.Errorf("Expected 0, but encountered error %v instead.", err) - } - if cnt != 0 { - t.Errorf("Expected 0, but got %v instead.", cnt) +// Tests disassembling instructions +func TestInstructionIterator(t *testing.T) { + for i, tc := range []struct { + want int + code string + wantErr string + }{ + {2, "61000000", ""}, // valid code + {0, "6100", "incomplete push instruction at 0"}, // invalid code + {2, "5900", ""}, // push0 + {0, "", ""}, // empty + + } { + var ( + have int + code, _ = hex.DecodeString(tc.code) + it = NewInstructionIterator(code) + ) + for it.Next() { + have++ + } + var haveErr = "" + if it.Error() != nil { + haveErr = it.Error().Error() + } + if haveErr != tc.wantErr { + t.Errorf("test %d: encountered error: %q want %q", i, haveErr, tc.wantErr) + continue + } + if have != tc.want { + t.Errorf("wrong instruction count, have %d want %d", have, tc.want) + } } } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 969354cf596f..26523e53ce42 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -25,11 +25,7 @@ type OpCode byte // IsPush specifies if an opcode is a PUSH opcode. func (op OpCode) IsPush() bool { - switch op { - case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: - return true - } - return false + return PUSH0 <= op && op <= PUSH32 } // 0x0 range - arithmetic ops. From 771c2f34f3b8fbcfdfa2c01c1f75bf2002e0a30b Mon Sep 17 00:00:00 2001 From: Liam Lai Date: Fri, 8 Mar 2024 15:52:50 +1100 Subject: [PATCH 53/59] enable 0x prefix for devnet code rpcs --- cicd/devnet/start.sh | 2 +- cicd/mainnet/start.sh | 2 +- cicd/testnet/start.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh index 4c3a81ee5b53..d5f7e152f490 100755 --- a/cicd/devnet/start.sh +++ b/cicd/devnet/start.sh @@ -81,5 +81,5 @@ XDC --ethstats ${netstats} --gcmode archive \ --rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ ---ws --wsaddr=0.0.0.0 --wsport $ws_port \ +--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \ --wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cicd/mainnet/start.sh b/cicd/mainnet/start.sh index 35f11a5d3406..35d53ff60188 100755 --- a/cicd/mainnet/start.sh +++ b/cicd/mainnet/start.sh @@ -80,5 +80,5 @@ XDC --ethstats ${netstats} --gcmode archive \ --rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ ---ws --wsaddr=0.0.0.0 --wsport $ws_port \ +--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \ --wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log diff --git a/cicd/testnet/start.sh b/cicd/testnet/start.sh index d5f9a0f443fc..3c5b2234a560 100755 --- a/cicd/testnet/start.sh +++ b/cicd/testnet/start.sh @@ -82,5 +82,5 @@ XDC --ethstats ${netstats} --gcmode archive \ --rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \ --gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \ --debugdatadir /work/xdcchain \ ---ws --wsaddr=0.0.0.0 --wsport $ws_port \ +--enable-0x-prefix --ws --wsaddr=0.0.0.0 --wsport $ws_port \ --wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log From 66e3ebaf0c3c067d97189de5eae494f6ea66bfb8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 8 Mar 2024 14:55:39 +0800 Subject: [PATCH 54/59] enable shanghai for devnet on block 16832700 --- common/constants/constants.go.devnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet index 2bbd3ac06a73..81951a760cd8 100644 --- a/common/constants/constants.go.devnet +++ b/common/constants/constants.go.devnet @@ -49,7 +49,7 @@ var TIPXDCXDISABLE = big.NewInt(15894900) var BerlinBlock = big.NewInt(9999999999) var LondonBlock = big.NewInt(9999999999) var MergeBlock = big.NewInt(9999999999) -var ShanghaiBlock = big.NewInt(9999999999) +var ShanghaiBlock = big.NewInt(16832700) var TIPXDCXTestnet = big.NewInt(0) var IsTestnet bool = false From 75c8d40399b8533366f3b5f901c5e86cef3bb514 Mon Sep 17 00:00:00 2001 From: Wanwiset Peerapatanapokin Date: Fri, 8 Mar 2024 14:57:15 +0400 Subject: [PATCH 55/59] Ensure DB is close before client exit (#478) --- cmd/XDC/chaincmd.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index 2f18dae19600..ae4221e4b211 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -319,7 +319,8 @@ func exportChain(ctx *cli.Context) error { utils.Fatalf("This command requires an argument.") } stack, _ := makeFullNode(ctx) - chain, _ := utils.MakeChain(ctx, stack) + chain, db := utils.MakeChain(ctx, stack) + defer db.Close() start := time.Now() var err error @@ -353,6 +354,7 @@ func importPreimages(ctx *cli.Context) error { } stack, _ := makeFullNode(ctx) diskdb := utils.MakeChainDatabase(ctx, stack) + defer diskdb.Close() start := time.Now() if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil { @@ -369,6 +371,7 @@ func exportPreimages(ctx *cli.Context) error { } stack, _ := makeFullNode(ctx) diskdb := utils.MakeChainDatabase(ctx, stack) + defer diskdb.Close() start := time.Now() if err := utils.ExportPreimages(diskdb, ctx.Args().First()); err != nil { @@ -386,6 +389,7 @@ func copyDb(ctx *cli.Context) error { // Initialize a new chain for the running node to sync into stack, _ := makeFullNode(ctx) chain, chainDb := utils.MakeChain(ctx, stack) + defer chainDb.Close() syncmode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode) dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil, nil) @@ -458,6 +462,8 @@ func removeDB(ctx *cli.Context) error { func dump(ctx *cli.Context) error { stack, _ := makeFullNode(ctx) chain, chainDb := utils.MakeChain(ctx, stack) + defer chainDb.Close() + for _, arg := range ctx.Args() { var block *types.Block if hashish(arg) { @@ -477,7 +483,6 @@ func dump(ctx *cli.Context) error { fmt.Printf("%s\n", state.Dump()) } } - chainDb.Close() return nil } From 910d41565315645282191628e9e11bde25ea9daf Mon Sep 17 00:00:00 2001 From: Wanwiset Peerapatanapokin Date: Fri, 8 Mar 2024 15:38:33 +0400 Subject: [PATCH 56/59] Missing `json` Field Tags in Marshalled Struct (#475) --- core/types/consensus_v2.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/types/consensus_v2.go b/core/types/consensus_v2.go index 00db4c9488b6..8dae6aaa0728 100644 --- a/core/types/consensus_v2.go +++ b/core/types/consensus_v2.go @@ -14,17 +14,17 @@ type Signature []byte // Block Info struct in XDPoS 2.0, used for vote message, etc. type BlockInfo struct { - Hash common.Hash - Round Round - Number *big.Int + Hash common.Hash `json:"hash"` + Round Round `json:"round"` + Number *big.Int `json:"number"` } // Vote message in XDPoS 2.0 type Vote struct { - signer common.Address - ProposedBlockInfo *BlockInfo - Signature Signature - GapNumber uint64 + signer common.Address //field not exported + ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"` + Signature Signature `json:"signature"` + GapNumber uint64 `json:"gapNumber"` } func (v *Vote) Hash() common.Hash { @@ -81,9 +81,9 @@ func (s *SyncInfo) Hash() common.Hash { // Quorum Certificate struct in XDPoS 2.0 type QuorumCert struct { - ProposedBlockInfo *BlockInfo - Signatures []Signature - GapNumber uint64 + ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"` + Signatures []Signature `json:"signatures"` + GapNumber uint64 `json:"gapNumber"` } // Timeout Certificate struct in XDPoS 2.0 From c7cb63dd09ff61c2617e13569b893ee5f8204427 Mon Sep 17 00:00:00 2001 From: Banana-J Date: Sat, 9 Mar 2024 16:25:47 +1100 Subject: [PATCH 57/59] fix: error handling on x.getEpochSwitchInfo in sendTimeout should return error (#444) Co-authored-by: wjrjerome --- consensus/XDPoS/engines/engine_v2/timeout.go | 1 + 1 file changed, 1 insertion(+) diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 4331ba3ecd2f..d53ed386ee16 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -199,6 +199,7 @@ func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error { epochSwitchInfo, err := x.getEpochSwitchInfo(chain, currentBlockHeader, currentBlockHeader.Hash()) if err != nil { log.Error("[sendTimeout] Error when trying to get current epoch switch info for a non-epoch block", "currentRound", x.currentRound, "currentBlockNum", currentBlockHeader.Number, "currentBlockHash", currentBlockHeader.Hash(), "epochNum", epochNum) + return err } gapNumber = epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() - epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64()%x.config.Epoch - x.config.Gap log.Debug("[sendTimeout] non-epoch-switch block found its epoch block and calculated the gapNumber", "epochSwitchInfo.EpochSwitchBlockInfo.Number", epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64(), "gapNumber", gapNumber) From 3051b2de41792e60526a5d30b8daedd33bd46c4d Mon Sep 17 00:00:00 2001 From: Banana-J Date: Sun, 10 Mar 2024 09:51:05 +1100 Subject: [PATCH 58/59] use rlock instead of lock in v1 consensus engine (#483) Co-authored-by: wjrjerome --- consensus/XDPoS/engines/engine_v1/engine.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index b647f43055e0..7fce2e6a8676 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -958,9 +958,9 @@ func (x *XDPoS_v1) Seal(chain consensus.ChainReader, block *types.Block, stop <- // that a new block should have based on the previous blocks in the chain and the // current signer. func (x *XDPoS_v1) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { - x.lock.Lock() + x.lock.RLock() signer := x.signer - x.lock.Unlock() + x.lock.RUnlock() return x.calcDifficulty(chain, parent, signer) } From 45ccc3751b6c4e5af95c773ade728a9f28900a80 Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 11 Mar 2024 18:05:22 +1100 Subject: [PATCH 59/59] bump version to 2.1.0 (#485) Co-authored-by: Liam Lai --- params/version.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/params/version.go b/params/version.go index ea91eea3d59e..f458365c4801 100644 --- a/params/version.go +++ b/params/version.go @@ -22,8 +22,8 @@ import ( const ( VersionMajor = 2 // Major version component of the current release - VersionMinor = 0 // Minor version component of the current release - VersionPatch = 2 // Patch version component of the current release + VersionMinor = 1 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release VersionMeta = "beta1" // Version metadata to append to the version string )