diff --git a/conformance/poseidon_test.go b/conformance/poseidon_test.go index 205873db..0d3017f9 100644 --- a/conformance/poseidon_test.go +++ b/conformance/poseidon_test.go @@ -18,7 +18,7 @@ func TestConformance_Poseidon_Big_Endian(t *testing.T) { 3, 85, 242, 99, 25, 32, 123, 132, 254, 156, 162, 206, 27, 38, 231, 53, 200, 41, 130, 25, 144} - hash, err := sealevel.PoseidonHash([][]byte{bytes1, bytes2}, true) + hash, err := sealevel.PoseidonHash([][]byte{bytes1, bytes2}, true, false) assert.NoError(t, err) assert.Equal(t, expected1, hash) @@ -32,7 +32,7 @@ func TestConformance_Poseidon_Big_Endian(t *testing.T) { 169, 243, 2, 63, 119, 18, 148, 167, 138, 203, 112, 231, 63, 144, 175, 226, 124, 173, 64, 30, 129} - hash, err = sealevel.PoseidonHash([][]byte{input3, input3}, true) + hash, err = sealevel.PoseidonHash([][]byte{input3, input3}, true, false) assert.NoError(t, err) assert.Equal(t, expected2, hash) @@ -42,7 +42,7 @@ func TestConformance_Poseidon_Big_Endian(t *testing.T) { 205, 35, 194, 2, 177, 134, 115, 191, 37, 67, } - hash, err = sealevel.PoseidonHash([][]byte{input3, input3, input3}, true) + hash, err = sealevel.PoseidonHash([][]byte{input3, input3, input3}, true, false) assert.NoError(t, err) assert.Equal(t, expected3, hash) } @@ -58,7 +58,7 @@ func TestConformance_Poseidon_Little_Endian(t *testing.T) { 156, 254, 132, 123, 32, 25, 99, 242, 85, 3, 94, 235, 125, 28, 140, 138, 143, 147, 225, 84, 13} - hash, err := sealevel.PoseidonHash([][]byte{bytes1, bytes2}, false) + hash, err := sealevel.PoseidonHash([][]byte{bytes1, bytes2}, false, false) assert.NoError(t, err) assert.Equal(t, expected, hash) } diff --git a/pkg/features/gates.go b/pkg/features/gates.go index 5825a74e..c6f8b0e5 100644 --- a/pkg/features/gates.go +++ b/pkg/features/gates.go @@ -59,6 +59,11 @@ var EnableSbpfV3DeploymentAndExecution = FeatureGate{Name: "EnableSbpfV3Deployme var DisableSbpfV0Execution = FeatureGate{Name: "DisableSbpfV0Execution", Address: base58.MustDecodeFromString("TestFeature11111111111111111111111111111111")} var ReenableSbpfV0Execution = FeatureGate{Name: "ReenableSbpfV0Execution", Address: base58.MustDecodeFromString("TestFeature21111111111111111111111111111111")} var FormalizeLoadedTransactionDataSize = FeatureGate{Name: "FormalizeLoadedTransactionDataSize", Address: base58.MustDecodeFromString("DeS7sR48ZcFTUmt5FFEVDr1v1bh73aAbZiZq3SYr8Eh8")} +var IncreaseCpiAccountInfoLimit = FeatureGate{Name: "IncreaseCpiAccountInfoLimit", Address: base58.MustDecodeFromString("H6iVbVaDZgDphcPbcZwc5LoznMPWQfnJ1AM7L1xzqvt5")} +var StaticInstructionLimit = FeatureGate{Name: "StaticInstructionLimit", Address: base58.MustDecodeFromString("64ixypL1HPu8WtJhNSMb9mSgfFaJvsANuRkTbHyuLfnx")} +var PoseidonEnforcePadding = FeatureGate{Name: "PoseidonEnforcePadding", Address: base58.MustDecodeFromString("poUdAqRXXsNmfqAZ6UqpjbeYgwBygbfQLEvWSqVhSnb")} +var FixAltBn128PairingLengthCheck = FeatureGate{Name: "FixAltBn128PairingLengthCheck", Address: base58.MustDecodeFromString("bnYzodLwmybj7e1HAe98yZrdJTd7we69eMMLgCXqKZm")} +var DeprecateRentExemptionThreshold = FeatureGate{Name: "DeprecateRentExemptionThreshold", Address: base58.MustDecodeFromString("rent6iVy6PDoViPBeJ6k5EJQrkj62h7DPyLbWGHwjrC")} var AllFeatureGates = []FeatureGate{StopTruncatingStringsInSyscalls, EnablePartitionedEpochReward, EnablePartitionedEpochRewardsSuperfeature, LastRestartSlotSysvar, Libsecp256k1FailOnBadCount, Libsecp256k1FailOnBadCount2, EnableBpfLoaderSetAuthorityCheckedIx, @@ -73,4 +78,5 @@ var AllFeatureGates = []FeatureGate{StopTruncatingStringsInSyscalls, EnableParti FullInflationVote, FullInflationEnable, FullInflationDevnetAndTestnet, PicoInflation, DisableAccountLoaderSpecialCase, EnableGetEpochStakeSyscall, ReserveMinimalCUsForBuiltinInstructions, MaskOutRentEpochInVmSerialization, RemoveAccountsExecutableFlagChecks, AccountsLtHash, RemoveAccountsDeltaHash, EnableLoaderV4, EnableSbpfV1DeploymentAndExecution, EnableSbpfV2DeploymentAndExecution, - EnableSbpfV3DeploymentAndExecution, DisableSbpfV0Execution, ReenableSbpfV0Execution, FormalizeLoadedTransactionDataSize} + EnableSbpfV3DeploymentAndExecution, DisableSbpfV0Execution, ReenableSbpfV0Execution, FormalizeLoadedTransactionDataSize, IncreaseCpiAccountInfoLimit, + StaticInstructionLimit, PoseidonEnforcePadding, FixAltBn128PairingLengthCheck, DeprecateRentExemptionThreshold} diff --git a/pkg/replay/block.go b/pkg/replay/block.go index 2f2e7f17..fe3a0735 100644 --- a/pkg/replay/block.go +++ b/pkg/replay/block.go @@ -640,8 +640,8 @@ func loadBlockAccountsAndUpdateSysvars(accountsDb *accountsdb.AccountsDb, block } func scanAndEnableFeatures(acctsDb *accountsdb.AccountsDb, slot uint64, startOfEpoch bool) (*features.Features, []*accounts.Account, []*accounts.Account) { - parentNewlyActivatedFeatureAccts := make([]*accounts.Account, 0) - newlyActivatedFeatureAccts := make([]*accounts.Account, 0) + parentAccts := make([]*accounts.Account, 0) + modifiedAccts := make([]*accounts.Account, 0) f := features.NewFeaturesDefault() @@ -651,7 +651,7 @@ func scanAndEnableFeatures(acctsDb *accountsdb.AccountsDb, slot uint64, startOfE if acct.Owner != a.FeatureAddr { continue } - parentNewlyActivatedFeatureAccts = append(parentNewlyActivatedFeatureAccts, acct.Clone()) + parentAccts = append(parentAccts, acct.Clone()) featureAcct := features.UnmarshalFeatureAcct(acct.Data) @@ -668,21 +668,48 @@ func scanAndEnableFeatures(acctsDb *accountsdb.AccountsDb, slot uint64, startOfE } acct.Data = newFeatureAcctBytes - newlyActivatedFeatureAccts = append(newlyActivatedFeatureAccts, acct) + modifiedAccts = append(modifiedAccts, acct) f.EnableFeature(featureGate, slot) } } } - if len(newlyActivatedFeatureAccts) != 0 { - err := acctsDb.StoreAccounts(newlyActivatedFeatureAccts, slot, nil) + if len(modifiedAccts) != 0 { + err := acctsDb.StoreAccounts(modifiedAccts, slot, nil) if err != nil { panic(err) } } - return f, newlyActivatedFeatureAccts, parentNewlyActivatedFeatureAccts + for _, featureAcct := range modifiedAccts { + // Handle *SIMD-0194: Deprecate rent exemption threshold* feature activation by updating the Rent sysvar. + if f.IsActive(features.DeprecateRentExemptionThreshold) && featureAcct.Key == features.DeprecateRentExemptionThreshold.Address { + var rentSysvar sealevel.SysvarRent + rentSysvar.LamportsPerUint8Year = uint64(float64(sealevel.SysvarCache.Rent.Sysvar.LamportsPerUint8Year) * sealevel.SysvarCache.Rent.Sysvar.ExemptionThreshold) + rentSysvar.ExemptionThreshold = 1.0 + rentSysvar.BurnPercent = sealevel.SysvarCache.Rent.Sysvar.BurnPercent + + rentAcct, err := acctsDb.GetAccount(slot, sealevel.SysvarRentAddr) + if err != nil { + panic(err) + } + parentAccts = append(parentAccts, rentAcct.Clone()) + + newRentSysvarBytes := rentSysvar.MustMarshal() + copy(rentAcct.Data, newRentSysvarBytes) + err = acctsDb.StoreAccounts([]*accounts.Account{rentAcct}, slot, nil) + if err != nil { + panic(err) + } + modifiedAccts = append(modifiedAccts, rentAcct) + + sealevel.SysvarCache.Rent.Sysvar = &rentSysvar + sealevel.SysvarCache.Rent.Acct = rentAcct + } + } + + return f, modifiedAccts, parentAccts } // setupInitialVoteAcctsAndStakeAccts populates the vote and stake caches at startup. diff --git a/pkg/replay/transaction.go b/pkg/replay/transaction.go index 1cb6e32e..487b54b0 100644 --- a/pkg/replay/transaction.go +++ b/pkg/replay/transaction.go @@ -46,6 +46,7 @@ var ( TxErrProgramAccountNotFound = errors.New("TxErrProgramAccountNotFound") TxErrInvalidProgramForExecution = errors.New("TxErrInvalidProgramForExecution") TxErrInvalidBlockhash = errors.New("TxErrInvalidBlockhash") + TxErrSanitizeFailure = errors.New("TxErrSanitizeFailure") ) func programIndices(tx *solana.Transaction, instrIdx int) []uint64 { @@ -317,6 +318,13 @@ func ProcessTransaction(slotCtx *sealevel.SlotCtx, sigverifyWg *sync.WaitGroup, if arena != nil { arena.Reset() } + + if slotCtx.Features.IsActive(features.StaticInstructionLimit) { + if len(tx.Message.Instructions) > maxInstrTraceCapacity { + return nil, TxErrSanitizeFailure + } + } + start := time.Now() sigverifyWg.Add(1) go verifySignatures(tx, slotCtx.Slot, sigverifyWg) diff --git a/pkg/sealevel/syscalls_cpi.go b/pkg/sealevel/syscalls_cpi.go index 5c5d7151..5f696fbb 100644 --- a/pkg/sealevel/syscalls_cpi.go +++ b/pkg/sealevel/syscalls_cpi.go @@ -18,10 +18,12 @@ import ( ) const ( - MaxSigners = 16 - MaxCpiInstructionDataLen = 10 * 1024 - MaxCpiInstructionAccounts = 255 - MaxCpiAccountInfos = 128 + MaxSigners = 16 + MaxCpiInstructionDataLen = 10 * 1024 + MaxCpiInstructionAccounts = 255 + MaxCpiAccountInfos = 128 + MaxCpiAccountInfosSimd0339 = 255 + AccountInfoByteSize = 80 ) func checkInstructionSize(execCtx *ExecutionCtx, numAccounts uint64, dataLen uint64) error { @@ -91,7 +93,14 @@ func translateInstructionC(vm sbpf.VM, addr uint64) (Instruction, error) { execCtx := executionCtx(vm) if execCtx.Features.IsActive(features.LoosenCpiSizeRestriction) { - err = execCtx.ComputeMeter.Consume(ix.DataLen / cu.CUCpiBytesPerUnit) + totalCuTranslationCost := ix.DataLen / cu.CUCpiBytesPerUnit + + if execCtx.Features.IsActive(features.IncreaseCpiAccountInfoLimit) { + acctMetaTranslationCost := safemath.SaturatingMulU64(ix.AccountsLen, AccountMetaSize) / cu.CUCpiBytesPerUnit + totalCuTranslationCost = safemath.SaturatingAddU64(totalCuTranslationCost, acctMetaTranslationCost) + } + + err = execCtx.ComputeMeter.Consume(totalCuTranslationCost) if err != nil { return Instruction{}, err } @@ -168,7 +177,14 @@ func translateInstructionRust(vm sbpf.VM, addr uint64) (Instruction, error) { execCtx := executionCtx(vm) if execCtx.Features.IsActive(features.LoosenCpiSizeRestriction) { - err = execCtx.ComputeMeter.Consume(ix.Data.Len / cu.CUCpiBytesPerUnit) + totalCuTranslationCost := ix.Data.Len / cu.CUCpiBytesPerUnit + + if execCtx.Features.IsActive(features.IncreaseCpiAccountInfoLimit) { + acctMetaTranslationCost := safemath.SaturatingMulU64(ix.Accounts.Len, AccountMetaSize) / cu.CUCpiBytesPerUnit + totalCuTranslationCost = safemath.SaturatingAddU64(totalCuTranslationCost, acctMetaTranslationCost) + } + + err = execCtx.ComputeMeter.Consume(totalCuTranslationCost) if err != nil { return Instruction{}, err } @@ -294,7 +310,9 @@ func checkAuthorizedProgram(execCtx *ExecutionCtx, programId solana.PublicKey, i func checkAccountInfos(execCtx *ExecutionCtx, numAccountInfos uint64) error { if execCtx.Features.IsActive(features.LoosenCpiSizeRestriction) { var maxAccountInfos uint64 - if execCtx.Features.IsActive(features.IncreaseTxAccountLockLimit) { + if execCtx.Features.IsActive(features.IncreaseCpiAccountInfoLimit) { + maxAccountInfos = MaxCpiAccountInfosSimd0339 + } else if execCtx.Features.IsActive(features.IncreaseTxAccountLockLimit) { maxAccountInfos = MaxCpiAccountInfos } else { maxAccountInfos = 64 @@ -330,11 +348,20 @@ func translateAccountInfosC(vm sbpf.VM, accountInfosAddr, accountInfosLen uint64 accountInfos = append(accountInfos, acctInfo) } - err = checkAccountInfos(executionCtx(vm), uint64(len(accountInfos))) + execCtx := executionCtx(vm) + err = checkAccountInfos(execCtx, uint64(len(accountInfos))) if err != nil { return nil, nil, err } + if execCtx.Features.IsActive(features.IncreaseCpiAccountInfoLimit) { + acctInfosBytes := safemath.SaturatingMulU64(uint64(len(accountInfos)), AccountInfoByteSize) + err = execCtx.ComputeMeter.Consume(acctInfosBytes / cu.CUCpiBytesPerUnit) + if err != nil { + return nil, nil, err + } + } + accountInfoKeys := make([]solana.PublicKey, 0, len(accountInfos)) for _, acctInfo := range accountInfos { keyData, err := vm.Translate(acctInfo.KeyAddr, 32, false) @@ -367,11 +394,20 @@ func translateAccountInfosRust(vm sbpf.VM, accountInfosAddr, accountInfosLen uin accountInfos = append(accountInfos, acctInfo) } - err = checkAccountInfos(executionCtx(vm), uint64(len(accountInfos))) + execCtx := executionCtx(vm) + err = checkAccountInfos(execCtx, uint64(len(accountInfos))) if err != nil { return nil, nil, err } + if execCtx.Features.IsActive(features.IncreaseCpiAccountInfoLimit) { + acctInfosBytes := safemath.SaturatingMulU64(uint64(len(accountInfos)), AccountInfoByteSize) + err = execCtx.ComputeMeter.Consume(acctInfosBytes / cu.CUCpiBytesPerUnit) + if err != nil { + return nil, nil, err + } + } + accountInfoKeys := make([]solana.PublicKey, 0, len(accountInfos)) for _, acctInfo := range accountInfos { keyData, err := vm.Translate(acctInfo.PubkeyAddr, 32, false) diff --git a/pkg/sealevel/syscalls_curve.go b/pkg/sealevel/syscalls_curve.go index 22610637..4fe8cc36 100644 --- a/pkg/sealevel/syscalls_curve.go +++ b/pkg/sealevel/syscalls_curve.go @@ -863,9 +863,14 @@ func altbn128Multiplication(input []byte, expectedLen uint64) ([]byte, error) { return resultBytes, nil } -func altbn128Pairing(input []byte) ([]byte, error) { - elementsLen := uint64(len(input)) / AltBn128PairingElementLen +func altbn128Pairing(input []byte, enforceSimd0334LenCheck bool) ([]byte, error) { + if enforceSimd0334LenCheck { + if len(input)%AltBn128PairingElementLen != 0 { + return nil, fmt.Errorf("AltBn128Error::InvalidInputData") + } + } + elementsLen := uint64(len(input)) / AltBn128PairingElementLen g1Vals := make([]*bn256.G1, 0) g2Vals := make([]*bn256.G2, 0) @@ -990,7 +995,12 @@ func SyscallAltBn128Impl(vm sbpf.VM, groupOp, inputAddr, inputLen, resultAddr ui case AltBn128Pairing: { - result, err := altbn128Pairing(inputSlice) + var enforceSimd0334LenCheck bool + if execCtx.Features.IsActive(features.FixAltBn128PairingLengthCheck) { + enforceSimd0334LenCheck = true + } + + result, err := altbn128Pairing(inputSlice, enforceSimd0334LenCheck) if err != nil { mlog.Log.Debugf("altbn128 pairing err: %s", err) return syscallSuccess(1) diff --git a/pkg/sealevel/syscalls_curve_test.go b/pkg/sealevel/syscalls_curve_test.go index a6f9817a..507d8f3a 100644 --- a/pkg/sealevel/syscalls_curve_test.go +++ b/pkg/sealevel/syscalls_curve_test.go @@ -343,7 +343,7 @@ func TestConformance_AltBn128_Pairing1(t *testing.T) { knownCorrect, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000001") assert.NoError(t, err) - result, err := altbn128Pairing(inputBytes) + result, err := altbn128Pairing(inputBytes, false) assert.NoError(t, err) assert.Equal(t, knownCorrect, result) @@ -390,7 +390,7 @@ func TestConformance_AltBn128_Pairing_Multiple(t *testing.T) { inputBytes, err := hex.DecodeString(inStr) assert.NoError(t, err) - result, err := altbn128Pairing(inputBytes) + result, err := altbn128Pairing(inputBytes, false) assert.NoError(t, err) knownCorrectBytes, err := hex.DecodeString(resultStrs[idx]) diff --git a/pkg/sealevel/syscalls_hash.go b/pkg/sealevel/syscalls_hash.go index a73da2a1..b73a1cd0 100644 --- a/pkg/sealevel/syscalls_hash.go +++ b/pkg/sealevel/syscalls_hash.go @@ -8,6 +8,7 @@ import ( //"github.com/Overclock-Validator/mithril/pkg/mlog" "github.com/Overclock-Validator/mithril/pkg/cu" + "github.com/Overclock-Validator/mithril/pkg/features" "github.com/Overclock-Validator/mithril/pkg/sbpf" "github.com/ethereum/go-ethereum/crypto" "github.com/iden3/go-iden3-crypto/poseidon" @@ -268,14 +269,18 @@ func SwapEndianness(xs []byte) []byte { return ys } -func PoseidonHash(input [][]byte, isBigEndian bool) ([]byte, error) { - //mlog.Log.Debugf("PoseidonHash: len(input) = %d, isBigEndian = %t", len(input), isBigEndian) +func PoseidonHash(input [][]byte, isBigEndian bool, enforceSimd0359 bool) ([]byte, error) { inputBigInts := make([]*big.Int, 0, len(input)) for _, inputSlice := range input { if len(inputSlice) > 32 { return nil, fmt.Errorf("input too long") } + + if enforceSimd0359 && len(inputSlice) < 32 { + return nil, fmt.Errorf("input too short") + } + var bigInt *big.Int if isBigEndian { bigInt = new(big.Int).SetBytes(inputSlice) @@ -366,7 +371,8 @@ func SyscallPoseidonImpl(vm sbpf.VM, parameters, endianness, valsAddr, valsLen, isBigEndian := endianness == 0 - hash, err := PoseidonHash(inputs, isBigEndian) + enforceSimd0359 := execCtx.Features.IsActive(features.PoseidonEnforcePadding) + hash, err := PoseidonHash(inputs, isBigEndian, enforceSimd0359) if err != nil { return syscallSuccess(1) } diff --git a/pkg/sealevel/sysvar_rent.go b/pkg/sealevel/sysvar_rent.go index bf656cd0..79f11ab1 100644 --- a/pkg/sealevel/sysvar_rent.go +++ b/pkg/sealevel/sysvar_rent.go @@ -54,6 +54,28 @@ func (sr *SysvarRent) MustUnmarshalWithDecoder(decoder *bin.Decoder) { } } +func (sr *SysvarRent) MustMarshal() []byte { + data := new(bytes.Buffer) + enc := bin.NewBinEncoder(data) + + err := enc.WriteUint64(sr.LamportsPerUint8Year, bin.LE) + if err != nil { + panic(fmt.Sprintf("failed to marshal LamportsPerUint8Year in sysvar rent: %s", err)) + } + + err = enc.WriteFloat64(sr.ExemptionThreshold, bin.LE) + if err != nil { + panic(fmt.Sprintf("failed to marshal ExemptionThreshold in sysvar rent: %s", err)) + } + + err = enc.WriteByte(sr.BurnPercent) + if err != nil { + panic(fmt.Sprintf("failed to marshal BurnPercent in sysvar rent: %s", err)) + } + + return data.Bytes() +} + func (sr *SysvarRent) MinimumBalance(dataLen uint64) uint64 { dataLenWithOverhead := safemath.SaturatingAddU64(dataLen, rentAccountStorageOverhead) lamportsPerYear := safemath.SaturatingMulU64(dataLenWithOverhead, sr.LamportsPerUint8Year)