mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
Merge branch 'master' into enhance-simulation-api
This commit is contained in:
commit
fa245e2a50
262 changed files with 64320 additions and 10171 deletions
|
|
@ -77,18 +77,18 @@ func (arguments Arguments) isTuple() bool {
|
|||
}
|
||||
|
||||
// Unpack performs the operation hexdata -> Go format.
|
||||
func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
||||
func (arguments Arguments) Unpack(data []byte) ([]any, error) {
|
||||
if len(data) == 0 {
|
||||
if len(arguments.NonIndexed()) != 0 {
|
||||
return nil, errors.New("abi: attempting to unmarshal an empty string while arguments are expected")
|
||||
}
|
||||
return make([]interface{}, 0), nil
|
||||
return make([]any, 0), nil
|
||||
}
|
||||
return arguments.UnpackValues(data)
|
||||
}
|
||||
|
||||
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value.
|
||||
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
||||
func (arguments Arguments) UnpackIntoMap(v map[string]any, data []byte) error {
|
||||
// Make sure map is not nil
|
||||
if v == nil {
|
||||
return errors.New("abi: cannot unpack into a nil map")
|
||||
|
|
@ -110,7 +110,7 @@ func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte)
|
|||
}
|
||||
|
||||
// Copy performs the operation go format -> provided struct.
|
||||
func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
|
||||
func (arguments Arguments) Copy(v any, values []any) error {
|
||||
// make sure the passed value is arguments pointer
|
||||
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
||||
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
||||
|
|
@ -128,7 +128,7 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
|
|||
}
|
||||
|
||||
// copyAtomic copies ( hexdata -> go ) a single value
|
||||
func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error {
|
||||
func (arguments Arguments) copyAtomic(v any, marshalledValues any) error {
|
||||
dst := reflect.ValueOf(v).Elem()
|
||||
src := reflect.ValueOf(marshalledValues)
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{
|
|||
}
|
||||
|
||||
// copyTuple copies a batch of values from marshalledValues to v.
|
||||
func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface{}) error {
|
||||
func (arguments Arguments) copyTuple(v any, marshalledValues []any) error {
|
||||
value := reflect.ValueOf(v).Elem()
|
||||
nonIndexedArgs := arguments.NonIndexed()
|
||||
|
||||
|
|
@ -181,11 +181,17 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
|||
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
|
||||
// without supplying a struct to unpack into. Instead, this method returns a list containing the
|
||||
// values. An atomic argument will be a list with one element.
|
||||
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
||||
nonIndexedArgs := arguments.NonIndexed()
|
||||
retval := make([]interface{}, 0, len(nonIndexedArgs))
|
||||
virtualArgs := 0
|
||||
for index, arg := range nonIndexedArgs {
|
||||
func (arguments Arguments) UnpackValues(data []byte) ([]any, error) {
|
||||
var (
|
||||
retval = make([]any, 0)
|
||||
virtualArgs = 0
|
||||
index = 0
|
||||
)
|
||||
|
||||
for _, arg := range arguments {
|
||||
if arg.Indexed {
|
||||
continue
|
||||
}
|
||||
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -208,18 +214,19 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
|||
virtualArgs += getTypeSize(arg.Type)/32 - 1
|
||||
}
|
||||
retval = append(retval, marshalledValue)
|
||||
index++
|
||||
}
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
// PackValues performs the operation Go format -> Hexdata.
|
||||
// It is the semantic opposite of UnpackValues.
|
||||
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
|
||||
func (arguments Arguments) PackValues(args []any) ([]byte, error) {
|
||||
return arguments.Pack(args...)
|
||||
}
|
||||
|
||||
// Pack performs the operation Go format -> Hexdata.
|
||||
func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||
func (arguments Arguments) Pack(args ...any) ([]byte, error) {
|
||||
// Make sure arguments match up and pack them
|
||||
abiArgs := arguments
|
||||
if len(args) != len(abiArgs) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,46 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func BenchmarkUnpack(b *testing.B) {
|
||||
testCases := []struct {
|
||||
def string
|
||||
packed string
|
||||
}{
|
||||
{
|
||||
def: `[{"type": "uint32"}]`,
|
||||
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint32[]"}]`,
|
||||
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||
},
|
||||
}
|
||||
for i, test := range testCases {
|
||||
b.Run(strconv.Itoa(i), func(b *testing.B) {
|
||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||
abi, err := JSON(strings.NewReader(def))
|
||||
if err != nil {
|
||||
b.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||
}
|
||||
encb, err := hex.DecodeString(test.packed)
|
||||
if err != nil {
|
||||
b.Fatalf("invalid hex %s: %v", test.packed, err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
var result any
|
||||
for range b.N {
|
||||
result, _ = abi.Unpack("method", encb)
|
||||
}
|
||||
_ = result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnpack tests the general pack/unpack tests in packing_test.go
|
||||
func TestUnpack(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
|
|
|||
|
|
@ -156,8 +156,9 @@ func (s *beaconBlockSync) updateEventFeed() {
|
|||
return
|
||||
}
|
||||
s.chainHeadFeed.Send(types.ChainHeadEvent{
|
||||
BeaconHead: optimistic.Attested.Header,
|
||||
Block: execBlock,
|
||||
Finalized: finalizedHash,
|
||||
BeaconHead: optimistic.Attested.Header,
|
||||
Block: execBlock,
|
||||
ExecRequests: headBlock.ExecutionRequestsList(),
|
||||
Finalized: finalizedHash,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,6 +100,11 @@ func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent)
|
|||
params = []any{execData}
|
||||
)
|
||||
switch fork {
|
||||
case "electra":
|
||||
method = "engine_newPayloadV4"
|
||||
parentBeaconRoot := event.BeaconHead.ParentRoot
|
||||
blobHashes := collectBlobHashes(event.Block)
|
||||
params = append(params, blobHashes, parentBeaconRoot, event.ExecRequests)
|
||||
case "deneb":
|
||||
method = "engine_newPayloadV3"
|
||||
parentBeaconRoot := event.BeaconHead.ParentRoot
|
||||
|
|
@ -135,7 +140,7 @@ func (ec *engineClient) callForkchoiceUpdated(fork string, event types.ChainHead
|
|||
|
||||
var method string
|
||||
switch fork {
|
||||
case "deneb":
|
||||
case "deneb", "electra":
|
||||
method = "engine_forkchoiceUpdatedV3"
|
||||
case "capella":
|
||||
method = "engine_forkchoiceUpdatedV2"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ var (
|
|||
)
|
||||
|
||||
type CommitteeUpdate struct {
|
||||
Version string
|
||||
Update types.LightClientUpdate
|
||||
NextSyncCommittee types.SerializedSyncCommittee
|
||||
}
|
||||
|
|
@ -81,9 +80,9 @@ func (u *CommitteeUpdate) UnmarshalJSON(input []byte) error {
|
|||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
u.Version = dec.Version
|
||||
u.NextSyncCommittee = dec.Data.NextSyncCommittee
|
||||
u.Update = types.LightClientUpdate{
|
||||
Version: dec.Version,
|
||||
AttestedHeader: types.SignedHeader{
|
||||
Header: dec.Data.Header.Beacon,
|
||||
Signature: dec.Data.SyncAggregate,
|
||||
|
|
@ -206,7 +205,7 @@ func (api *BeaconLightApi) GetOptimisticUpdate() (types.OptimisticUpdate, error)
|
|||
|
||||
func decodeOptimisticUpdate(enc []byte) (types.OptimisticUpdate, error) {
|
||||
var data struct {
|
||||
Version string
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Attested jsonHeaderWithExecProof `json:"attested_header"`
|
||||
Aggregate types.SyncAggregate `json:"sync_aggregate"`
|
||||
|
|
@ -259,7 +258,7 @@ func (api *BeaconLightApi) GetFinalityUpdate() (types.FinalityUpdate, error) {
|
|||
|
||||
func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) {
|
||||
var data struct {
|
||||
Version string
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Attested jsonHeaderWithExecProof `json:"attested_header"`
|
||||
Finalized jsonHeaderWithExecProof `json:"finalized_header"`
|
||||
|
|
@ -289,6 +288,7 @@ func decodeFinalityUpdate(enc []byte) (types.FinalityUpdate, error) {
|
|||
}
|
||||
|
||||
return types.FinalityUpdate{
|
||||
Version: data.Version,
|
||||
Attested: types.HeaderWithExecProof{
|
||||
Header: data.Data.Attested.Beacon,
|
||||
PayloadHeader: attestedExecHeader,
|
||||
|
|
@ -355,7 +355,8 @@ func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types
|
|||
// See data structure definition here:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientbootstrap
|
||||
type bootstrapData struct {
|
||||
Data struct {
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Header jsonBeaconHeader `json:"header"`
|
||||
Committee *types.SerializedSyncCommittee `json:"current_sync_committee"`
|
||||
CommitteeBranch merkle.Values `json:"current_sync_committee_branch"`
|
||||
|
|
@ -374,6 +375,7 @@ func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types
|
|||
return nil, fmt.Errorf("invalid checkpoint block header, have %v want %v", header.Hash(), checkpointHash)
|
||||
}
|
||||
checkpoint := &types.BootstrapData{
|
||||
Version: data.Version,
|
||||
Header: header,
|
||||
CommitteeBranch: data.Data.CommitteeBranch,
|
||||
CommitteeRoot: data.Data.Committee.Root(),
|
||||
|
|
@ -395,7 +397,7 @@ func (api *BeaconLightApi) GetBeaconBlock(blockRoot common.Hash) (*types.BeaconB
|
|||
}
|
||||
|
||||
var beaconBlockMessage struct {
|
||||
Version string
|
||||
Version string `json:"version"`
|
||||
Data struct {
|
||||
Message json.RawMessage `json:"message"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ func GenerateTestUpdate(config *params.ChainConfig, period uint64, committee, ne
|
|||
var attestedHeader types.Header
|
||||
if finalizedHeader {
|
||||
update.FinalizedHeader = new(types.Header)
|
||||
*update.FinalizedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+100, params.StateIndexNextSyncCommittee, merkle.Value(update.NextSyncCommitteeRoot))
|
||||
attestedHeader, update.FinalityBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexFinalBlock, merkle.Value(update.FinalizedHeader.Hash()))
|
||||
*update.FinalizedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+100, params.StateIndexNextSyncCommittee(""), merkle.Value(update.NextSyncCommitteeRoot))
|
||||
attestedHeader, update.FinalityBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexFinalBlock(""), merkle.Value(update.FinalizedHeader.Hash()))
|
||||
} else {
|
||||
attestedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+2000, params.StateIndexNextSyncCommittee, merkle.Value(update.NextSyncCommitteeRoot))
|
||||
attestedHeader, update.NextSyncCommitteeBranch = makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+2000, params.StateIndexNextSyncCommittee(""), merkle.Value(update.NextSyncCommitteeRoot))
|
||||
}
|
||||
update.AttestedHeader = GenerateTestSignedHeader(attestedHeader, config, committee, attestedHeader.Slot+1, signerCount)
|
||||
return update
|
||||
|
|
@ -63,7 +63,7 @@ func GenerateTestSignedHeader(header types.Header, config *params.ChainConfig, c
|
|||
}
|
||||
|
||||
func GenerateTestCheckpoint(period uint64, committee *types.SerializedSyncCommittee) *types.BootstrapData {
|
||||
header, branch := makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexSyncCommittee, merkle.Value(committee.Root()))
|
||||
header, branch := makeTestHeaderWithMerkleProof(types.SyncPeriodStart(period)+200, params.StateIndexSyncCommittee(""), merkle.Value(committee.Root()))
|
||||
return &types.BootstrapData{
|
||||
Header: header,
|
||||
Committee: committee,
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ var (
|
|||
AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}).
|
||||
AddFork("BELLATRIX", 100, []byte{144, 0, 0, 113}).
|
||||
AddFork("CAPELLA", 56832, []byte{144, 0, 0, 114}).
|
||||
AddFork("DENEB", 132608, []byte{144, 0, 0, 115})
|
||||
AddFork("DENEB", 132608, []byte{144, 0, 0, 115}).
|
||||
AddFork("ELECTRA", 222464, []byte{144, 0, 0, 116})
|
||||
|
||||
HoleskyLightConfig = (&ChainConfig{
|
||||
GenesisValidatorsRoot: common.HexToHash("0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1"),
|
||||
|
|
@ -52,5 +53,6 @@ var (
|
|||
AddFork("ALTAIR", 0, []byte{2, 1, 112, 0}).
|
||||
AddFork("BELLATRIX", 0, []byte{3, 1, 112, 0}).
|
||||
AddFork("CAPELLA", 256, []byte{4, 1, 112, 0}).
|
||||
AddFork("DENEB", 29696, []byte{5, 1, 112, 0})
|
||||
AddFork("DENEB", 29696, []byte{5, 1, 112, 0}).
|
||||
AddFork("ELECTRA", 115968, []byte{6, 1, 112, 0})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,18 +29,46 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
StateIndexGenesisTime = 32
|
||||
StateIndexGenesisValidators = 33
|
||||
StateIndexForkVersion = 141
|
||||
StateIndexLatestHeader = 36
|
||||
StateIndexBlockRoots = 37
|
||||
StateIndexStateRoots = 38
|
||||
StateIndexHistoricRoots = 39
|
||||
StateIndexFinalBlock = 105
|
||||
StateIndexSyncCommittee = 54
|
||||
StateIndexNextSyncCommittee = 55
|
||||
StateIndexExecPayload = 56
|
||||
StateIndexExecHead = 908
|
||||
StateIndexGenesisTime = 32
|
||||
StateIndexGenesisValidators = 33
|
||||
StateIndexForkVersion = 141
|
||||
StateIndexLatestHeader = 36
|
||||
StateIndexBlockRoots = 37
|
||||
StateIndexStateRoots = 38
|
||||
StateIndexHistoricRoots = 39
|
||||
StateIndexFinalBlockOld = 105
|
||||
StateIndexFinalBlockElectra = 169
|
||||
StateIndexSyncCommitteeOld = 54
|
||||
StateIndexSyncCommitteeElectra = 86
|
||||
StateIndexNextSyncCommitteeOld = 55
|
||||
StateIndexNextSyncCommitteeElectra = 87
|
||||
StateIndexExecPayload = 56
|
||||
StateIndexExecHead = 908
|
||||
|
||||
BodyIndexExecPayload = 25
|
||||
)
|
||||
|
||||
func StateIndexFinalBlock(forkName string) uint64 {
|
||||
switch forkName {
|
||||
case "bellatrix", "capella", "deneb":
|
||||
return StateIndexFinalBlockOld
|
||||
default:
|
||||
return StateIndexFinalBlockElectra
|
||||
}
|
||||
}
|
||||
func StateIndexSyncCommittee(forkName string) uint64 {
|
||||
switch forkName {
|
||||
case "bellatrix", "capella", "deneb":
|
||||
return StateIndexSyncCommitteeOld
|
||||
default:
|
||||
return StateIndexSyncCommitteeElectra
|
||||
}
|
||||
}
|
||||
func StateIndexNextSyncCommittee(forkName string) uint64 {
|
||||
switch forkName {
|
||||
case "bellatrix", "capella", "deneb":
|
||||
return StateIndexNextSyncCommitteeOld
|
||||
default:
|
||||
return StateIndexNextSyncCommitteeElectra
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,16 +17,21 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/protolambda/zrnt/eth2/beacon/capella"
|
||||
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
|
||||
"github.com/protolambda/zrnt/eth2/beacon/deneb"
|
||||
"github.com/protolambda/zrnt/eth2/configs"
|
||||
"github.com/protolambda/ztyp/codec"
|
||||
"github.com/protolambda/ztyp/tree"
|
||||
|
||||
// beacon forks
|
||||
"github.com/protolambda/zrnt/eth2/beacon/capella"
|
||||
"github.com/protolambda/zrnt/eth2/beacon/deneb"
|
||||
"github.com/protolambda/zrnt/eth2/beacon/electra"
|
||||
)
|
||||
|
||||
type blockObject interface {
|
||||
|
|
@ -43,10 +48,12 @@ type BeaconBlock struct {
|
|||
func BlockFromJSON(forkName string, data []byte) (*BeaconBlock, error) {
|
||||
var obj blockObject
|
||||
switch forkName {
|
||||
case "deneb":
|
||||
obj = new(deneb.BeaconBlock)
|
||||
case "capella":
|
||||
obj = new(capella.BeaconBlock)
|
||||
case "deneb":
|
||||
obj = new(deneb.BeaconBlock)
|
||||
case "electra":
|
||||
obj = new(electra.BeaconBlock)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported fork: %s", forkName)
|
||||
}
|
||||
|
|
@ -63,6 +70,8 @@ func NewBeaconBlock(obj blockObject) *BeaconBlock {
|
|||
return &BeaconBlock{obj}
|
||||
case *deneb.BeaconBlock:
|
||||
return &BeaconBlock{obj}
|
||||
case *electra.BeaconBlock:
|
||||
return &BeaconBlock{obj}
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported block type %T", obj))
|
||||
}
|
||||
|
|
@ -75,6 +84,8 @@ func (b *BeaconBlock) Slot() uint64 {
|
|||
return uint64(obj.Slot)
|
||||
case *deneb.BeaconBlock:
|
||||
return uint64(obj.Slot)
|
||||
case *electra.BeaconBlock:
|
||||
return uint64(obj.Slot)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
|
||||
}
|
||||
|
|
@ -84,9 +95,12 @@ func (b *BeaconBlock) Slot() uint64 {
|
|||
func (b *BeaconBlock) ExecutionPayload() (*types.Block, error) {
|
||||
switch obj := b.blockObj.(type) {
|
||||
case *capella.BeaconBlock:
|
||||
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot)
|
||||
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot, nil)
|
||||
case *deneb.BeaconBlock:
|
||||
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot)
|
||||
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot, nil)
|
||||
case *electra.BeaconBlock:
|
||||
requests := b.ExecutionRequestsList()
|
||||
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot, requests)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
|
||||
}
|
||||
|
|
@ -99,6 +113,8 @@ func (b *BeaconBlock) Header() Header {
|
|||
return headerFromZRNT(obj.Header(configs.Mainnet))
|
||||
case *deneb.BeaconBlock:
|
||||
return headerFromZRNT(obj.Header(configs.Mainnet))
|
||||
case *electra.BeaconBlock:
|
||||
return headerFromZRNT(obj.Header(configs.Mainnet))
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
|
||||
}
|
||||
|
|
@ -108,3 +124,38 @@ func (b *BeaconBlock) Header() Header {
|
|||
func (b *BeaconBlock) Root() common.Hash {
|
||||
return common.Hash(b.blockObj.HashTreeRoot(configs.Mainnet, tree.GetHashFn()))
|
||||
}
|
||||
|
||||
// ExecutionRequestsList returns the execution layer requests of the block.
|
||||
func (b *BeaconBlock) ExecutionRequestsList() [][]byte {
|
||||
switch obj := b.blockObj.(type) {
|
||||
case *capella.BeaconBlock, *deneb.BeaconBlock:
|
||||
return nil
|
||||
case *electra.BeaconBlock:
|
||||
r := obj.Body.ExecutionRequests
|
||||
return marshalRequests(configs.Mainnet,
|
||||
&r.Deposits,
|
||||
&r.Withdrawals,
|
||||
&r.Consolidations,
|
||||
)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported block type %T", b.blockObj))
|
||||
}
|
||||
}
|
||||
|
||||
func marshalRequests(spec *zrntcommon.Spec, items ...zrntcommon.SpecObj) (list [][]byte) {
|
||||
var buf bytes.Buffer
|
||||
list = [][]byte{}
|
||||
for typ, data := range items {
|
||||
buf.Reset()
|
||||
buf.WriteByte(byte(typ))
|
||||
w := codec.NewEncodingWriter(&buf)
|
||||
if err := data.Serialize(spec, w); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if buf.Len() == 1 {
|
||||
continue // skip empty requests
|
||||
}
|
||||
list = append(list, bytes.Clone(buf.Bytes()))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,27 @@ func TestBlockFromJSON(t *testing.T) {
|
|||
wantBlockHash common.Hash
|
||||
}
|
||||
tests := []blocktest{
|
||||
{
|
||||
file: "block_electra_withdrawals.json",
|
||||
version: "electra",
|
||||
wantSlot: 151850,
|
||||
wantBlockNumber: 141654,
|
||||
wantBlockHash: common.HexToHash("0xf6730485a38be5ada3e110990a2c7adaabd2e8d4a49782134f1a8bfbc246a5d7"),
|
||||
},
|
||||
{
|
||||
file: "block_electra_deposits.json",
|
||||
version: "electra",
|
||||
wantSlot: 151016,
|
||||
wantBlockNumber: 140858,
|
||||
wantBlockHash: common.HexToHash("0x1f2637170986346c7993d5adbadbebbf4c9ed89c6a4d2dff653db99c8c168076"),
|
||||
},
|
||||
{
|
||||
file: "block_electra_consolidations.json",
|
||||
version: "electra",
|
||||
wantSlot: 151717,
|
||||
wantBlockNumber: 141529,
|
||||
wantBlockHash: common.HexToHash("0xc8807f7a1f96b0a073ff27065776dd21eff6b7e64079c60bffd33f690efbb330"),
|
||||
},
|
||||
{
|
||||
file: "block_deneb.json",
|
||||
version: "deneb",
|
||||
|
|
|
|||
|
|
@ -22,10 +22,12 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/beacon/merkle"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/protolambda/zrnt/eth2/beacon/capella"
|
||||
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
|
||||
"github.com/protolambda/zrnt/eth2/beacon/deneb"
|
||||
"github.com/protolambda/ztyp/tree"
|
||||
|
||||
// beacon chain forks
|
||||
"github.com/protolambda/zrnt/eth2/beacon/capella"
|
||||
"github.com/protolambda/zrnt/eth2/beacon/deneb"
|
||||
)
|
||||
|
||||
type headerObject interface {
|
||||
|
|
@ -43,7 +45,7 @@ func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, er
|
|||
switch forkName {
|
||||
case "capella":
|
||||
obj = new(capella.ExecutionPayloadHeader)
|
||||
case "deneb":
|
||||
case "deneb", "electra": // note: the payload type was not changed in electra
|
||||
obj = new(deneb.ExecutionPayloadHeader)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported fork: %s", forkName)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ type payloadType interface {
|
|||
}
|
||||
|
||||
// convertPayload converts a beacon chain execution payload to types.Block.
|
||||
func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root) (*types.Block, error) {
|
||||
func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root, requests [][]byte) (*types.Block, error) {
|
||||
var (
|
||||
header types.Header
|
||||
transactions []*types.Transaction
|
||||
|
|
@ -62,7 +62,10 @@ func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root) (*typ
|
|||
default:
|
||||
panic("unsupported block type")
|
||||
}
|
||||
|
||||
if requests != nil {
|
||||
reqHash := types.CalcRequestsHash(requests)
|
||||
header.RequestsHash = &reqHash
|
||||
}
|
||||
block := types.NewBlockWithHeader(&header).WithBody(types.Body{Transactions: transactions, Withdrawals: withdrawals})
|
||||
if hash := block.Hash(); hash != expectedHash {
|
||||
return nil, fmt.Errorf("sanity check failed, payload hash does not match (expected %x, got %x)", expectedHash, hash)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ type HeadInfo struct {
|
|||
// together with a proof through a beacon header and corresponding state.
|
||||
// Note: BootstrapData is fetched from a server based on a known checkpoint hash.
|
||||
type BootstrapData struct {
|
||||
Version string
|
||||
Header Header
|
||||
CommitteeRoot common.Hash
|
||||
Committee *SerializedSyncCommittee `rlp:"-"`
|
||||
|
|
@ -47,7 +48,7 @@ func (c *BootstrapData) Validate() error {
|
|||
if c.CommitteeRoot != c.Committee.Root() {
|
||||
return errors.New("wrong committee root")
|
||||
}
|
||||
return merkle.VerifyProof(c.Header.StateRoot, params.StateIndexSyncCommittee, c.CommitteeBranch, merkle.Value(c.CommitteeRoot))
|
||||
return merkle.VerifyProof(c.Header.StateRoot, params.StateIndexSyncCommittee(c.Version), c.CommitteeBranch, merkle.Value(c.CommitteeRoot))
|
||||
}
|
||||
|
||||
// LightClientUpdate is a proof of the next sync committee root based on a header
|
||||
|
|
@ -59,6 +60,7 @@ func (c *BootstrapData) Validate() error {
|
|||
// See data structure definition here:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientupdate
|
||||
type LightClientUpdate struct {
|
||||
Version string
|
||||
AttestedHeader SignedHeader // Arbitrary header out of the period signed by the sync committee
|
||||
NextSyncCommitteeRoot common.Hash // Sync committee of the next period advertised in the current one
|
||||
NextSyncCommitteeBranch merkle.Values // Proof for the next period's sync committee
|
||||
|
|
@ -79,11 +81,11 @@ func (update *LightClientUpdate) Validate() error {
|
|||
if update.FinalizedHeader.SyncPeriod() != period {
|
||||
return errors.New("finalized header is from different period")
|
||||
}
|
||||
if err := merkle.VerifyProof(update.AttestedHeader.Header.StateRoot, params.StateIndexFinalBlock, update.FinalityBranch, merkle.Value(update.FinalizedHeader.Hash())); err != nil {
|
||||
if err := merkle.VerifyProof(update.AttestedHeader.Header.StateRoot, params.StateIndexFinalBlock(update.Version), update.FinalityBranch, merkle.Value(update.FinalizedHeader.Hash())); err != nil {
|
||||
return fmt.Errorf("invalid finalized header proof: %w", err)
|
||||
}
|
||||
}
|
||||
if err := merkle.VerifyProof(update.AttestedHeader.Header.StateRoot, params.StateIndexNextSyncCommittee, update.NextSyncCommitteeBranch, merkle.Value(update.NextSyncCommitteeRoot)); err != nil {
|
||||
if err := merkle.VerifyProof(update.AttestedHeader.Header.StateRoot, params.StateIndexNextSyncCommittee(update.Version), update.NextSyncCommitteeBranch, merkle.Value(update.NextSyncCommitteeRoot)); err != nil {
|
||||
return fmt.Errorf("invalid next sync committee proof: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
|
@ -194,6 +196,7 @@ func (u *OptimisticUpdate) Validate() error {
|
|||
// See data structure definition here:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientfinalityupdate
|
||||
type FinalityUpdate struct {
|
||||
Version string
|
||||
Attested, Finalized HeaderWithExecProof
|
||||
FinalityBranch merkle.Values
|
||||
// Sync committee BLS signature aggregate
|
||||
|
|
@ -223,14 +226,15 @@ func (u *FinalityUpdate) Validate() error {
|
|||
if err := u.Finalized.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
return merkle.VerifyProof(u.Attested.StateRoot, params.StateIndexFinalBlock, u.FinalityBranch, merkle.Value(u.Finalized.Hash()))
|
||||
return merkle.VerifyProof(u.Attested.StateRoot, params.StateIndexFinalBlock(u.Version), u.FinalityBranch, merkle.Value(u.Finalized.Hash()))
|
||||
}
|
||||
|
||||
// ChainHeadEvent returns an authenticated execution payload associated with the
|
||||
// latest accepted head of the beacon chain, along with the hash of the latest
|
||||
// finalized execution block.
|
||||
type ChainHeadEvent struct {
|
||||
BeaconHead Header
|
||||
Block *ctypes.Block
|
||||
Finalized common.Hash
|
||||
BeaconHead Header
|
||||
Block *ctypes.Block
|
||||
ExecRequests [][]byte // execution layer requests (added in Electra)
|
||||
Finalized common.Hash // latest finalized block hash
|
||||
}
|
||||
|
|
|
|||
194
beacon/types/testdata/block_electra_consolidations.json
vendored
Normal file
194
beacon/types/testdata/block_electra_consolidations.json
vendored
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
{
|
||||
"slot": "151717",
|
||||
"proposer_index": "20165",
|
||||
"parent_root": "0x0b968237e4cd877e1b5f146da849d5a228cba9b9020c10095cdf177ff0cbdca9",
|
||||
"state_root": "0xc99af23f66c2b3c964b5301f314ffae1add20b1d88b3a9d8a95c72110b06b2fd",
|
||||
"body": {
|
||||
"randao_reveal": "0x81126bf1b4491dd0906c2d82615b34b8d3f779096bcf0a5f0ddda2a6da2b6c1858d3359a0b70cf66af69fc649bf6683e0520b3b14edcde3d6bd75b394d0812c5fd232350e6e3b5e9e6b262219c788ca2e74fa15a1e02e034b091d6c694aaeccd",
|
||||
"eth1_data": {
|
||||
"deposit_root": "0x8654042dec994aa6fc1a36fd0f4ebb2a49d1b27ada9460b045e2ea2a15718cc2",
|
||||
"deposit_count": "10010",
|
||||
"block_hash": "0xb358182abdc706e4a7ca709816043ebb23f3d93e943ca0becdfb9202abecd2d3"
|
||||
},
|
||||
"graffiti": "0x6c69676874686f7573652d676574682d33000000000000000000000000000000",
|
||||
"proposer_slashings": [],
|
||||
"attester_slashings": [],
|
||||
"attestations": [
|
||||
{
|
||||
"aggregation_bits": "0xfefffffffffff7ffbfefffdff7ff77ffffffffffdfe7ffffffffbfffffff7fbfffeffffffffffffffffffffffffffffffbffffffffffeffffeffffffffffeffffffbfffbfffffffffffffffffebfffffffff7ffffffdffffffffffffffffffffffffbfff7fffffffffffffffffffffefffffffffff7ffffffffeffffffffffffff7fffeffffffdfffffffff7ff7fffff7bffffffffbfffffffffffffdfff7ffffffffeffffffffffffffffffffdffffbffffffffffffff7ff6fffffffffffffffffffffffffffffefffff7fffefffeffffdffbffffff3fffdfdfbfffffffffffffffffdffdfffffffffffffffffffffff7ffffffffeffd7ffffffffffffffffffbff7ffeffffffffffbfffdfffffffffffffffffffff7fff0f",
|
||||
"data": {
|
||||
"slot": "151716",
|
||||
"index": "0",
|
||||
"beacon_block_root": "0x0b968237e4cd877e1b5f146da849d5a228cba9b9020c10095cdf177ff0cbdca9",
|
||||
"source": {
|
||||
"epoch": "4740",
|
||||
"root": "0x75198e06e7a0fe301a524212e6376d2222e421fb3cfd1ef0dcb637bf6d20deac"
|
||||
},
|
||||
"target": {
|
||||
"epoch": "4741",
|
||||
"root": "0x8bb6fe4f7ea104312914c88ac84534e4da2ff8207790060f4ba903aaf231678e"
|
||||
}
|
||||
},
|
||||
"signature": "0x9138d56149037ae150d8c544c8b609a250fe5e3d66cb36e5d2dd618a642dd79bfa45ec1da014f24872410ddf9ecba28907bec2ea0e5581694871746fbeba9c4e5a553725b03feba05272a11a327b02bae60da8144d333eb019568cf64eb8a43d",
|
||||
"committee_bits": "0xffff010000000000"
|
||||
},
|
||||
{
|
||||
"aggregation_bits": "0xbdfffffffdffffffffffffffffffefffffffffffffffffffffffbffffffffffffffffffffffffffbffffdfffffffbfffffffffffffffffff7efffffffffffffffffffdffffffffff7ffffff3fffffffffffffbfdffff7ff7ffffffffffffffff7fffdffffffffffffdfffffffffffffffbffffffffffffffffbfffffffffdfffffffffffffffffffffffffffeffeffffffffffefffffffffffffffffffffdfdffefffffffffffffdfffffffffffffffffeffefffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffefffffffdfffffffffffdffffffffffbffffffffffffffeffffffffffd9ffffdfffffffffffffffffffffffffbfffdfffffffffffffffffffffffffdfffffff07",
|
||||
"data": {
|
||||
"slot": "151715",
|
||||
"index": "0",
|
||||
"beacon_block_root": "0x3fe0c30d4be1a2e83c5109a35fd811fe20f783952199e0d10b485985510edafa",
|
||||
"source": {
|
||||
"epoch": "4740",
|
||||
"root": "0x75198e06e7a0fe301a524212e6376d2222e421fb3cfd1ef0dcb637bf6d20deac"
|
||||
},
|
||||
"target": {
|
||||
"epoch": "4741",
|
||||
"root": "0x8bb6fe4f7ea104312914c88ac84534e4da2ff8207790060f4ba903aaf231678e"
|
||||
}
|
||||
},
|
||||
"signature": "0xb1653ef5666f3ec1ef9ba39036cc6a361b96b3b82ae9ccd6aa7c83c63e358843c054de6022915421360c04ab343b4cee104f2d5d357454f0ca5d86bc24f2524520f27621624c3e78b0e0915a59cd3ba583eb380dbf984265ffe720eef9201b04",
|
||||
"committee_bits": "0xffff010000000000"
|
||||
}
|
||||
],
|
||||
"deposits": [],
|
||||
"voluntary_exits": [],
|
||||
"sync_aggregate": {
|
||||
"sync_committee_bits": "0xf7f7fffffffbff6ffffffbfffffffdffffffeff7ffdf7fffffffffffffffffffffffffffffffffffffffffffef777dffffffff7ffeffffebffbf9ffffbffffff",
|
||||
"sync_committee_signature": "0x99a90d385ef2a1c8d7c8d96bdfc9aa03065532f4415709efbade179de42a8b1c9b736a5d08377c3f9d3a9c220dcd95c61986ba9cc0b735b2c23ac6b8128fc5851e4057c7c469b94ae25c2d0c560793b12021dfb29d34d78e085b8cb8a952f4ba"
|
||||
},
|
||||
"execution_payload": {
|
||||
"parent_hash": "0xdeab769383aabae234218751c24b5c286c54b7e8545308767217c48ee8a66a03",
|
||||
"fee_recipient": "0xb9e79d19f651a941757b35830232e7efc77e1c79",
|
||||
"state_root": "0xb1a9669102c2f7d49af01c4831923cb6f030c6a98b334ecb4d307b6c0c7698a7",
|
||||
"receipts_root": "0xcd85cea85d138342fef326c1c73eb4fc4479f1fc567e7687c5757c39b5a20c34",
|
||||
"logs_bloom": "0x00200000000000000000000080000000000000000000100000000000200000000000000000800000000080000000000000000000000010000000000000000000000020000000010001800408000000200000040000000000000000000000000000000000000000000000000000000008000000000000000000000810000040000000000000000000008000000000000000000000000000080000004410000000800000000000000000000000000000000000000001000000000000000000000000000002000000000000000000000000000000000000001000000000000000000000000000000000010200000000000000000000000000000000000000000000",
|
||||
"prev_randao": "0x1e00579f0d5b1861c9e0978a213dc67e0fc4296458f392e378d06b4b85b4a8c3",
|
||||
"block_number": "141529",
|
||||
"gas_limit": "30000000",
|
||||
"gas_used": "207715",
|
||||
"timestamp": "1740424464",
|
||||
"extra_data": "0xf09f90bce29aa1f09fa496",
|
||||
"base_fee_per_gas": "7",
|
||||
"block_hash": "0xc8807f7a1f96b0a073ff27065776dd21eff6b7e64079c60bffd33f690efbb330",
|
||||
"transactions": [
|
||||
"0x02f9017b8501a588771083013b2285012a05f2008512a05f2000830249f094d27d57804f09a93989e290cf12cb872c39ad2ad280b901040cc7326300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000005693ed58622afdb000000000000000000000000b3db4f6329df01ac317a70200f6614e1cd0db6f7000000000000000000000000fc7360b3b28cf4204268a8354dbec60720d155d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000d0ada425f6835193b8507d7de3a77ec1bd6c5377000000000000000000000000c8ae6c2d3f6695e41b5cb149beae76600f4ac97dc080a0ee5a62cc99129e5aad3d6b2de4f169dbcfc3b17878bfa9e944879f2899f5d16ba013bc53a251af668e24bfd2157c52a7168e5cbc235cd3615b6f5f733e0bdfc5c2",
|
||||
"0x02f8d18501a5887710038459682f008459682f0e8301395c940000bbddc7ce488642fb579f8b00f3a59000725101b860aa01b02b16b7a56850cc9b7e1275e8d49e16fc12bd30b4bdf2ef68b5543822b2466101cb541b8815b5ca1721120e4f9da3dc91086418a5680fe3037dba62dda3de79dd22bb41036719c3771f140b419586ae7d9bdf3b10d88850909d4556b19bc001a07ce67cc0fe3ce5e16083414de6c422bbb8d61e2f345b25fc60c8bf58af8a52d3a024b28ae0bae9e75bb588466a4a0ec84d27b4df8127dc58a50cf80b2edc5add54",
|
||||
"0x02f8708501a58877108219a7800782520894f97e180c050e5ab072211ad2c213eb5aee4df13487025494cb85b9c880c080a08a3568a5f66c85d336f3b98586760753bf3960dc22b5a7edb7551fed54637727a033e49b70c561443b3cccc06e8656e4ac23820f1911800473776d92c0a6014f1b"
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"index": "491304",
|
||||
"validator_index": "71416",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491305",
|
||||
"validator_index": "71417",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491306",
|
||||
"validator_index": "71418",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491307",
|
||||
"validator_index": "71419",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491308",
|
||||
"validator_index": "71420",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491309",
|
||||
"validator_index": "71421",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491310",
|
||||
"validator_index": "71422",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "59212"
|
||||
},
|
||||
{
|
||||
"index": "491311",
|
||||
"validator_index": "71423",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491312",
|
||||
"validator_index": "71424",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "59263"
|
||||
},
|
||||
{
|
||||
"index": "491313",
|
||||
"validator_index": "71425",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491314",
|
||||
"validator_index": "71426",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491315",
|
||||
"validator_index": "71427",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491316",
|
||||
"validator_index": "71428",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491317",
|
||||
"validator_index": "71429",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491318",
|
||||
"validator_index": "71430",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
},
|
||||
{
|
||||
"index": "491319",
|
||||
"validator_index": "71431",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "67541"
|
||||
}
|
||||
],
|
||||
"blob_gas_used": "0",
|
||||
"excess_blob_gas": "69468160"
|
||||
},
|
||||
"bls_to_execution_changes": [],
|
||||
"blob_kzg_commitments": [],
|
||||
"execution_requests": {
|
||||
"deposits": [],
|
||||
"withdrawals": [],
|
||||
"consolidations": [
|
||||
{
|
||||
"source_address": "0xb57a360b34e22c598a9b0da37c5b9a7825da4db6",
|
||||
"source_pubkey": "0xaa01b02b16b7a56850cc9b7e1275e8d49e16fc12bd30b4bdf2ef68b5543822b2466101cb541b8815b5ca1721120e4f9d",
|
||||
"target_pubkey": "0xa3dc91086418a5680fe3037dba62dda3de79dd22bb41036719c3771f140b419586ae7d9bdf3b10d88850909d4556b19b"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
305
beacon/types/testdata/block_electra_deposits.json
vendored
Normal file
305
beacon/types/testdata/block_electra_deposits.json
vendored
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
{
|
||||
"slot": "151016",
|
||||
"proposer_index": "27017",
|
||||
"parent_root": "0x2f135d2fe887c6012e78b25b8adecc33bc268c8057e444422f9fbdbb02730a30",
|
||||
"state_root": "0x62a65efc3b24c02a29c4fdab5edf076329024a05dfc944b35f920a38d07b24ac",
|
||||
"body": {
|
||||
"randao_reveal": "0xb765dc7a976fb26b7cf8df404055cbab5e7665dfc1106ed4373709840d99312fb5f782a44c25294cece2f55e0cde6d1515504693574ddfb9bbcfb47c21763efed46b91cb47cefb5a14c78dd53f491d51e40b36eaf26e23e1edc49875e8c734cc",
|
||||
"eth1_data": {
|
||||
"deposit_root": "0x8654042dec994aa6fc1a36fd0f4ebb2a49d1b27ada9460b045e2ea2a15718cc2",
|
||||
"deposit_count": "10010",
|
||||
"block_hash": "0x28f59400e7becd79cfe3b14f36dd58fc27826849247dbfd1e4d09448806f8955"
|
||||
},
|
||||
"graffiti": "0x6c69676874686f7573652d6e65746865726d696e642d33000000000000000000",
|
||||
"proposer_slashings": [],
|
||||
"attester_slashings": [],
|
||||
"attestations": [
|
||||
{
|
||||
"aggregation_bits": "0xff7bffdffbffffffffffffefffffffdfffffffffffffffffffffffffffffffbfffffffffffffffffffffffffffffff7ff7efffffffffffdfffffffffefffbf7fffffffffffffffdfffffeffffff7ffff7efffffffbfffdffffdfffffffffffbffffbffffffffffffffefffbfefffffffffffffffffffffeffbfffffffffff7fff7bffbfffffffffffffffff7fffffdfffffffffffb7fffffffffffffffffbffffffffffdf7fffffffefbeffffffeffffffff7fffffdfb5dff7fffffffdffefffffffd7ffffeffeffffffffffdfffffffffffffffff5fff7ffffffffffbffffdf7ff77ffff9fffffffffffffffffffff7fcfcffffffffffff7ffffffff7ffffffdffffffffffffffefffffffdfbfffffffffafbfbf7fffdff0f",
|
||||
"data": {
|
||||
"slot": "151015",
|
||||
"index": "0",
|
||||
"beacon_block_root": "0x2f135d2fe887c6012e78b25b8adecc33bc268c8057e444422f9fbdbb02730a30",
|
||||
"source": {
|
||||
"epoch": "4718",
|
||||
"root": "0x6567f31ab5ccc3a0b0cd5d27abf183ed36704f817d09cb9dbe183da83cf07bf2"
|
||||
},
|
||||
"target": {
|
||||
"epoch": "4719",
|
||||
"root": "0x49ce68a9103d485d81d74d0c744a3fde20657c4cb81fb13b12cee75a2a29804f"
|
||||
}
|
||||
},
|
||||
"signature": "0xb7ad39b499b0e5b8a22849b068c98a08fe611d45a2f43e30fa449e657436e685e9e2a9bfae3a3128baf939f4394c0f2717835b0756e3177cb38d58775618ff6607fb68f362d60b9f865daff0ac239da49690e8fe439aa0dc27e2d4196beac98a",
|
||||
"committee_bits": "0xffff010000000000"
|
||||
},
|
||||
{
|
||||
"aggregation_bits": "0x0108000000201040400000000000000208",
|
||||
"data": {
|
||||
"slot": "151015",
|
||||
"index": "0",
|
||||
"beacon_block_root": "0x2f135d2fe887c6012e78b25b8adecc33bc268c8057e444422f9fbdbb02730a30",
|
||||
"source": {
|
||||
"epoch": "4718",
|
||||
"root": "0x6567f31ab5ccc3a0b0cd5d27abf183ed36704f817d09cb9dbe183da83cf07bf2"
|
||||
},
|
||||
"target": {
|
||||
"epoch": "4719",
|
||||
"root": "0x49ce68a9103d485d81d74d0c744a3fde20657c4cb81fb13b12cee75a2a29804f"
|
||||
}
|
||||
},
|
||||
"signature": "0x833659ce0cf0ea303c1df6d0754f5303392789c91498635bcd339f0056c496e770072f025eb962793105137c424d80af05a6ebe8d6e7f9de48121fbc362b91d1644f23ce3728ddfab680ff2c064fc73a049389116a56409ae35d675b37c59e92",
|
||||
"committee_bits": "0x4000000000000000"
|
||||
}
|
||||
],
|
||||
"deposits": [],
|
||||
"voluntary_exits": [],
|
||||
"sync_aggregate": {
|
||||
"sync_committee_bits": "0xf7f7fffffffbff6ffffffbfffffffdffffffeff7ffdf7ffffeffffffffffffffffffffffffffffffffffffffef777dffffffff7ffeffffebffbf9ffffbffffff",
|
||||
"sync_committee_signature": "0xa7be0f119fee5d9f9409d508b2291e1d8d81d3b9edd64d7a68d12f9b1a84bd50230618e4db78ef6e7f1e2f3e6aa56e2807985b25d52750a56b9d1b087ae3bca80c3a0b091e52c3797d82778acdf877e42b8fb18598d9fc56e9e469ebdd5c15e1"
|
||||
},
|
||||
"execution_payload": {
|
||||
"parent_hash": "0x52ad968c44fe260e5bb67b63c3ede2ade269a23641d27fcceba237065784c89e",
|
||||
"fee_recipient": "0xf97e180c050e5ab072211ad2c213eb5aee4df134",
|
||||
"state_root": "0x34ecb1a20d718e06f69e4ec6b6ad86c75603149a207480b03a077d0231668805",
|
||||
"receipts_root": "0xd2da2f149a53d2c26982187652ecdf1114ae5301359c0ee6752c2b78dd97ea02",
|
||||
"logs_bloom": "0x10200000000000000000000080000000000000000000100000000000200000000000000000800000000080000000000000000000000010000000000000000000000020000000010001800408000000200000000000000000000004000000000000000000000000000000000000000008000000000000000000000810000040000000000000000000008000000000000000000000000000080000004010400000800000000000000000020000000000000000000000000000000000000000000000000002000000000000000000000000000000000000001000000000000000000000000000000000010200000000000000000008000000000000000000000000",
|
||||
"prev_randao": "0xb45479ddbad8fc0733b7762aed1a5b5861712b29bc20c2c0e34dc5e6722e82c9",
|
||||
"block_number": "140858",
|
||||
"gas_limit": "30029295",
|
||||
"gas_used": "2383293",
|
||||
"timestamp": "1740416052",
|
||||
"extra_data": "0x4e65746865726d696e64",
|
||||
"base_fee_per_gas": "7",
|
||||
"block_hash": "0x1f2637170986346c7993d5adbadbebbf4c9ed89c6a4d2dff653db99c8c168076",
|
||||
"transactions": [
|
||||
"0x03f901198501a5887710821dd184773594008477359407830186a0946e8d9c2108be0894bd30c8525c71d1bbcb31912e80b8808b177fd73255e0d6bd6e8a142b0572ba8f34d66075cf829e3fab19009fb1a8a5d057276080527fd8fff3f3f4d3b02314aa185d801e8d378d88992aa540c8b093c1e4264b68777c60a0527f69df553e04f0a6a7435a5650668958d47f9d4187d2ff2e72a91d8d9f74928c8c60c0527f8d8f0c90c68115c209dcec644ae93d4d05c0830f4240e1a001359c559c4114b7919abca725d032f48ab4519d2857567795ca4f49345e65a280a091007fbb893a18315106cbcb53bf044d6a5c0ea7098e0b6198727c3983ebc628a06c9abb0a1ded37b6305a5711831f03fce5e467a70dbc9c0c724ebd18b200a202",
|
||||
"0x03f901198501a5887710821ae084773594008477359407830186a0949ae53a8e1ac8eeab1feece735b3d840634e30c4980b8807fb45278a2777c8ecd11eb2c2052276fd12c86bdb2373aa59d293c6b0ae1e8df076051527f8c9bb0742b467c164d5aad3087f92355c3f8cd0c291ca87b086e86fffa73780e6071527f8b11001c065ced40db4a5b2c43de47fde7a3b22f8919b39acd8d0b309df8a40a6091527fa49bf4a805c16f59556bec2665b647e02cee4fc0830f4240e1a0019da1702b651bd92e079ca2e801671cf22f3d3b6c097a185275dca50784123f80a039e483afa3751d7d758f2273fb19ac2143e5ce225eec49244f0aa358dc9fcb13a02e4a552312d68db400acc3898835e0aa32f46b8ef786ad6e2fbb0d933c51c551",
|
||||
"0x03f901198501a5887710821dd284773594008477359407830186a0943c9f26a8f3c71bd76665e89a50f5f70d9953901e80b880605c60fe5360ce60ff53606f61010053604b6101015360fc6101025360396101035360e36101045360b36101055360926101065360e061010753601461010853605261010953607361010a53609561010b5360c861010c53608a61010d5360d261010e5360bd61010f5360ff61011053603461011153603761011253609e6101c0830f4240e1a001f8b21450b076ae1037e2e42f8a6899f01f793a2bee9f59e00ee520af66d56a80a0e97e75f469a7478d725c078e7d5b6979018753556850a61cfcd01cd2c87f02c8a024a8247013e502c09f4d7981f912995c44978f9680c261829bb3f0ec86627ea6",
|
||||
"0x03f901198501a588771082398384773594008477359407830186a09401abea29659e5e97c95107f20bb753cd3e09bbbb80b880600060335dc61f7ffede24c98d6f2e886373c1dd99c824ad17b93cd6cca65a8856cd72972eea737960665260b3608653601a60875360f660885360ab6089536024608a536043608b536001608c536038608d537f15db390ca3613b231b40c801210901ec898c67f53aa7864f903944c3e3568817601b527f23a5a30cb6c97aa2c0830f4240e1a001c543c4e8d14cbdfb690f66b63b8b4c4fbd3963b1a555c6ab3ae43b08367b7880a065b7869dd9655457013e3e88c7866205124bec055f51b41e1fa92acce78b994aa07b83ebb8f12ba28df90ebd3eda8858204f2ff0eccb7ad8be10fafcb249bb3fdc",
|
||||
"0x03f901198501a5887710821ae184773594008477359407830186a0946a5e2c588bb18c17cdadb44f82d22a895da0d62480b8806000632a674ca35d600060f15d60e249603c49604d496097497f8474c6950d31f88091bd7bed171350e494e797be9c933f1288dabacbbb84810e60c1527f84f66da36ef2af9007aa316630acc16ee30260d66c55ec449fdf03fb20860c8660e1527fba2768ed15416619a048e1b79c81583af46d1f3ff8141279fd98cb2326e0c0830f4240e1a0018ddeb1837db4291ad48990f90ac5bca82d10b9d3d73eebf1aa5c314cf91abd01a010004635691cfef330e4bb27701074e719e65c037e4182be65becc5e1945522ca02cb00278eb15a555f9ce56dcdf783135e8aa6c852816497d389190e63ae304e6",
|
||||
"0x03f901198501a5887710821dd384773594008477359407830186a094000f3df6d732807ef1319fb7b8bb8522d0beac0280b8807f9a8968ca7a94768406d68b36a066e74dcadc49bc3605fdb95370ae9040470a4860eb527f0d8c1c38ee4b5dbf2b28f10ecc705f9cd2a9a36cfeff6c433efb8bd7c99626e061010b527fbbc1026522b0e497dc9722fc477fb7344969fbb16d66e31cc6e03de9a23178e361012b527f63ed37d8ac0e69b5609744b3ada8384a1dc0830f4240e1a0014cd492e6c4d129d4d3b22182ea5a5a632f3320aebbb6251d0ad724a747391a80a0c07736c5db2bc49a29ac00df7b32abf94978bcea15e65423b5cdd7d35df126dfa06b3f8c10978fc3e1cd60753975ed21762bf99a59fb29cd8e0a970321ee72ba9d",
|
||||
"0x03f901198501a588771082398484773594008477359407830186a094dc547f9b829e446d70566195aecc6a5977e5860a80b880600060335dc61f7ffede24c98d6f2e886373c1dd99c824ad17b93cd6cca65a8856cd72972eea737960665260b3608653601a60875360f660885360ab6089536024608a536043608b536001608c536038608d537f15db390ca3613b231b40c801210901ec898c67f53aa7864f903944c3e3568817601b527f23a5a30cb6c97aa2c0830f4240e1a00150e9783b2dae0fabae7243e90ddc1dbab17e6a0934ba2434aac62f27cdb2d001a0ad1f8b4409159784a76bc7abe3a89003139274ff8215bfb50da2ffaa8b312cc8a032aa53063bbae96713f93714c830c536949c843cc09a3dde8cb2ebdfcfe5d613",
|
||||
"0x03f901198501a588771082398584773594008477359407830186a0947a40026a3b9a41754a95eec8c92c6b99886f440c80b880600060335dc61f7ffede24c98d6f2e886373c1dd99c824ad17b93cd6cca65a8856cd72972eea737960665260b3608653601a60875360f660885360ab6089536024608a536043608b536001608c536038608d537f15db390ca3613b231b40c801210901ec898c67f53aa7864f903944c3e3568817601b527f23a5a30cb6c97aa2c0830f4240e1a001bae560947db14e3ed438ca8504783d40c9047191f49933b1d5e1ec8208583a80a0237ae7f13f17c0f994513c83636e1ac7e4d7c722221b51778352dfab41864310a036cba8ec5a2d31e6342f6afc914e3ba9dbfad0db22d29cb74e46e170ae2b09a9",
|
||||
"0x03f901198501a58877108236ff84773594008477359407830186a094000f3df6d732807ef1319fb7b8bb8522d0beac0280b8807fc51dcb985e3563ad803460f85ed4746c57bf060d8e2259116646a0002c898fb360ea52606561010a5360a761010b53600561010c53604861010d53600761010e5360a661010f5360986101105360a96101115360b06101125360dc6101135360e6610114536071610115536060610116536091610117536084610118536041c0830f4240e1a00118b3d58c9a58f76bb27cff8ee97664f8b88ea8ec955abc1463805d2b96428d01a00eb390be8ce5900249b939af5bf2ca243ed1902ea6b1bc3bf372f8028b95861fa048dadfa661c274595edc2cf2797b9ec4dc873cd6a75d7329293f873c4335438a",
|
||||
"0x02f9017b8501a58877108301391785012a05f2008512a05f2000830249f094d27d57804f09a93989e290cf12cb872c39ad2ad280b901040cc7326300000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000ee86442fcd06c0000000000000000000000000000b3db4f6329df01ac317a70200f6614e1cd0db6f7000000000000000000000000fc7360b3b28cf4204268a8354dbec60720d155d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c8ae6c2d3f6695e41b5cb149beae76600f4ac97d000000000000000000000000d0ada425f6835193b8507d7de3a77ec1bd6c5377c080a0bb753e1df11f4e15f7b1f44c04d738b30e0849b804bb786f7fba10edc3d7668ca05d7151817aca075c8d7bf9ba2b74e9294d28de1c4b8d3e5b138669e89324885c",
|
||||
"0xf873836181cc847735940782520894fcb6e353ad4f79245c7cb704abcffe2f4868424188058d15e1762800008085034b10ee43a031323a053c2013914ed32e251907c3311e4218561cdd8c25eaf8672355e66720a046900db9b5826610a5208c61a6e0c9f64178a76caefd64037db69b997295bf5c",
|
||||
"0xf873836181cd8477359407825208940d3de4256d6322683fdea9ee23765ccbfcb83da488058d15e1762800008085034b10ee44a0cde1811bcf855221edb3b337e6bdd6bf26b11c9387768fba8597daff950ad5cca07867720bce30276937c2d806024791f158a64862ec35a036ff1dbb01d16be959",
|
||||
"0xf873836181ce8477359407825208946021752d8d9b2f221d4fea4349dea34ddbcfce5088058d15e1762800008085034b10ee43a0f833125f6025471a4f351c2777d5841a23e4f6002c51161855004820fb91eaafa01722b78eba514ac450c0459568b4dc9521bb797a7c601c5c8825cd5e006f71a7",
|
||||
"0xf873836181cf84773594078252089461e296d527edc89e831cf593ec341f16197eeafb88058d15e1762800008085034b10ee44a0151e6c4bedcf29fafb9ef50f81f7e573d75ba42e00c46e91b9761de3e13cdc37a047bc54be6c3493ceb0949a3324ee1cb93c8058c9db71fbfb5c16ffdb472c0fed",
|
||||
"0xf873836181d0847735940782520894cf7317ee7a3b497ecf634b94bff60ff91b92574788058d15e1762800008085034b10ee44a0c6e018c4eb28bab20355104705e0570a371c157dc592e503713bcce730953d76a0279412202acdd26ab9d03c2edb129aa974fc39c86a0ecbef410c0a69d27789d9",
|
||||
"0xf873836181d18477359407825208947e7b519df31f77ced83eea1b16aedb6dcb0f0b2488058d15e1762800008085034b10ee44a08fe9e5cd6238d8e3e21ab280bca46ce43c23620b1f81b386265efbcbdb106385a018945e95e62a6148cbf35649e1e2ec4835491b4e4b6216e316c4932c96220ecb",
|
||||
"0xf873836181d284773594078252089488a075e0fb1c9309a200a8bf0a88b214bf7ceb8d88058d15e1762800008085034b10ee43a090b175e1335dc2c4450dd3a0543e19e2635aebea5675c94eac1cce52c7ce3d64a021aab7cccb861a1c30d876526a4ef0b7e217d784fc89cbd02e8c6d63ad35b0ba",
|
||||
"0xf873836181d3847735940782520894c8d7cfb58f3ac02568e6505bf3fb5eb6f080703988058d15e1762800008085034b10ee43a06d530baafa0a899792978df61bf5adc6028be85be12f55c687e44f1740dc22ffa06d4196c744ecfed500c4ad0fa17947c12238227155acea265edb7c7461571259",
|
||||
"0xf873836181d4847735940782520894e0132e8d7b1b766e0ade5543d6c6c0b2d5a2f01d88058d15e1762800008085034b10ee43a007e42562bf3a9a058fb1803d7c423ea0d7113fc98788be2a6b4d982210066139a02a24a9efb9f88cc53ad464b62601af3e54fedd8305e1b98670bfc8dab223c596",
|
||||
"0xf873836181d5847735940782520894eb674c0411db79654afdc1e131f3b6e734baee6c88058d15e1762800008085034b10ee44a0e197786db41e4d21914f319e669aa504f1e821d2cd17b39a396b1b85b5348025a00b1efe1355fcb919642f14f7609893afe70017eaa999d531e6f77fbdc5d28029",
|
||||
"0xf873836181d6847735940782520894dc07c60993cf689438b8c85f86b0ed938dca77ea88058d15e1762800008085034b10ee43a0f00a06c5148f52c55b2f65668a757c9d0011e7924032b88c771e3934e0bab6a4a0480e37e4cd116cedecd7de44cdb91c698ad896abbd3b08e2651773dea497e8e6",
|
||||
"0xf873836181d7847735940782520894110ddc93db59ed31a03518510221ec2f35d28f2f88058d15e1762800008085034b10ee43a047c9b1fb51b626b40cb2707269a8ad6a7391c467864e4423efa3b4ede8bf6d3ea028314d010ef79d04027fec9c0efb0bbad7ef386e110bc8e07797e78f484cb51d",
|
||||
"0xf873836181d8847735940782520894b599a876aaac824cfce21bdf15627c9fd8634c3088058d15e1762800008085034b10ee43a0630d66f57eae656309cdcf02dbf20db3bf960c77fa0c66b72de13b7852b0c8d7a00541603caf792d410387983650ec5799d9eaac5761296b3ad09f26fbe40fe251",
|
||||
"0xf873836181d9847735940782520894d36e5540dd71acbd6416d60252c4d7c34a3c824588058d15e1762800008085034b10ee43a0dd6a775844cc76e89ab1b2bd43d266b3b1b6e34a8d129cc1c1f7ce28c44b2439a01ab09f383573d80274ed64053a079902796c2e6e4cc19abf0271d37e2db4a0d0",
|
||||
"0xf873836181da8477359407825208943adeca35af56206a74987a8fe13c669365c770cf88058d15e1762800008085034b10ee43a0a8f36c6870413fdea217d52c0b6bb3d6b55a837e7907ad2a3979da577f9f1763a0646ed9c93233cae748988a3a33debd3411706a2a88c3349d2702da1b5a728cea",
|
||||
"0xf873836181db847735940782520894d77b95acd12f7b4b5692b55717b7bbca1165195488058d15e1762800008085034b10ee43a052069ecf399b7323e46892858d2d99668d7554b581245656d97768972f533683a009824b000ffa65ec5acc5210674b12ec2b92f45214e2af2c541a9da825baa9f6",
|
||||
"0xf873836181dc847735940782520894f388bf5766b5ed5d4e1cbf15772e677dbfa80b0088058d15e1762800008085034b10ee44a0532fcb541538ee4041bd97e09c0f86fde22760d4d3e3922b48d2fec22c41503fa00aab5d1fc4438ea787d42f08508c82cc8c9940583217eaf25e9cd12e53241b4b",
|
||||
"0xf873836181dd84773594078252089435d4996296e58560e6ef47787d51b55f1e2bd92a88058d15e1762800008085034b10ee43a01c2172f632447d91029c0bb197b6f29c6f16be40ddbc45637132aae1734dfbf2a06c7e9868cb7e95d72edd545e173887a45d405e3597da99067985d8e7343ba224",
|
||||
"0xf873836181de847735940782520894a4c3b77b898e53d6095f11c53a1ce272cff9af3188058d15e1762800008085034b10ee43a02ba7caf4ef662c4a459f11d2ff90c970c8b4d5ab27e201b8f80e12d763a46f57a02e1f114037a153d9ad303e5633753785bd7c73c5cb0b9f3bb8159312eb0692a4",
|
||||
"0xf873836181df8477359407825208946e84f6113fc1919714f0266705813fb81a17181f88058d15e1762800008085034b10ee44a07ac801acd1375d7bc7d5a7e54553be58976519169de1e2e14186494087a0b4baa043a9a4311283cbcbca29255de9e3bf1e688d73a0e4e20ff8d8b5a2e16b6e630c",
|
||||
"0xf873836181e0847735940782520894e9ae1a806004e1452baae0493920815aadd8479888058d15e1762800008085034b10ee43a02fb8319f5613dbf37419d177b132306a7c55b2d72d04522d6eceab27ddba4f82a03f1b125a7c93ff5e6c278df26ebf8411fd9317112424a26dabc5a23557712272",
|
||||
"0xf873836181e1847735940782520894fe1905d8ebd20e037274eef441283c811ea82c1688058d15e1762800008085034b10ee43a0224ee510dbb1e0ef70a7a016bd1512039fcf3af0af9a34b9b1cc2ee90a002339a02b7a11a1ff4a20c1253069e4b899ef0aa2f2702c3c4460d478f92a9a43e00b67",
|
||||
"0xf873836181e28477359407825208946adece88e477f53a143a4c29d97940df2ec768e088058d15e1762800008085034b10ee43a08590ee9e8715492a2e5c1390f4e389143c410e1d416f5202994b516f51e7eafba03159c4853aeaf69bf1c44c4e6cc8e449bc7d42eba79384cd8b133659d38159e5",
|
||||
"0xf873836181e38477359407825208940d34d140a7376892c4593fcea3ae26f5d6f202d788058d15e1762800008085034b10ee43a01a115564422f4e9fe3d1e51202e25ad90705509c2b827423bd9a766d67e6c6d1a00fbc0b850c9835c6a75a4baef78f29458f592ca83447dbe1c5f14c38724a86d0",
|
||||
"0xf873836181e4847735940782520894d1c7fa75b9bc55d041fcdf215f3e3a351c9f9edc88058d15e1762800008085034b10ee44a0f1bcf05bfdd6d0d2a4345f86c2875dd7bfa685745427754fd3b524a69664114ea0674e3c62d1e0b5ac4d79cbc972ea51e2183201f879e17b9061671107089fcdda",
|
||||
"0xf873836181e5847735940782520894418ebe350a8c6387bf5e42f3502742af8e0781f188058d15e1762800008085034b10ee44a02b5935a36f3240d235d2574f6a0d97f432aa4443da752e1101479a9392b124d3a0674f43bb33455d22e08224c584a2f206cb6ac4a86aa312f8d63b75f99fef5764",
|
||||
"0xf873836181e684773594078252089484914d2770c711d27888c775c547b1d933b48c4788058d15e1762800008085034b10ee44a0c94d55923ef4bd2e4c0a83f31e36dc49f2ef84bda5a4dba429795ffe33bb6574a04101d4d64e212019cdeb93c8e2ec7488405c2f716acb64e5ab70ca4689c4a370",
|
||||
"0xf873836181e78477359407825208948f51e560b85edf2e653c689c4e9fac02ce0556b888058d15e1762800008085034b10ee43a081e8fb2724b676b50ed6a9dd02a3dfdbeb1cd715cbe485cfa98a204c5a7a35dea04d7f13607047239a65d798a199a66460d89f3334a79f49587fbb58ad499e1a1b",
|
||||
"0xf873836181e8847735940782520894ee2503205c24dc66346e356f13f333fb8782d35888058d15e1762800008085034b10ee44a098b33a8231bf024845d5bf52add950edc4b1c53f23b03b8b5439ffcbfa034522a03dced7a7342c7d2eb201d8225009a41527dd98f7a6475a8a65660feb4c68d6d6",
|
||||
"0xf873836181e9847735940782520894096ba6c59bd667a0fea9a356bcc988e4d9f2d8eb88058d15e1762800008085034b10ee44a0c3a8d920e1f80f5139f70822ca6c899edf0bc17c8fb3c7f3a1318ddea7d62c7aa054e14fb1325f2a23214fc6ba3abf11ea547e01a1d95a3e490c3b1f9af277ec90",
|
||||
"0xf873836181ea847735940782520894da0adce4f1dc7debe7b2b52e8fe9ace6c7ea9c6688058d15e1762800008085034b10ee43a0755d9805b1d15d1392d860e86df05834fd94a61d18c22956907ac7c1bc982892a063de885856b515242c33c31f9cee81d23aee55fd632b88d86fcdb3f8566af3ef",
|
||||
"0xf873836181eb847735940782520894af7d412aeab7525c0541dc3aa6c1085cfb8c909988058d15e1762800008085034b10ee44a0b91c06dd7e4010c5d7b3f66285f4c139e22480773fb053409950ffe5bce7b720a0397f1b6ef9424dd5e30b07c110fa5b009cf86bfe92a98e994427d595dd9586fe",
|
||||
"0xf873836181ec8477359407825208943cf8c0d567261eaf4ac0872d33a9f48af361769f88058d15e1762800008085034b10ee44a0828618325c9586073c8199d866e9001a7b9ae129a29cc60cb43aca355175b13ea07f885bfcc7b096b6c47e0122bef7f0295712f0810a78ab4f0b53112561d2c84a",
|
||||
"0xf873836181ed8477359407825208944779242587ba9e828999249eadd82984430f484388058d15e1762800008085034b10ee44a0dd150d77fe8d9073a20510992f5eb8c70380327f5483064a6f42fb547465ccb3a04cb3406733b1e7318315255edccbad06adf0e6a3fe13f01389c5c95245cc8150",
|
||||
"0xf873836181ee847735940782520894ea531cfe2de357ecff3855b88dbd07f60b03cdca88058d15e1762800008085034b10ee43a08c72279cb318e4dacdfaf265959cd7f31002a1521113bc7e65851ab8ac2aed06a008340ab82a4a61f91d3e5f4594629b6a422fe11e8d6b39b2a145eabf15f3bbf8",
|
||||
"0xf873836181ef847735940782520894d00b5f53ea2a66ad33c3fee304bb22857dfb8a8788058d15e1762800008085034b10ee44a0ebb567f7d111eb54667984476e3a271c99220942e20e96f713ef9b4ae5728404a040bae6c5b88e58b0e02d1218df7ae41d9b3b99d2180eddf8ba770976852de483",
|
||||
"0xf873836181f08477359407825208947ead29f6616f78f21a951c9686dd257be7b8efe488058d15e1762800008085034b10ee43a0f749f26b7f700cb7bfbbb6e37206553e64ae70307f33cb60455bee1da135d4a6a072467439050b5a0d411d90c92fe6fb17676189d3fa8a0ad27ec4dbcdc03c14cb",
|
||||
"0xf873836181f1847735940782520894d503c13ee55c1ea128357d4018ec58d0d5e5c3db88058d15e1762800008085034b10ee44a0adbe9e31e5a740ee0b52a69b0cc85a718c799309df72991bc856c364f9b85f28a051a55333b441a7255a63e0fe6918aff4b6269576ef99763190c784624f8f7e45",
|
||||
"0xf873836181f28477359407825208944ac670d8760faf780468638ef80034876ed8918d88058d15e1762800008085034b10ee43a0636ee52b3e021114085c7413cbaab77013b432fd41cbf0a88dbd8d145e8a1125a02d15ffe159ec762142907b2e8b5e04b804361a399b23ff335ee4b996f7ee0a81",
|
||||
"0xf873836181f384773594078252089424ffb8c97ce443f8d3265ba3316defcfc07c659c88058d15e1762800008085034b10ee44a03c4c9d42c3a76c275f8f167cc846e62a16e2d4f54418b0303f64f0a1f2122fcda034d0cd15b9d14b118eb7a2e5bf9a464043e5b1935e740ca557fd05b47328b092",
|
||||
"0xf873836181f48477359407825208940c5cafc547ab98c9ceaa1c07fdd6bf7820aeb95488058d15e1762800008085034b10ee44a064728de799b66a56f3f634bca37944e0027ab9be12419d2e976bacfe295c5854a03a4b830c50b7c1cf39511b592ac20634bb54e6afb795dddbfc6c322bf6ac85fb",
|
||||
"0xf873836181f5847735940782520894db8d964741c53e55df9c2d4e9414c6c96482874e88058d15e1762800008085034b10ee44a0d56eac7a793978c4a8356bf94024e3fe292d9f61f594c3bc847a49c4c94df56fa059b143fedac3f768e936b63291180c1fe34415d27888e1b90326d28b4567fd53",
|
||||
"0xf873836181f6847735940782520894ba85bb35ae6ff7a34745993fcf92b9afd34124f188058d15e1762800008085034b10ee44a0b368c25c6edfe52749b1377664c2886a5c09f89d12c63078ba635dad22ae3ec7a004d02d8cec691a288092fd337f4a9c18eb4d15493cd512cb963a3be9cd9c0b04",
|
||||
"0xf873836181f784773594078252089458871015f5a2d3948264f7c16ad194c80ffd531d88058d15e1762800008085034b10ee44a0c6ecc87a4c18d2476757ca03080992de68e24b16826e35d0a26b6d137a4b6be8a065defc8e1b910d228675e323898baf81d24633cf57549021e64678e4e41bb78c",
|
||||
"0xf873836181f88477359407825208942a90af45df70b0031f218cc122598ddf3e10469f88058d15e1762800008085034b10ee43a010c9e461dbdac507e73c5c2092f83c780fd91de482b1db746544dca8adbabb9da05f1c695251e7fcb4fa0a655f5d64845e597fb3f2a948326dda2e7a87fa74ea50",
|
||||
"0xf873836181f9847735940782520894761bbaaea6ceb265f5262c3b559adc2ad3ed2f0988058d15e1762800008085034b10ee44a01e238f26c7ed2a54504a1ff05ab2cdffd5306c80281f61e3a93f950b2c965014a00739c6e4e75c3c312636bef62ff6fb8a1c0d0c71dd34f117e19bc58fd9353e1d",
|
||||
"0xf873836181fa847735940782520894dfe86f51c5e603f1420d1f0ab366bd3bfe23d2a788058d15e1762800008085034b10ee43a06994565ae218ce6ce28078aa4924a01207584026a892fd82b4bc8c187d8bf1c2a02b44cbe2120bbf57c777b2321e2b54e3d8015a6484e109bf761e07e38b22c9d9",
|
||||
"0xf873836181fb847735940782520894d616547158b05ab5079106dc0336d72763a7287188058d15e1762800008085034b10ee43a01559edc6eb87baba522ba21cd45da85f76be8adceeca311a378035d723efa31da063253dd048dbe7e7c7285a5b1b23dfce7655e060c5b9523f56c6de6b7fa30ec0",
|
||||
"0xf873836181fc847735940782520894dc68cd278cb7f5f666ce7b0a3a214a8540ed4dfa88058d15e1762800008085034b10ee43a067f63eb982417af7de349aa9cf07889ed0d7423407995926b8044fe7525154eaa00417db33f88ae9c142950ef84ae18f47445fa7c26b6a482df6ffb5e13ca89cad",
|
||||
"0xf873836181fd84773594078252089411f8107da05b6905e8cc0227ca3b0c6eb764fac088058d15e1762800008085034b10ee43a06594ca7cfdb0ee036f1735bbd3f2971be1da00edefc122a0214807828a7ead31a075a11a79283bd39456153246494d408c61f18f27022c4bb888b5742e1c66e3ef",
|
||||
"0xf873836181fe84773594078252089404da906545679850a7ee0ef6836e183031bedc8888058d15e1762800008085034b10ee43a062c55b09cc71c0c3e34bb97dc8e3d9e9918929f0c9490309090bc430bf6eb722a05f733500640bd0f6e61952424a3d9bd5dfde0ba3cda5be598489f0859df6c919",
|
||||
"0xf873836181ff8477359407825208948bdc25c43c010fd3db6281fcd8f7a0bed18838e388058d15e1762800008085034b10ee43a02518ed9188048e9a9666de8a3983dd208c2e582a18720a2605baaaffdb2ef4aaa0510893d599ed7e303b7027e9c9453a8f25409735ae782faad9fb3afefe4e458a",
|
||||
"0xf87383618200847735940782520894af16f746b8a834a383fd0597d941fee52b7791eb88058d15e1762800008085034b10ee43a0d8b4839aed9b334066a24ef5295f456fba1d0fc6d783b7d18012cdb2c18861e2a07a74bd5a40d360c32bceb00be00d3e6e608f39b04a13ecc3ed4672900f5c54b3",
|
||||
"0xf873836182018477359407825208940c5c736600f8ea58ccb89aa72e3f3634651fd55188058d15e1762800008085034b10ee44a033c53fbdc9c86cf60777c5d2bd192f57d36628c672a7ca61352effbe7fa781f4a01943da2502b48ee6b3b936769270da655c8d955c9a390e0c186ee5d3646d1d27",
|
||||
"0xf873836182028477359407825208946f475e0f0e9eda58556fddc04de9b1a9b6a4cfb488058d15e1762800008085034b10ee43a00a00563e58ee9203cba306e432af000938b1e2e2daebbb7d06210c0b3cdac601a05d0f1492b63df3df5ac1310fccbdc3e0863d8c3c7231186df265d32abe00f87f",
|
||||
"0xf873836182038477359407825208949b2e76498a695c4dc7d0890069cffa84a9581d2488058d15e1762800008085034b10ee44a0cb83570f01c24cdef394144b462fd9b7753337794a84fbb89c68e40038491487a03cb5dbc916553e4534921cc0c0d7e9d3c12afc7bb345ecf88e030c8d6f275a1c",
|
||||
"0xf87383618204847735940782520894e2d2b2069f4a54fcc171223ff0c17adbd743c28588058d15e1762800008085034b10ee43a01e6d349911a4261a944c289ec6594cb1d6414fbc96240ea5d0a134cb75cf5ff2a042dcc7cfe31d1260bc5093627e0fbace0f87812d37cfbcffe7beff500dfff385",
|
||||
"0xf87383618205847735940782520894386bd49f04322544f3c7178fa5ae1a24b947b45488058d15e1762800008085034b10ee44a06e76a8890665419b746548fd24bf999efa9eda82b509a2a13dff3c8d8774d239a00c54e75e6280c2368a3cc742f1c6c6d4853cdff6aa71afb3b59939283a70ba70",
|
||||
"0xf8738361820684773594078252089400af839c3fc067fafc2e0a205858d6957f0dd18d88058d15e1762800008085034b10ee44a06e90bb215dedf42450c3529ecacc852df6fa5c4d76b04b673d2dee5162f77afda061c0f17a6fb4eaafd5c1792bd780e256ae93b8197eba1f739d3483b6018b34c8",
|
||||
"0xf87383618207847735940782520894ebb6d32a650afa9221b55a11c6a6de52b6f07cd788058d15e1762800008085034b10ee44a0bb5b7a794279609699dcfef3544af96e2f36e1a080d9df5ae2f0d065d897e7f6a020b02a03622daf550239dbffa1d81c3637dfcc4267f19ec86deccf77005c7b3c",
|
||||
"0xf87383618208847735940782520894011d26a3a9adc9203c8943a6a77aa8657af5242088058d15e1762800008085034b10ee43a0033c85b6bc49bc1122163ed03061ffd5a580e70621a77877108c975f6280374da03a2ebd53fa8a5b2c0676e05e87ce8ae0f64f2a1304c1c01fb0a353ca084123f6",
|
||||
"0xf873836182098477359407825208949c85bc61a89fb5abd957e6c819c653fc1aa0d11b88058d15e1762800008085034b10ee44a02c511d53e802a2653fe1284802c56362ce7f36936030da8f2afabc9369812b8fa02ca196683edf5a07038d65d12d23b607d331558f8e8bbe1bb3638b3f87ed1e5d",
|
||||
"0xf8738361820a847735940782520894bd8e8435b7897d87cf7cedb5cf8c5dd865dbf72088058d15e1762800008085034b10ee44a0dc65a971d20e6b631f9ce091c891068600846b80a519b897dc90d85cf493b67ea075b4159b6bb958d789c05bfbdab2f58bffe006f1bc6481f5e5fbd0c0de87e0ab",
|
||||
"0xf8738361820b847735940782520894adebee2e3ff041078b62380d001c6e51b4f1559888058d15e1762800008085034b10ee44a0dc5c31ab6d48576b337145ca3480f20403342d4632db7e3c58a5c2a3722aad36a0088bee3e8da14156e74abd4b830ce7dadd9aa5acc682b0a5f441e2c579930dd5",
|
||||
"0xf8738361820c84773594078252089471e94c459c9f05085fc0d34b5f21e648e05dc6b388058d15e1762800008085034b10ee44a0cc7dd758523571aa417ca7e92ce18fa344eb09ae4030cbe67a20749a5f25cfa6a079260f268e88f5349a96d339a543c81800ddac65ded5bf66ff69bb8d2cf6ac8a",
|
||||
"0xf8738361820d8477359407825208947c1fe317db82c9298b87c56c3194178271b621e188058d15e1762800008085034b10ee43a03670f304b0cfec63bbb48f6adea22daabf97cb5066464b66dc8b0c5062547c94a046ff008a93f3ce2964b77be87730cd5e4fe706832955148580869189edd18d1e",
|
||||
"0xf8738361820e847735940782520894e069d1c9abf5127bdc3a164fb93b96bfa9f74ce088058d15e1762800008085034b10ee43a0118c5332b8e96e0938c49205cd48c34651dac16e06b177fe9ccb3c4a5714a818a0335ee8a457a31e10f6ece6faa85587e2527e69e7287f9cbfcbe977033091d5cb",
|
||||
"0xf8738361820f847735940782520894b9bbddd1eb6ef8fb1bdc6a853d5ad7486a9487dd88058d15e1762800008085034b10ee44a0fd2a39184707e76817b5132d4f680f58b814ae07053bff5444341964029676bfa03a96b5d6be262592e72f19bc64ca930f1e76a6655d89ac3640aa927ea066f679",
|
||||
"0xf87383618210847735940782520894a804387cdaf986d45831e8074efb2115af053f7a88058d15e1762800008085034b10ee44a03809b662c3a9d240d7b7f9e2cb4fb8d50b1e421b4548180704aedea809b2218ea063c9d8136461e93058f75ae91000d4e6f0cf21ff74d5c91c340209c1d04f0bff",
|
||||
"0xf87383618211847735940782520894f23501d784a041fc911b4c86c2bfb1f63ec170ea88058d15e1762800008085034b10ee44a02af9b021ce8467c713e9ba2dd9a948cdc1d67dd04117868b6897831a4c9b2448a06bb0aa527519e251ec40b747f25dc7c7c2ed689022ac4dd80c4a40c37efa06b3",
|
||||
"0xf873836182128477359407825208943928be2a7058088313c0fb3294014e88a3c5ed4a88058d15e1762800008085034b10ee44a098ea195f842b013d76e62b19cb989e3d853ca5b84602bad0b777ecdfebc2f0dba0540fdd9b2a2ddcb4a634bf63186c80987bcb9c19d0025ca0cbefba565e4afbbb",
|
||||
"0xf87383618213847735940782520894196aa07204141478459c14106ef5e5282efe995788058d15e1762800008085034b10ee43a0b817d9e63a606c505074d41ce01c53612ba083cd4b39adaff884c869d539fd6ca06dd3bd03aefe17446e3354dcf4864a712e4ac2e4c59701a41a4f7ffbc937adcd",
|
||||
"0xf87383618214847735940782520894763cbf89560e2da270000822abda9584db693fa388058d15e1762800008085034b10ee43a09b856c0c79b5f977db2d9c26992f9e2a113e86dfde2fa684ae4a28a45fdf7dd3a078ac54eacbdc28a7b98176c5c79becb1ebee800186c48b664a643ae760cf0318",
|
||||
"0xf873836182158477359407825208947feaea0ff70ffc9eec2104f57f7136aff4dea68088058d15e1762800008085034b10ee43a0a5d30ceced18dbf6684a5ce409f654f359ad2d6105ca8cfb6c66d0ad18fa840ca06a3f64c8d043f06d2b0d26fde83f003aeb24904226fea3712af38e2c7c805a96",
|
||||
"0xf87383618216847735940782520894e5466aacd9dd6d3bb35060a1ccc76a438de88ca188058d15e1762800008085034b10ee44a0b2646589ce46644bddfb0bbe5f5dfe9700a82accbc4ef29997094913c1ad85a1a01b65a1ac0f935af862b93dd79559b685d2c6afdf93795e70fc3cf174f95a7d9f",
|
||||
"0xf87383618217847735940782520894f670980415cfe8c4f8d10645ecf974c9a2fea00e88058d15e1762800008085034b10ee43a06f2d0bca0b319a77accb02cf01e2edf5fff3f5cb38fc99d25a2d19642027e359a05223e3bce8700a92b7770fe92d7c50527505230cea7a5ab05e90c5a7959e0cc1",
|
||||
"0xf87383618218847735940782520894a29115bce7829ffdd989b7cf1bdd1eac06a2cb3688058d15e1762800008085034b10ee44a09377c8d9a2e30c4c08b6d81fb5b645b6c4938d4047858668cbb6ddd3d67b2433a079e9b9182f4c0c6291a470f6dde8846b0aa729676224081d29940f0c319c54a0",
|
||||
"0xf873836182198477359407825208948f528aa67dc1846c893465fa1c8c26556bc5fe1988058d15e1762800008085034b10ee43a00e35ce7ac258b4230bbc01045e9b5e1789543dbd324e975504c0c60e9cb5cbcfa057879da45fe069aa050ff7734b6e59b5e127db0f1921b973885ff3ef6aebe3db",
|
||||
"0xf8738361821a8477359407825208944dc4ec6ac43c8c45777292db987203c0248e17b788058d15e1762800008085034b10ee43a03b63fd365340ceebecdb997d2b4bb41b336683c02eb1139cb1791d286ee13246a003ea3888ed23e1f986d8d3b9d560281d3484dd2fe974c25f7216c90568208b45",
|
||||
"0xf8738361821b8477359407825208940d2f39f251cb547cba567a31e5e9f93c19dffa8588058d15e1762800008085034b10ee44a050f14998905eb10dc8e53395da51b21b0eefde3bc103b615d6f97d7b44f26e36a008ae2d148621eefa13ef0e3fb6ce1d259c62c8e057180ed49d761fab9d312b93",
|
||||
"0xf8738361821c8477359407825208949eb31fb94ce5111e2a04cb9d156b513887ccbd0088058d15e1762800008085034b10ee44a07e88781530f943cd64ca63c2b7133bf8ed704356622be4b54f08751b54bf1d2ba00fa89ce3cc8f773cc83a72b7d58327740c41c95679e7003522c489ab9f66f88f",
|
||||
"0xf8738361821d84773594078252089404b88ef83f8c41b1465d360a1e82f07ae190892a88058d15e1762800008085034b10ee43a096a28917edd16daee0707b0459a228fb5e006a5b9714a5d9cdac681a6960836ca02bce226c902887909a8bba3ad618153e90ff2d93e3825a42b72181bd18154d87",
|
||||
"0xf8738361821e847735940782520894af23e04b04fbe15630eadd32a6f27a5a65ea554a88058d15e1762800008085034b10ee44a01dd4f73c1317ed5bd565999aa5fc130e388378ccd9f9641ed6a213d357d7bb7ea03e94fdad16c029234002c3ee24d382fae242f60bb58340c2a45488e57ad1bcb1",
|
||||
"0xf8738361821f847735940782520894746cdff371e3f1e905b3ac52280078bac2dec7dd88058d15e1762800008085034b10ee43a02a4b086a715feb49bb892f026c3995f4235ced4da4a500bd3c960af2c3853ecfa03c14793e2606d25e7b5469a04bca55f50af24abe997afa154ef276ebfc7a6e6c",
|
||||
"0xf87383618220847735940782520894c33e5155bdbf1a0a7ceb1b80f8586c5cda5c378188058d15e1762800008085034b10ee44a0b23359e18485b91d7599425b986ebb19cee52960533f993c24998636ec169a82a028d4cd7d4bc768fe29a6a3f32065b0a59188239808fbe5bb39e90ccd8eb43989",
|
||||
"0xf87383618221847735940782520894e7fdef5f5219068f3d0f88a7445005574c66279888058d15e1762800008085034b10ee43a0101fbd3be6b8fc9c5a7f5c8f3c9fec2abcedbac8ab3c7247df2c98e6b968af38a019c11ee7058d8a6eeceb2e8cb45492f442eaaaa7fff37cc7fdd9d17e92e3bfaa",
|
||||
"0xf87383618222847735940782520894f0a81a63c5e09b0bd08e027de48058e377d3732d88058d15e1762800008085034b10ee44a0f0e999e581df579e31fae928c52a7df96a1524d38f2e41bf4893ae7c4a083a48a022e1d07f18385e6d2f9fce94a25d042f4dee7eabb2df53d9ef694ba27869bb29",
|
||||
"0xf873836182238477359407825208949878ab34dc3b4a63c80fdb733491472c11d59a5688058d15e1762800008085034b10ee44a0d719bfbbfbdbdab38420d454322ccf418defe93e5d81dffc354e0324d4ebd5dda05859b730b918a46f7e58bc0858aee845eeb07e64a6be8df0357a18b519b3c00b",
|
||||
"0xf87383618224847735940782520894912859bebae3086ac7a062dee5d68aa8ed2d71ec88058d15e1762800008085034b10ee43a0a73cddf5a57016d0cf7e7f91e0e6e419f1a3e6485159868817ff0383fee03b4ba02a0cbafef77b725e6e3adf60c14463316b2d6780536e29d411044bf9d4a0518f",
|
||||
"0xf873836182258477359407825208945a0b737ed85049410e5ea61f444d07d5c8c0359f88058d15e1762800008085034b10ee44a06cea82c78f91a7e463a6493ae9aed4039cb220686ed4b650c89e4160835d1a30a062d3191502b312090301ea3dd63b6b29f4e64622639a6e080d4a15a1b2b22ae0",
|
||||
"0xf87383618226847735940782520894305a5dfd46e6128abce28c03b3ad971f4e4915ff88058d15e1762800008085034b10ee44a06ee9ca5a35966737addbde05c3702020db87737e2f3fe6f783023e69ef49c84da067ac4e87f6f02258d33cc1507006ed81501d1398428016f0b99eac2bd23f54d2",
|
||||
"0x02f9021e8501a5887710808477359407847735940782dfad9442424242424242424242424242424242424242428901bc16d674ec800000b901a422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200eabd7137c45a0f283d7839ca69f6bbcee6adc41416bc9e01c31e8ca86b767390000000000000000000000000000000000000000000000000000000000000030a3dc91086418a5680fe3037dba62dda3de79dd22bb41036719c3771f140b419586ae7d9bdf3b10d88850909d4556b19b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020010000000000000000000000bf3da697ab02552a5da95f267075cbe495d1ecb30000000000000000000000000000000000000000000000000000000000000060896bccd536b4a30c4c3ce5877544574c624dff09f100abcb2df38df7c797069aed3213f1320fe77e4cf89907fb23fe4601a9008fdbac478412f8d6a5eb4c53b12c3848c29ced5ded2a7e3fbeb1e1dc4f58a563d761f7ea80c06088126e0dd9eac001a0ccca64755c8eae02419698251ccdc9e3691d2caaf5a104cf9542383c1a548d2da00ac0d62ff682d5edf5746aa633e34a5e8e789ec3eccdbd63b8d12fb80862d80c"
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"index": "481571",
|
||||
"validator_index": "71016",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481572",
|
||||
"validator_index": "71017",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481573",
|
||||
"validator_index": "71018",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481574",
|
||||
"validator_index": "71019",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481575",
|
||||
"validator_index": "71020",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481576",
|
||||
"validator_index": "71021",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481577",
|
||||
"validator_index": "71022",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481578",
|
||||
"validator_index": "71023",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481579",
|
||||
"validator_index": "71024",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481580",
|
||||
"validator_index": "71025",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481581",
|
||||
"validator_index": "71026",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481582",
|
||||
"validator_index": "71027",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481583",
|
||||
"validator_index": "71028",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481584",
|
||||
"validator_index": "71029",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481585",
|
||||
"validator_index": "71030",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
},
|
||||
{
|
||||
"index": "481586",
|
||||
"validator_index": "71031",
|
||||
"address": "0x7bf8bca0ccd13d04fd466539989efe2adcb0ca7e",
|
||||
"amount": "34197"
|
||||
}
|
||||
],
|
||||
"blob_gas_used": "1179648",
|
||||
"excess_blob_gas": "67895296"
|
||||
},
|
||||
"bls_to_execution_changes": [],
|
||||
"blob_kzg_commitments": [
|
||||
"0x92549576706cd5a855ea6a82537d1c7190c8d7d439f4dcbcf4b9b2bd34c4712d14f4ed041644bca5659e1826570e58b8",
|
||||
"0xac58961e240bb13fd49d4cd60ace53bf3b6205ab7aee98987f3e589680fbe9d3febf3991150dd2ce0a785b9c7ceb42e2",
|
||||
"0x83945613f3af6994e24c48b02a39a7c0e9c98f168c0c12ce3bf1d5bc267236ed6770c9c8a1fd7d7a9152066ccfa7147e",
|
||||
"0x9334f6af7b93b7c1ee1cbac56bd38e9d9d6e50dd110bcee546333ec3ba0702c4aabe16b332ebc657967c66e9789a3a69",
|
||||
"0xa43089b05e5987070bdbc3b8742bb30d7c34ea0e81518dc26ccb3f3a97c23df6d96f2cf1000ff985b494cc04743c5e2a",
|
||||
"0xb45aefd32b12b74523a1082102de20e6965b31fb493228a12247e85165346087be8e54f435a60e9c96c1af716d4477c9",
|
||||
"0x93c5be3093d114302d2dbd86f132c925d7de78bf977c0fd47e0e2c2dff528af381989fe0e50dc82e48d7f5c34d02f6ed",
|
||||
"0x8a6a84e0ad21a30bacb75e8d0d5c3cf9fa89bb4d72a3cb4462a771cc533227aee35a21030258bb6b5869bebb3075be69",
|
||||
"0xb54c1dd86ee3f132994ed666988cbdfd9a2b775697ce3e17139850cfe6ab2a5f250a1e2e9103fbe241582eca35d26b25"
|
||||
],
|
||||
"execution_requests": {
|
||||
"deposits": [
|
||||
{
|
||||
"pubkey": "0xa3dc91086418a5680fe3037dba62dda3de79dd22bb41036719c3771f140b419586ae7d9bdf3b10d88850909d4556b19b",
|
||||
"withdrawal_credentials": "0x010000000000000000000000bf3da697ab02552a5da95f267075cbe495d1ecb3",
|
||||
"amount": "32000000000",
|
||||
"signature": "0x896bccd536b4a30c4c3ce5877544574c624dff09f100abcb2df38df7c797069aed3213f1320fe77e4cf89907fb23fe4601a9008fdbac478412f8d6a5eb4c53b12c3848c29ced5ded2a7e3fbeb1e1dc4f58a563d761f7ea80c06088126e0dd9ea",
|
||||
"index": "10011"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"consolidations": []
|
||||
}
|
||||
}
|
||||
}
|
||||
232
beacon/types/testdata/block_electra_withdrawals.json
vendored
Normal file
232
beacon/types/testdata/block_electra_withdrawals.json
vendored
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
{
|
||||
"slot": "151850",
|
||||
"proposer_index": "38060",
|
||||
"parent_root": "0xcedd94fbf2ebaf371384911b85bb3073eadcca25eeb4ab29d14acd95cd88bcfb",
|
||||
"state_root": "0x611fa0d96bedd90a2474b2e67f93f5a5edf82af93443079b58658c6739207a30",
|
||||
"body": {
|
||||
"randao_reveal": "0xa69041c990d2c6cab84d979be3c9db5081026874c8e37750c4274a355a7acf06f7fbc88857db3c20c84309f61026b20917a99fc826123ae930dba33dc92ced07ebdd57b45b1760fa08309c67fef843ee33ffef518e2ce9cb3c41da9e65d6f6f6",
|
||||
"eth1_data": {
|
||||
"deposit_root": "0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e",
|
||||
"deposit_count": "0",
|
||||
"block_hash": "0x1b60b6c9500355aa0ff7e1654482fea15eee29f2d52a25aeae859df953c0f7d4"
|
||||
},
|
||||
"graffiti": "0x74656b752d676574682d3420544b656466343463616147456230323761393061",
|
||||
"proposer_slashings": [],
|
||||
"attester_slashings": [],
|
||||
"attestations": [
|
||||
{
|
||||
"aggregation_bits": "0xfffffffffffffffff7fefeffffffffffffffffffffff7fffffffffffffffffffbffffffffffffbfffffbf7ffbfffffbdfff7ffdfffffffffffffffbff3fefffffffff7fffffdfbfafffffffffffffffffffffffdffffffffffdff7ffffffffffffffffffffdffffdffffffffffffdfffffffffbf7ffffffffffdfffffffffffffffeffffffeffffffffbfefffffefefdffffffffeff7fffffffd7ffbfffffffffffdfffffffdffdbfffffdfdfefffffffffffffffffffff7d9ffffffffdffffffeffffffffeffffffffaf3ffffffffffffff7bfffff7ffffffeffffdffdfffffffffffffffffffffdffffeffefffffffffffbfffffffffffffffd7ffbffffeffdffffbfffffffffffdfffffffefbfffffffffbfffdfdff7f0f",
|
||||
"data": {
|
||||
"slot": "151849",
|
||||
"index": "0",
|
||||
"beacon_block_root": "0xcedd94fbf2ebaf371384911b85bb3073eadcca25eeb4ab29d14acd95cd88bcfb",
|
||||
"source": {
|
||||
"epoch": "4744",
|
||||
"root": "0x810ed886bb9706fc2df193e2272e3a864eae69b43ca5797789f865b562cc3456"
|
||||
},
|
||||
"target": {
|
||||
"epoch": "4745",
|
||||
"root": "0x9a8e0a72cf9f99379750c1c8b403c6daed6138c4577610e36d4b405f4f7e307b"
|
||||
}
|
||||
},
|
||||
"signature": "0xaffbe8cdd9c06cb23046969189e66e3c825ac6d0d7170334d9a5d8e7797d47069dbf96f391a86f0eb4b1c6d2eca55b34123567907c3342b4bbe31e520a0e0be813d5986f2d679a2b1f9ee0072bdbde35bf56affc060fd33556733e57103ca4bd",
|
||||
"committee_bits": "0xffff010000000000"
|
||||
},
|
||||
{
|
||||
"aggregation_bits": "0x00000000002000000000000000100000000000800000000000000000000000000001",
|
||||
"data": {
|
||||
"slot": "151849",
|
||||
"index": "0",
|
||||
"beacon_block_root": "0x2bafda2b58819919eb49363290dfc50050f3f57cd6a0112ab4b5d4a42ba7c821",
|
||||
"source": {
|
||||
"epoch": "4744",
|
||||
"root": "0x810ed886bb9706fc2df193e2272e3a864eae69b43ca5797789f865b562cc3456"
|
||||
},
|
||||
"target": {
|
||||
"epoch": "4745",
|
||||
"root": "0x9a8e0a72cf9f99379750c1c8b403c6daed6138c4577610e36d4b405f4f7e307b"
|
||||
}
|
||||
},
|
||||
"signature": "0xa56ae375f437178909c0bf4ea632b94dff322f63bf383c816693c6f9841fb24fa7de3440fbc749cfc0c0d55e341c466b0f019dc77758dae97362c23ce1f8f71d8aab12f7491874d2d7c3e197037f313c840646da75c40577ebc0cdd239524f1a",
|
||||
"committee_bits": "0x8010000000000000"
|
||||
},
|
||||
{
|
||||
"aggregation_bits": "0x0000000000000000000000000000000400000000000000000000000000000001000000000000000000000000000040000010",
|
||||
"data": {
|
||||
"slot": "151848",
|
||||
"index": "0",
|
||||
"beacon_block_root": "0x85f3e44f8ea07968ddbca17eafd5b6ae402560a120ce96ae8ab9f06fe9bc2deb",
|
||||
"source": {
|
||||
"epoch": "4744",
|
||||
"root": "0x810ed886bb9706fc2df193e2272e3a864eae69b43ca5797789f865b562cc3456"
|
||||
},
|
||||
"target": {
|
||||
"epoch": "4745",
|
||||
"root": "0x9a8e0a72cf9f99379750c1c8b403c6daed6138c4577610e36d4b405f4f7e307b"
|
||||
}
|
||||
},
|
||||
"signature": "0x93c1542a02328cda2a26b7a2bdf02480fc244608c232d8239a6519ab5899588b531cc383f1a6462e37167ab6b85eead40296ceda75a9da55805bffd8631642b2197cc3c2684070b68f2543ad406ff992a9f9a3f852f9a4cce664400f4a0850da",
|
||||
"committee_bits": "0xd000000000000000"
|
||||
}
|
||||
],
|
||||
"deposits": [],
|
||||
"voluntary_exits": [],
|
||||
"sync_aggregate": {
|
||||
"sync_committee_bits": "0xf7f7fffffffbff6ffffffbfffffffdffffffeff7ffdf7ffffeffffffffffffffffffffffffffffffffffffffef777dffffffff7ffeffffebffbf9ffffbffffff",
|
||||
"sync_committee_signature": "0x86b38c811f1415024af4bdc7b60bbea76e938f326595aa7238ca047bf336ff74707e9da3052d3d376766901b2d097fd5140de9bd4a9c0a5fbd4f12e0ac6ae472d8493a87e6e9bd69590d39ef6f142f7b2990938cd56e54e48bd9a9a55e1a40a1"
|
||||
},
|
||||
"execution_payload": {
|
||||
"parent_hash": "0xef54f75df413929ddfa60638b93feab47a0ad57e7585069308dc3b31beb42e05",
|
||||
"fee_recipient": "0xf97e180c050e5Ab072211Ad2C213Eb5AEE4DF134",
|
||||
"state_root": "0xa694a1983a7427b1ee0524a1619573db4e8f48368d13dde2a1103142e1e77cbb",
|
||||
"receipts_root": "0x72a0eed2e520b8f791fc8dcafa8a94c3e411cba82097af3fb0287d3698c7bf0a",
|
||||
"logs_bloom": "0x00200000008000000000000080000040000000000000100000000000200000000000000000800000000080000000000000000000000010000000000000000000000020000000010001800408000000220000000000000000000000000000000000000000000000000000000000000008000000000000000000000810000040000000000000000000008000000000000000000000000000080000004010000000800000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000001000000000000000000000000000000000010200000000000000000000000000000000000000000000",
|
||||
"prev_randao": "0xbad3a8687ee866a509e9b22c4bd16d16ac2fc5a134fe8ce3477552604e5870c6",
|
||||
"block_number": "141654",
|
||||
"gas_limit": "30000000",
|
||||
"gas_used": "2716694",
|
||||
"timestamp": "1740426060",
|
||||
"extra_data": "0xd883010f01846765746888676f312e32332e36856c696e7578",
|
||||
"base_fee_per_gas": "7",
|
||||
"block_hash": "0xf6730485a38be5ada3e110990a2c7adaabd2e8d4a49782134f1a8bfbc246a5d7",
|
||||
"transactions": [
|
||||
"0x02f9017b8501a588771083013b8485012a05f2008512a05f2000830249f094d27d57804f09a93989e290cf12cb872c39ad2ad280b901040cc7326300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000056e915d8da78e50000000000000000000000000b3db4f6329df01ac317a70200f6614e1cd0db6f7000000000000000000000000fc7360b3b28cf4204268a8354dbec60720d155d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000d0ada425f6835193b8507d7de3a77ec1bd6c5377000000000000000000000000c8ae6c2d3f6695e41b5cb149beae76600f4ac97dc080a0212407d77d016985f42a043805a755a10b29c905c7e104c4c0f4bc00f0128ab0a0153258c0796acf19214c28c4554d82022c0f5b14c324068acc895d6e365a43fc",
|
||||
"0xf873836202478477359407825208946177843db3138ae69679a54b95cf345ed759450d88058d15e1762800008085034b10ee44a0797cebc48b0b84885aae6d7be2e45bb148b09f6a58c7bc35f6d4b0ed4218d4c1a0671bebb2c27f37350c18bc626f7096e22233e8243828efb5831d8de85e230f15",
|
||||
"0xf87383620248847735940782520894687704db07e902e9a8b3754031d168d46e3d586e88058d15e1762800008085034b10ee43a003a4493dbfa34810445419bea223d56ef71737f28a1c821a46862b173c3a06e8a01fe9f6643b5054a85e45ae0413118dc1c0de71ead60704241fa40ed8ecb432b9",
|
||||
"0xf8738362024984773594078252089415e6a5a2e131dd5467fa1ff3acd104f45ee5940b88058d15e1762800008085034b10ee44a0c8c7a7b76c6525400c1ce5d7dba14548060d36d2bc4bd846a7a7425217d66957a025449e02d5bcf26d422dfbf41bae2de5e8468d7399043c12da8bbb7f5fb02eed",
|
||||
"0xf8738362024a84773594078252089480c4c7125967139acaa931ee984a9db4100e0f3b88058d15e1762800008085034b10ee43a01ff07f88e3ef809a7326f7912323ddbed12184b573230d4f269969a48602a5aca052831c32b936474c7e200297135e951b8948130a61f0064ef5bce0fd8ff421f2",
|
||||
"0xf8738362024b847735940782520894d08a63244fcd28b0aec5075052cdce31ba04fead88058d15e1762800008085034b10ee44a01fd39c5fc58bff2729959025ef99dda0614160f06289efc56e6ccb0b67bf209aa002ec26712f5595db24e57dbef108c3f4e8b1863d71dc0a6746d944c10353f209",
|
||||
"0xf8738362024c8477359407825208940b06ef8be65fcda88f2dbae5813480f997ee8e3588058d15e1762800008085034b10ee44a0095291ad41e987063ba52692f06dbb29612393dcb40ae89ee3571e660c65db81a07a59182d51c5d07fcd8fc48adf3667a50d9b39185619c52938c741303603609e",
|
||||
"0xf8738362024d8477359407825208941cb96c5809da5977f99f69c11ee58bae5711c5f188058d15e1762800008085034b10ee44a065c2371b3cded7aee9ffbd9383f86970b06b191d93b1485991f3d25686d86c38a0067905277c21e7402e8af63e6ae6b577d9c669db4a78f61c887cb7bd4c8b117a",
|
||||
"0xf8738362024e8477359407825208942aa48ee899410a6d97c01b0bb0eeaf1771cc435b88058d15e1762800008085034b10ee43a099694c115ac14278bbc982204081457ec7b12404a86010dfc6877b87a3571bffa0117d6a284e25bda56b7fa75d26ac4429c38659a8f339280f1b7df051fcea84ee",
|
||||
"0xf8738362024f84773594078252089407b9d920dd8e8d83dc1125c94fc0b3cdcdf602fb88058d15e1762800008085034b10ee43a0b7068fc0a17b7f6dbef5a97f10202748f9f45be2d47a54658d0c4d7da0f0d653a03362d98c11472ab5dad42e39de5175cf756d9fe022a8d0628f5d6da7b5b7525c",
|
||||
"0xf87383620250847735940782520894fcb6e353ad4f79245c7cb704abcffe2f4868424188058d15e1762800008085034b10ee43a0ed028a4990f492396d8e86f9b0b58f934a8ee54938cbed8f83d68c29752f0bc3a062ce81c4edca14179ec5ce4d046e5d612c23d59f6744599bfd6ec71deb4ce2cf",
|
||||
"0xf873836202518477359407825208940d3de4256d6322683fdea9ee23765ccbfcb83da488058d15e1762800008085034b10ee44a026d858e4cee789dfedcce3d7f52c8b1e5553a59c41894652dad1fec3f2dfe7f6a04935cb6ee45e295ecbd0d8ece2860f6697786d439c1f8e066e8ceca802bc705b",
|
||||
"0xf873836202528477359407825208946021752d8d9b2f221d4fea4349dea34ddbcfce5088058d15e1762800008085034b10ee43a0d68f1b33afc775f5ee8e35d72a2abfa5966f6fc6f24cbc74498cc49163554898a02c8fbf6de58c657a47db2deb478659bcc31d92ab1697b336169010d3d937409d",
|
||||
"0xf8738362025384773594078252089461e296d527edc89e831cf593ec341f16197eeafb88058d15e1762800008085034b10ee44a0cbf42d8ee53861132879567876c2d3d4361a2bde5a8a03d673e8a9e02e2f2198a0315ae79bef63a5105373ab4bc82f4d981674ddf22f555a414394252de873bc7a",
|
||||
"0xf87383620254847735940782520894cf7317ee7a3b497ecf634b94bff60ff91b92574788058d15e1762800008085034b10ee44a033f4bfc7c5f798824317332a8e1cbc5dc6c3008aa09cb66dd090aa3c2a5cca1da013d43f1901fe898eab79bf8491a7eeb84373a187ec4d181ff1d2e5815cdce8f7",
|
||||
"0xf873836202558477359407825208947e7b519df31f77ced83eea1b16aedb6dcb0f0b2488058d15e1762800008085034b10ee43a0268a7ead7124d86048cc864fc1ae417c5bc72d569c506a08bfd95abd1a3869eea02ca3d7363cb6f6507d81f070784cb74edb060228c93f740815fe96aa7d884574",
|
||||
"0xf8738362025684773594078252089488a075e0fb1c9309a200a8bf0a88b214bf7ceb8d88058d15e1762800008085034b10ee43a064a8c228105a6a32632607e256ebb266a1b4ee37fd624ffc1f1ba939cef76066a0221b7fdbf2e6eba0742990b2b6d715f813599ec7ff01cd839901424230e5b0eb",
|
||||
"0xf87283620257847735940782520894c8d7cfb58f3ac02568e6505bf3fb5eb6f080703988058d15e1762800008085034b10ee449f9df05b5989b41018aae438d7cf6eaab197ac212e434f3c4d97b08a2490b802a02732feacef45e05a73bbf32caf062f0d84bc2daaef88a9527b1ffbcb44a17a21",
|
||||
"0xf87383620258847735940782520894e0132e8d7b1b766e0ade5543d6c6c0b2d5a2f01d88058d15e1762800008085034b10ee43a029a831ae211d616087569490c670fe44a681465a30144626e9787ea7174d0e01a05fc1c4037e8b7ffc15ced7750833ec8771f1b4d65d1e68381c68cf9523e44c98",
|
||||
"0xf87383620259847735940782520894eb674c0411db79654afdc1e131f3b6e734baee6c88058d15e1762800008085034b10ee44a01064ff856cbb0bfbcc61383cae3aed541f6fe1e011e86bb842efd25e88429539a046a0b8db35289c2285d917ec68ca78cc2ee7492d8ef7c1e11e63b64d8530c121",
|
||||
"0xf8738362025a847735940782520894dc07c60993cf689438b8c85f86b0ed938dca77ea88058d15e1762800008085034b10ee44a06f9e81bd4cdb2847e729b1ef1a331a663fc7531ed74a09e59ad02920db531f2da058e52df9092b08b45e4a9b961f431af4dfbb0bd86a48cc162c3e2f0d56d5658a",
|
||||
"0xf8738362025b847735940782520894110ddc93db59ed31a03518510221ec2f35d28f2f88058d15e1762800008085034b10ee43a02eade5940ccc8290dc553ef3027d086985b03a19cd97928e258c3f4f7b1523aca0549d6ef116c9932490e49144b0287b6bbb702584a17576291241ac15ffc94bb5",
|
||||
"0xf8738362025c847735940782520894b599a876aaac824cfce21bdf15627c9fd8634c3088058d15e1762800008085034b10ee44a03cd476a5d252e0b18d8d0809f26fe3871b62091cfb43d08dabd0c5874dce3590a063b5231c6c7190ac4e87985d997d46ef4c8bf3177953272fd1d0df9de95a506c",
|
||||
"0xf8738362025d847735940782520894d36e5540dd71acbd6416d60252c4d7c34a3c824588058d15e1762800008085034b10ee44a07d9bd59a5a4480d84722885e29cd6d33f1cf04a98dcd8e3dcf7321f0fbcbdd74a0354eadbc5e2cac079487f00d57f5fccbcea7936010a7509e6712f8ce39806dbd",
|
||||
"0xf8738362025e8477359407825208943adeca35af56206a74987a8fe13c669365c770cf88058d15e1762800008085034b10ee43a0a7df3bd9baf87afa5894782cf23385baebd8128d2ff7a1a971234ecf3e6ac7dfa03c811122700ebe14becfa56791483c58a1810dcb7cdd7b3ac3e2ff0b0e1e042c",
|
||||
"0xf8738362025f847735940782520894d77b95acd12f7b4b5692b55717b7bbca1165195488058d15e1762800008085034b10ee43a0d1b54826d1311a0903886ef61521910679cdaa3cfa70f14b672ea803a955c15fa05e881bdf984a03ba04042f26b46c82cb55662cb3eb23c40618e17cfe1e91d80a",
|
||||
"0xf87383620260847735940782520894f388bf5766b5ed5d4e1cbf15772e677dbfa80b0088058d15e1762800008085034b10ee43a0ab360b7a78ca54508f199cce95ea4c443482716205c32237b2be1dcfd1cb011aa02fc48513039fd6fa6bd608c9dfbb1965d2e15e70ab6cd67b781ba486bab2d093",
|
||||
"0xf8738362026184773594078252089435d4996296e58560e6ef47787d51b55f1e2bd92a88058d15e1762800008085034b10ee43a0621eee5a891fdd819305e26ee2a6152e752381b29f7e2af5597bca6ed0f9c441a0747cdd3922479fd516c01172d6562a238576486d0fa084b9f3e603f304b4edea",
|
||||
"0xf87383620262847735940782520894a4c3b77b898e53d6095f11c53a1ce272cff9af3188058d15e1762800008085034b10ee43a0a73f452201074e6cb8fb75a74da059a7e0928fed9ded27dd17bc4cfd93a22443a0674b3c5006a65687aa6802133fd29c77e68cef8a315175f677e9b6b0dbab56b6",
|
||||
"0xf873836202638477359407825208946e84f6113fc1919714f0266705813fb81a17181f88058d15e1762800008085034b10ee44a0f054fd8871770486554e18096985f02ee95c032b9be137a8e0026f619b55d2c1a02a4855957e29e3d20223cc41194d0be22f7ef7cf436df2783754147bff8a5c04",
|
||||
"0xf87383620264847735940782520894e9ae1a806004e1452baae0493920815aadd8479888058d15e1762800008085034b10ee43a0b475580a478780d91e262e2b1697d1faa216effee65d00d2b6357acb47b1ac54a015abc88732edd4be13376b6f910c5b5c6c1929210bf25f384a49fcb48aeb069f",
|
||||
"0xf87383620265847735940782520894fe1905d8ebd20e037274eef441283c811ea82c1688058d15e1762800008085034b10ee43a089466e0fb9624d16c977ac2d64276e4700c47ef9ff4c8cfce8c801109e8b0b77a05c6b093850d7e34da5d445e203900087275573e83681eda656e3638ec3661547",
|
||||
"0xf873836202668477359407825208946adece88e477f53a143a4c29d97940df2ec768e088058d15e1762800008085034b10ee44a071f1a5b69f6c8ae0393c4390adb7a3271f99af0c01524e277690ea199dcc2da1a03e887397647eea176bb4d7f7743a0f812a1223da3b465134e4f7b47a267379ef",
|
||||
"0xf873836202678477359407825208940d34d140a7376892c4593fcea3ae26f5d6f202d788058d15e1762800008085034b10ee43a044af0ef032fe68acae258743e7a913b65bb1bcecd04e3ca23ff4b1c35f8ea248a0180e9622e1cbc136d45607eed1371a21adec0f1e76c9f60ce48de13798462ba4",
|
||||
"0xf87383620268847735940782520894d1c7fa75b9bc55d041fcdf215f3e3a351c9f9edc88058d15e1762800008085034b10ee44a08b91903cf64c5ea1ef70728d84f3d6c3460e9b0373e39e25e5601a859578388fa059180173244f606fb86eb64187a39db3e6eea697b8eb725d44a348523e1a48c3",
|
||||
"0xf87383620269847735940782520894418ebe350a8c6387bf5e42f3502742af8e0781f188058d15e1762800008085034b10ee44a084bd185a547656a0e4d8cc84cb47b77509f857bd5de7d6c56182c143665bb7b5a068b10a9e4d24f54d7af772976d4c9806828af12e763207efa4c47e1352590719",
|
||||
"0xf8738362026a84773594078252089484914d2770c711d27888c775c547b1d933b48c4788058d15e1762800008085034b10ee43a0dd2129a3c3b56301435d67d5da0e8b4e5eaff46e6d30f49608c5b5d50c5e9dfba037b1663d9a8a20bf14f205817728934e3c6f0ec10642c2d24b54e46c0e3160b5",
|
||||
"0xf8738362026b8477359407825208948f51e560b85edf2e653c689c4e9fac02ce0556b888058d15e1762800008085034b10ee43a009ca07e74aa8d418f7888718ed9014b539c60efb053116aa8e02b0cfc9930e7da05eb1a866237575f7f7de2051f622a8e45192cf7ef57c143eeffac1a54dda4082",
|
||||
"0xf8738362026c847735940782520894ee2503205c24dc66346e356f13f333fb8782d35888058d15e1762800008085034b10ee43a0f02a886b4bd63232f0c1247c969d3d80c4881b5ea9fc1419564e38dd67f1831aa067f136cfa69edd0fced8be030956974d94a08e3ddbdaf29129e65ebb719dbdb9",
|
||||
"0xf8728362026d847735940782520894096ba6c59bd667a0fea9a356bcc988e4d9f2d8eb88058d15e1762800008085034b10ee43a0a6b9529f9e732c69e23688f6c5644dd7a73162e427eb9356a1dcf1759a6fee3b9fc127e4986c286784fde59cad6c3f1efd1c7f6d4f7e5088f5c7a74d386b0949",
|
||||
"0xf8738362026e847735940782520894da0adce4f1dc7debe7b2b52e8fe9ace6c7ea9c6688058d15e1762800008085034b10ee44a0734e8f035d7479da4857ce0e09202d497d831594ec24ff722106c8c7608963fea00a1f8a360c43f0b5fd58a71a2c2c5afe895aa5a15320b052baf1b44434cfe6ac",
|
||||
"0xf8738362026f847735940782520894af7d412aeab7525c0541dc3aa6c1085cfb8c909988058d15e1762800008085034b10ee43a099e288225e6dbbdc77753aa08f3efbb9f0b96d7def768d9854e855fe01610cd8a034f2d3aae0864b7afd5e691f33bc2d28083110f0111112936da8f45d98319630",
|
||||
"0xf873836202708477359407825208943cf8c0d567261eaf4ac0872d33a9f48af361769f88058d15e1762800008085034b10ee44a00f20fba4abb6ee417a1f0bdbf776b0d5dbaabc81ecaaba9c58f45360208e33c7a05dde3f3f6b92919e408a0aa72937c33523734a0a2cca772fa2147273d7be1c3b",
|
||||
"0xf873836202718477359407825208944779242587ba9e828999249eadd82984430f484388058d15e1762800008085034b10ee43a09b5e17f912c9fb7f803b43c2c1397b01b8ab205ff682a4423f0a8214a2e79197a0521997071cda90e04ca276220e6f7dcff723c5d27224528fa3601ef3a2221494",
|
||||
"0xf87383620272847735940782520894ea531cfe2de357ecff3855b88dbd07f60b03cdca88058d15e1762800008085034b10ee43a0022fc439ceef6778c85bcb9b86eca5cbfa6ac349baa54887159c8585af0e6ecba01080d90e6b555cccd88b693af79881d7f17247b01b3ca62c281c7ddf200ecc79",
|
||||
"0xf87383620273847735940782520894d00b5f53ea2a66ad33c3fee304bb22857dfb8a8788058d15e1762800008085034b10ee43a042e9e53473b2553d37080c3a8836b183ec8f3b41f47e9bbec02e3aebb7dcdfe1a070bafb99da6e92cf4090bff7eca3c3c8044cb46a9f05f544a0b56a521255d324",
|
||||
"0xf873836202748477359407825208947ead29f6616f78f21a951c9686dd257be7b8efe488058d15e1762800008085034b10ee43a0a04cae14d43584a0fed384fc6d981238e3cd2302a7fbdbc5245192068abed008a05aef32306b51b2320199e8f3f11280bae5f677b99907b6244ba892d1d441ec12",
|
||||
"0xf87383620275847735940782520894d503c13ee55c1ea128357d4018ec58d0d5e5c3db88058d15e1762800008085034b10ee43a0e280df73654670e3cf5ae5aeee1e1931e98cf29ff874ce7d32790f8233d5b287a01892f9b5e6902f7dde4411566c1ed3c499703b0801a0c4b1a9dc1b023d6443a8",
|
||||
"0xf873836202768477359407825208944ac670d8760faf780468638ef80034876ed8918d88058d15e1762800008085034b10ee43a0514dcddc1910947d09fa8a6c0e6f6cf7d05fb4d5bf158bda354d2e6c6348d581a0047981492e9b9eff0ea70bacfaecba069aa2b2c5269c8469cac42d9b11d19383",
|
||||
"0xf8738362027784773594078252089424ffb8c97ce443f8d3265ba3316defcfc07c659c88058d15e1762800008085034b10ee44a07fc741180d071dead819a84ba1be1f2eeb518caf707ceadeaad03ea9edecc80aa060eccb7fc130827b061d57c5fb3e168a30dccc1fd4ba6b69df65cf82f78a6bba",
|
||||
"0xf873836202788477359407825208940c5cafc547ab98c9ceaa1c07fdd6bf7820aeb95488058d15e1762800008085034b10ee44a0787e06914cf15a0d5c805c92ee953eba7efc3f9ba09ca62d0ed2f3b6167ffa60a014fa025423178902bb480a1823bd9cd17a55f7c2fedac8a42f81b834c4f5b547",
|
||||
"0xf87383620279847735940782520894db8d964741c53e55df9c2d4e9414c6c96482874e88058d15e1762800008085034b10ee43a0ee3e494f464ab6384f2a856e3e4a82b581b60dff1494c5d31d8ffac33133c4dda00fe28dc8352736880aeb0df3b34c846d9e48e056f0ee34115866abd2e4cb9cc9",
|
||||
"0xf8738362027a847735940782520894ba85bb35ae6ff7a34745993fcf92b9afd34124f188058d15e1762800008085034b10ee44a0697c551925faba103e19343af9b1373c15a915c94c2c2b2248754707ba6753efa0195134d1de635393b68258bbc3a6130da2f6f28e3a21f53cf7c0d309e8604a91",
|
||||
"0xf8738362027b84773594078252089458871015f5a2d3948264f7c16ad194c80ffd531d88058d15e1762800008085034b10ee44a065fd5136b42302cd12e08ce81d4bbbac0bedb774eb07df3ec89e10d1bd0e3eb3a053ef3a9a4b6b0fc0afc2aa8f0fd6eeeb0f8d79df5010395e68401b34a9c1b932",
|
||||
"0xf8738362027c8477359407825208942a90af45df70b0031f218cc122598ddf3e10469f88058d15e1762800008085034b10ee44a0b8318c8dc899f7a1c0c57fbe9199901c927788d51460a971328870dd05516748a02811349e21979f97393c1c0707f508bcc9edf7e8932a2058c62fc503c6e298f9",
|
||||
"0xf8738362027d847735940782520894761bbaaea6ceb265f5262c3b559adc2ad3ed2f0988058d15e1762800008085034b10ee43a0ee810426bf67a8873cc6dc0f41887bc6dcaff093a620b980db51343e80b828a6a05fdaf2729c1dee2029a5bc9174920d1bd16a253e2a20ab69fb4cce9a22bbcb44",
|
||||
"0xf8738362027e847735940782520894dfe86f51c5e603f1420d1f0ab366bd3bfe23d2a788058d15e1762800008085034b10ee43a053c6702a09db8bfbc3560bc380816ed7c119a95700fc9cc7cb44c631637876aba02bddd96beafe8592027d38a9596efed58b08361c863a905fabf4dac4b6fc22da",
|
||||
"0xf8738362027f847735940782520894d616547158b05ab5079106dc0336d72763a7287188058d15e1762800008085034b10ee44a05fce463f3ede6e6ea82f59b0e2f1356aa6cc9e4a2092186ff4eeac6144f3d70ea05870f706fea0bb3e813e3ecdacdaa82ad6234cf8cc051d15b0aa5fc041943aff",
|
||||
"0xf87383620280847735940782520894dc68cd278cb7f5f666ce7b0a3a214a8540ed4dfa88058d15e1762800008085034b10ee43a0e376459fbce80ca4941fdeb313efbe587a7567cf3a34c477df38b7409b043e69a03ba4f66d19838df4276344d1c44253d83789e1c77ee8a40d4c8c447789723386",
|
||||
"0xf8738362028184773594078252089411f8107da05b6905e8cc0227ca3b0c6eb764fac088058d15e1762800008085034b10ee44a03bcb59ba309b004c55ba3df3eec064e22fcfb5a57c8e5ca0ff146bda02774a75a07ed6d73ad8a0daa1f1c98430ad5e5fbdb4748554c8ffa0233638077e79deac03",
|
||||
"0xf8738362028284773594078252089404da906545679850a7ee0ef6836e183031bedc8888058d15e1762800008085034b10ee43a0094da8f36bc9c6ebb5f92ed23a48067f0efc5307094da7c5890a942f654748c2a05883676b2416160acf153351fc86c771a56bf4251aa134897a73cea3c3f4f7b2",
|
||||
"0xf873836202838477359407825208948bdc25c43c010fd3db6281fcd8f7a0bed18838e388058d15e1762800008085034b10ee44a0dac96e3d17998ae7886f80455c92444fc078f832b424e31c7c4d1cc1c09d5d2ba07104abf0bbd8433c357d0ae0d9ddfaa3e7d8dea15644912d51931b448bbfdada",
|
||||
"0xf87383620284847735940782520894af16f746b8a834a383fd0597d941fee52b7791eb88058d15e1762800008085034b10ee44a0294814d951013410c28b152df0361a6bdf245cc987ab30a7304251188641d609a05a530ee23d27f69768191325ffe65582ece63791bf09fe197f504fa57bfd5146",
|
||||
"0xf873836202858477359407825208940c5c736600f8ea58ccb89aa72e3f3634651fd55188058d15e1762800008085034b10ee44a0c1e93262c0f669d8dcafbe0b69d6fa444595b9991e0ab2d643ca82c32bb95779a07890187282502da60706b81047b42099ec2fcf12f967cada17ea595b8d2d50bf",
|
||||
"0xf873836202868477359407825208946f475e0f0e9eda58556fddc04de9b1a9b6a4cfb488058d15e1762800008085034b10ee43a02954b5ef28644f34235fa8a78138f8a20f451ee904bbcb557ee0d6a57aadebbda01e380d4925b3eeafe722cffec0811d4b546f2962dab2415247a609d017aa6830",
|
||||
"0xf873836202878477359407825208949b2e76498a695c4dc7d0890069cffa84a9581d2488058d15e1762800008085034b10ee43a0c4c1ad80c72b77662bdc60e5d9d2f8a7d4f7d5e591f7d36bf75792603c350a48a075948e3ef779497e000693a717ebd926dc2f1040a9fe108067b827470250cb5f",
|
||||
"0xf87383620288847735940782520894e2d2b2069f4a54fcc171223ff0c17adbd743c28588058d15e1762800008085034b10ee44a0c081205b252b273039c2e97df9831d3f3660a5d9c6432de2d9005d24bc1c949fa015a60d2b5a33cbc1d41626d844ab093695cffb1d445a7059bc30a0ee70130bba",
|
||||
"0xf87383620289847735940782520894386bd49f04322544f3c7178fa5ae1a24b947b45488058d15e1762800008085034b10ee44a02a76823c284c6d383ce1605f433ee24e9d25755d9914355e7ec0a2e747a31aaca023bfe68b343ec5da856c221a08e42325e2fd465cafa52872885e471e87a26783",
|
||||
"0xf8738362028a84773594078252089400af839c3fc067fafc2e0a205858d6957f0dd18d88058d15e1762800008085034b10ee43a02dd6ac183d86166f2d48f9e5798998beb2492203130dc29a8ccd60d682c68241a060f45d781a16584462adde0ca311cdd99a0c46c8e96c772e10dec6e21958ca64",
|
||||
"0xf8738362028b847735940782520894ebb6d32a650afa9221b55a11c6a6de52b6f07cd788058d15e1762800008085034b10ee44a0d7d9588987d423ddf1180b35560d0cc8c350c4eddf3b4f04d491bd952d1ee939a07b95e674b777c204738e34c332a4c96e067d1fd530ca4e17196c9c8a2a84428b",
|
||||
"0xf8738362028c847735940782520894011d26a3a9adc9203c8943a6a77aa8657af5242088058d15e1762800008085034b10ee44a0780c40f1a71ed2631a01df292f842ee8a8dc98d1b80d0685d92ac22dfc2fffe2a051a4e8f7bae167c3124d47938c54639fff25e9b159507881f6b9d891eb50d805",
|
||||
"0xf8738362028d8477359407825208949c85bc61a89fb5abd957e6c819c653fc1aa0d11b88058d15e1762800008085034b10ee43a0f041d0b9537468ebf005b0fad79296b1b74e63af050e13f31999737130346deba02d50794faca4b4e1515287ae2690e4d7b7855f29a56a6d644f8c828b6d30a997",
|
||||
"0xf8728362028e847735940782520894bd8e8435b7897d87cf7cedb5cf8c5dd865dbf72088058d15e1762800008085034b10ee439fcc0821fc19eab1c1e6e15d7447aed84f079a3d178cbc9e865e6009a58b558ea006d296a4d03301ff9663336813b95368dd3553f88d2635ec7559416445f214b4",
|
||||
"0xf8738362028f847735940782520894adebee2e3ff041078b62380d001c6e51b4f1559888058d15e1762800008085034b10ee44a0e67f178fbd1a91014a87c50bf50898474c1fd80cc782aa05cc2d7efc427d5c13a045ddd298df67dfac72dab9b255066e81b4a1a7988e61b6c3b0318b788733e0fd",
|
||||
"0xf8738362029084773594078252089471e94c459c9f05085fc0d34b5f21e648e05dc6b388058d15e1762800008085034b10ee44a0f16ec0330912b815ea3ad0d955766eebe92031c7a2d64bf786b7f55bb4f63798a03d03f718b4de0f491a08315e6fac7fe9ee98098ceb3d27ec4128da1f4b4f8153",
|
||||
"0xf873836202918477359407825208947c1fe317db82c9298b87c56c3194178271b621e188058d15e1762800008085034b10ee43a089edd1266f626652a65dddd34bcecd33cf0975ad6664cec15b706910d41d23b0a055f7752a91aa4759e5c84a4bb2180ac78810ef99246e52e8974fea996efaf8d4",
|
||||
"0xf87383620292847735940782520894e069d1c9abf5127bdc3a164fb93b96bfa9f74ce088058d15e1762800008085034b10ee43a00a809e015ed5159d60be66d8b65efa57f72be941e9928d889c6be888096896e6a0724239814c01e730b19dd093cee6384864964e59d34d84cf1e9a8116a11a7155",
|
||||
"0xf87383620293847735940782520894b9bbddd1eb6ef8fb1bdc6a853d5ad7486a9487dd88058d15e1762800008085034b10ee44a08717c8d1194e516264fb1ea3cb78b73f5dd507d17823eff675c97bf304a90b2da05a96d1bfd29677fa4e97fd37bc4e4962a9ab6f99fcf1c74bf4749a97adaf0e3c",
|
||||
"0xf87383620294847735940782520894a804387cdaf986d45831e8074efb2115af053f7a88058d15e1762800008085034b10ee44a00d92c86dad2165ddae28c5081638c3eea7a54bf7c15f75d2e3a0d52af40d3c97a043c833c190d2e78ce01fa96a331e475fc41f7b51622bf4f081e4831de49ba07e",
|
||||
"0xf87383620295847735940782520894f23501d784a041fc911b4c86c2bfb1f63ec170ea88058d15e1762800008085034b10ee43a05d27e028f1ef0555f022f214e222a33266971b98098643ba84578f70598781f0a0548bd8b2ba26f7f4b96bea6187981f5af5c36cbe0c57558d6144eb7590668a70",
|
||||
"0xf873836202968477359407825208943928be2a7058088313c0fb3294014e88a3c5ed4a88058d15e1762800008085034b10ee44a092c573d072e4fbd59fbe85d740fb99b1cc94a97c0896020b1eb4c50a0c1194b2a07edd4babced9ec70bb660fefae771aaf3244341da1e3123b42e6885a933c3d2d",
|
||||
"0xf87383620297847735940782520894196aa07204141478459c14106ef5e5282efe995788058d15e1762800008085034b10ee43a08d6b3fa7173e77e69d0c9a851396dd8b457d32e15a916082002f367acc0605bea06c7319e1eabe15b1887633080dac1be3e3611861e2ead80487a0858b24f2f9ee",
|
||||
"0xf87383620298847735940782520894763cbf89560e2da270000822abda9584db693fa388058d15e1762800008085034b10ee44a06a92950880b902296ea1c6d8ade6011d7249107a520c4935dee51dd59de41389a04701288ac10e1ede1468e197258e7b38a5157ed96cffdde2c99314fc9e64e260",
|
||||
"0xf873836202998477359407825208947feaea0ff70ffc9eec2104f57f7136aff4dea68088058d15e1762800008085034b10ee44a04d90fec69d1b3ffa4e21a81a12bde21223baf92be9c972ec983454d8271870a0a0159b7c2b5238c515755b3f7812afbb5d4726a957a9c9767c3d4c044f83274185",
|
||||
"0xf8738362029a847735940782520894e5466aacd9dd6d3bb35060a1ccc76a438de88ca188058d15e1762800008085034b10ee43a0837538d8e417194bed46f36eeba282fecbbb58bcbefc6f7e018c42a73ae81345a038b1d65f0d27ade5bf1d263fb67c758e76d1ef797cca936f3412e355074fb994",
|
||||
"0xf8738362029b847735940782520894f670980415cfe8c4f8d10645ecf974c9a2fea00e88058d15e1762800008085034b10ee43a0ac3f8bba9b19c4d9a3a0d03ed515f81da94b6ac72284b4d8a27c4922aae659c0a078c566cccfde53296c238d408e0ab3dd4a07df1199c12b673be05e45195bbc4e",
|
||||
"0xf8738362029c847735940782520894a29115bce7829ffdd989b7cf1bdd1eac06a2cb3688058d15e1762800008085034b10ee44a01edd6db1f63d1c384edf6bfc0c3253fb8bdf175c913b62fc5d0c76dd2c7bd411a004a5ceb0c53728dbefccd21e5342f25e90fc4e3de69661a96a2b6e8eabe99580",
|
||||
"0xf8738362029d8477359407825208948f528aa67dc1846c893465fa1c8c26556bc5fe1988058d15e1762800008085034b10ee44a0b8eaa2b524d2970d341996da7babffdaf92160c755e9b4ba6e453e800a93c5fea05b0bf41c26aefc1fa29539c34d32f42174fe27846ef4ea8427f9f64414ab2994",
|
||||
"0xf8738362029e8477359407825208944dc4ec6ac43c8c45777292db987203c0248e17b788058d15e1762800008085034b10ee43a061f4bd7053ee4bcc8d1a3edf748d9d3a585134c1fa61129951a094d5069705a6a03e45012ff902405e6c78e0e7d8be2abbc079b8c79af32022f3f6ded85a023a22",
|
||||
"0xf8738362029f8477359407825208940d2f39f251cb547cba567a31e5e9f93c19dffa8588058d15e1762800008085034b10ee44a0bcc76559b8384c5f31678d1efe17f1c7c9267ee4e50c688421fe269a18ff8ab5a07ededd086d4f210c05ac901c987a1b30106cd76e7cef0a6723bb1dc1b20ccd30",
|
||||
"0xf873836202a08477359407825208949eb31fb94ce5111e2a04cb9d156b513887ccbd0088058d15e1762800008085034b10ee44a0deb981e565672e01dcc8cd6514c459fd3936380d87f7fadb2660f7c350ac2e51a014849dcb352073dc9663c0a289710145efd7428cbe0c88213611432761ad93b9",
|
||||
"0xf873836202a184773594078252089404b88ef83f8c41b1465d360a1e82f07ae190892a88058d15e1762800008085034b10ee44a07e3e84ca883d7d8d4e51f36454609b4e6b313a98ea8d226d47682612ae87f812a052687dd82b4f62e8449f7c0034b950e82c78aab8a5518592ba5b652c72b36017",
|
||||
"0xf873836202a2847735940782520894af23e04b04fbe15630eadd32a6f27a5a65ea554a88058d15e1762800008085034b10ee43a0b90c86c8c6d5254ebdb8b82640ab629ccd6eb9acebfcf53b931949872d9aa6d9a02cbee4611a01bc2d694b3a3b86070ef762b497fbb2b9a9c3bca6b8c0112f97d4",
|
||||
"0xf873836202a3847735940782520894746cdff371e3f1e905b3ac52280078bac2dec7dd88058d15e1762800008085034b10ee44a0751ea5aa4636525bec891fd848390f6e032587d1bbda675c0e393e410844a1d6a032fdeac6961819450d3059c61f97b60f5cd50e27a18c392539719af3f194f53f",
|
||||
"0xf873836202a4847735940782520894c33e5155bdbf1a0a7ceb1b80f8586c5cda5c378188058d15e1762800008085034b10ee43a02fb25f3cb803a573ca57d7782a90267f5e2e92fd018f01fd7c5152beb3e2e179a0732710f218d2b8fa87143dd9c10e239aa7269da3dd08ed081cdef99fc28f2561",
|
||||
"0xf873836202a5847735940782520894e7fdef5f5219068f3d0f88a7445005574c66279888058d15e1762800008085034b10ee43a0f06655172c1d09a30c06930a24caadcd736beba5b63aa137ef0701e57820bceaa01821754fd8180b0ff357c500232f2008e703d2ac6b6fe0a14c202a1c039bdcc7",
|
||||
"0xf873836202a6847735940782520894f0a81a63c5e09b0bd08e027de48058e377d3732d88058d15e1762800008085034b10ee43a0443ce407a5a49665e5b7847293b62446a2bf6d5885309b75c72c93cc715f8202a05151017b926df1bcfff2c3309e4771f2310b5ed0ded4503e6c4e57d78cc2b380",
|
||||
"0xf873836202a78477359407825208949878ab34dc3b4a63c80fdb733491472c11d59a5688058d15e1762800008085034b10ee43a084ae376d4ecb7d0e23df6dd426999005f05fbcfffb8e1916d13e53f492fa058ca03b42dfaf1fcff5912e20d3bc9d0e26f61ca37a0c2647a62d20e443db23ebf400",
|
||||
"0xf873836202a8847735940782520894912859bebae3086ac7a062dee5d68aa8ed2d71ec88058d15e1762800008085034b10ee44a0e9172415691d94476adc2c2fb0b5d68285ce83ab024dc53166f870decec3a4eba07e5f2f8a52145948789e57e3311da5edcbf563307ff0987342424f4e23055673",
|
||||
"0xf873836202a98477359407825208945a0b737ed85049410e5ea61f444d07d5c8c0359f88058d15e1762800008085034b10ee43a0db10a0538cfe66a2152e6ed531eb5be016ec5f1cead127b91b8f123e54efd8e3a046c6477c90bae429b65e594b254a396bfddfdc15e1e3cec9ffa26a46a4884ccf",
|
||||
"0xf873836202aa847735940782520894305a5dfd46e6128abce28c03b3ad971f4e4915ff88058d15e1762800008085034b10ee43a074cf4ff900d6170b4266deced030c14cb92d9872837e9cbae3b768c3f561d1c8a0252ba383431d7108ac2402bc6f8f807177496bd9997643994a9c33dfeda303b4",
|
||||
"0x03f901198501a58877108216c784773594008477359407830186a094dba34be211bf539e27107cbe7e6893770280aec580b880297fb846f12a980aebe1d5b1187b2e6660f3e68f457379b59395a0411b9859c25842606f527f16e565e886e3afcd0ede18e04e3c827adf9e5c54db37870db32c31a18813d44c608f5260b660af53605b60b053605760b153603060b25360f060b35360e760b453600060425d600060c95d600060895d600060e95d7f25818248c0830f4240e1a001b7f74a7d5bae21aaf16e7b2d2ca7f47c800636b2a3f8b519aabb4b3541708801a0faebdfbd5a7832abed65c7fe25ffdb498b0c19c70cd53a1ec53e3de6c25ca0eaa03690227cf2f99e38b48a44f739fd63b00482017bee063657b418d85e68cd2094",
|
||||
"0x03f901198501a5887710823ba784773594008477359407830186a09409fc772d0857550724b07b850a4323f39112aaaa80b8800a7f9b44ddad8f17bce75dceb28c1eafa80fbfaf4e0d793fc9fc42489c913ffec7906023527f0d1debc2884eec1a57d882e7085ae952d5d76b3e168370cc9af6294ccb55a7ca604352606460635360d96064536042606553603a60665360f1606753609160685360cb606953600c606a536055606b536043606c53603b606d53c0830f4240e1a00159f296fad44019f3cfa307df01412afa1643a67ccafae96c83d43b6758ffe080a04b6af5cfdca885787b508751e15ebeddc4ae4f5e88035002a72fe720ed9c1808a07781ed6c6f1778c74f692c9b966d1c27185045042cf386eab20543852afd7d9f",
|
||||
"0x03f901198501a5887710823ba884773594008477359407830186a094000000000000000000000000000000000000000080b8801660f660ce53604b60cf53601260d053600b63df83d68853600263df83d68953604f63df83d68a53601b63df83d68b5360b263df83d68c53601363df83d68d5360f663df83d68e537f93ad362c9daf922157be015981847c3d63de3a139a88e7824d2c62693233acba60e4527fb97203d3cc1f05cb5b3a18ba38b93d6eae2675c0830f4240e1a00125c14e94773c0f2504208d25925d0def274e4cd05be154b64484cd55068b9401a01f25239f9cf530e73210788333c06832ede4d25811fbd5288f1c6ec09626a848a0142054f5501636d093d1f8fd9a18991a035ff8272ccc4b732e618929c5ae60a1",
|
||||
"0x03f901198501a5887710823ba984773594008477359407830186a094000000000000000000000000000000000000000080b8807f1c1e1707ec5a3c08643377106467f9d8cf7343cddde951134184de69c4a0ab5f60cd527f496d383985b30e2192e37082533c4040429f17d2d8066cbbb0df4ce5f1bcc72760ed527f934a6a5a54f24b814ed3df79092d3215e14b5fe7c1ae730e377b17777e7d3afc61010d527f324e451356f8780882a2abd81a56fa0ad656c0830f4240e1a0017fde88c70f2ed440c9dbdcee3973e4d7b85d3a65a7d3db7289a2a16db2f00480a0064a83db9cfa03f6af8b2e48c5b383d57f3de72c1a556bdb89f84ac5a33ef294a07a99426dd140e3f729bf681e9493c93b9f5466afab8bd43b08d44b83121e2c66",
|
||||
"0x03f901198501a5887710823baa84773594008477359407830186a0947a40026a3b9a41754a95eec8c92c6b99886f440c80b8806000603d5d6000605e5d7f2a4466d55bb88fa289d012628a52864aca1c1e1707ec5a3c08643377106467f9606d527fd8cf7343cddde951134184de69c4a0ab5f496d383985b30e2192e37082533c40608d527f40429f17d2d8066cbbb0df4ce5f1bcc727934a6a5a54f24b814ed3df79092d3260ad527f15e14b5fe7c1ae730ec0830f4240e1a00122ad5c899f52d21eba94a21044753587639cf91301db410de58f73d740feef01a02d622199b408f02048f237a3cee8de1cbe7c79de5fe0342f1a6cba7d9d5c53cea012cae1d118329a17eb3cef8d2a24ce1a96afdb6ef7c0fd58f11041f9ebb458a6",
|
||||
"0x03f901198501a5887710823bab84773594008477359407830186a09401abea29659e5e97c95107f20bb753cd3e09bbbb80b8807f1fcc4bfabba1d810c2e9750fe5f9f3c70adf76d7a54bdf886617f1a099584d2860d8527f410b6d3e2670b1d7b668f22f9b6d1e5bb1b4d12d4bc198e2b642cd9eef1ebf4e60f8527f753d66f7de6d60873b96cac74055a2c05f1822dd662fe1e18b5962e191d8cc88610118527ffd19563f4930341b9ae50f37f40c3519b4a8c0830f4240e1a001ff78fe5af972c7cce1be875aba37ff8b7367ab34a0879d0fde2873c1c34e3301a013580cd6864dabb7ae62de736772696075a507b26d352eecea97581662b11762a03a47f80d8b09c0ed5686c0082aeaa569bd7200bb6cd4a89507c391da4aae8882",
|
||||
"0x03f901198501a5887710823bac84773594008477359407830186a09401abea29659e5e97c95107f20bb753cd3e09bbbb80b8807febcd375a4548f911bbf94fb20a71723f2bcdf90b1893dede0d86ea0086c94f4663ded9bec5527fa75fc1bbd45e9a2357080a88f65a12c678ad697d56a558ddc54d4bb74c6c6d2663ded9bee5527f75eaa5150042917e755fc935fc3a8af87bf91944c7e3f00eebbf3a6574e80f4663ded9bf05527f27014ac42049105c03fbc0830f4240e1a001466f18955590485e681c566fab7923ffc11c44febd37dfa460e4ae57b2bcc301a08ac71347df4693d6fd98be1cfbdc5b7cc1ddc75f2512d1b85df24c9e1c754563a02145852bc429244eb4311fa25b529d4245e0a1b79581e829e251f92edaaf209a",
|
||||
"0x03f9013b8501a5887710823bad84773594008477359407830186a09409fc772d0857550724b07b850a4323f39112aaaa80b8807f9e664ccecd377f7cccdc8eacb44927d06ad21fa62b767c61a984a997b503e76a6021527f9884bb7e3c5262eabd6dc6f0daaf38581cc4e7387f25237ef70b3765d9f7f69d6041527f263c0b0f9697916d24f51e6443f9ddc9c00ee8795c6563b153305d4e889ff9336061527f3a7664aab6aa9ff375049c342739cf230af714c0830f4240f842a001148d696bf857b798a51d3cf76e1f30c0ff18dbdb67b4e4651ceb9a81bfe319a0014516a223e4dd099c56e3320d0f6b50e061e00a6548964a52ac15bba57028ed01a05077e103e2aabb0f6f8712c96fdee81a4a4b8b53404286535eb188b02be775dda021063c5abd0394b12c6935dd209bdc413472f558539469806e5baffeea1d00ef",
|
||||
"0x02f8a98501a5887710078459682f008459682f0e830137a19400000961ef480eb55e80d19ad83579a64c00700201b83882586e0c10f84614733e5752428cbbe809e3699d9a3bc5bc604e81169e6a59304352237e39c8e6644acae5f595f1a8640000000001312d00c001a01d67ab7c6569f238adca00963a31b5bd856960affb9d738ceb78085800ba29e1a0495ad28ef784fb388e2145af0778bfc08386cb0695d1c0ca1022fabff8ccd9a9"
|
||||
],
|
||||
"withdrawals": [],
|
||||
"blob_gas_used": "1179648",
|
||||
"excess_blob_gas": "68812800"
|
||||
},
|
||||
"bls_to_execution_changes": [],
|
||||
"blob_kzg_commitments": [
|
||||
"0x82b6012a3307f01e0ec06ce5768f093c75b731e81aa16f08afe60e2e457e2f4370f13ea15baac9c10ad8956cb1599f47",
|
||||
"0xa758a4d6c5dd35ec363c80f21dbee519db19aa0f69c1318b70dc01738555b97f7607793d072946019e4050b8b8e5105b",
|
||||
"0xa49b025f2a1c657f4c07df7cb04d430aeafacdc1cf9afaf841cdcb019ba3f61d6151241fcda0b2851f9d5adcd2a67bdb",
|
||||
"0xb909fcb4f35d2bdb684643f0dc3fbaae73024b7d2e771656cc0117fe34143aa29d882454285d340dbb1c6dcd1cff2d0c",
|
||||
"0xb9c826af3dbc163bddc7bcd0230b87f57193bf567ee7fa35fc17f970dfe620dee074c61776fb37fc19958996e5cf585f",
|
||||
"0x8ef3c784f8070d96f4a834640b6d76e589508ac121177a323f49b3a1073b9592d23464487a31a36974c8e571d8573d46",
|
||||
"0xa8ddab3b3301512e74e7d83b472af200f35d4b8a4df6ea783363035e2250a90ba473f31ea382c4c275e9c37621098dd0",
|
||||
"0xa33e2ae43573502a30c5f98a9a33c8a6879333f96d24fdff686544fd01b99130114d741e076ff873fb649fe44ab5e81d",
|
||||
"0x8f01d73cad9ce715598a653f48e9b9003abf2d2084890fc722c1eab65a4678146e81618b84d227652513723f4088cda3"
|
||||
],
|
||||
"execution_requests": {
|
||||
"deposits": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"source_address": "0x4b6C4667921A132eE6eD26a2DE7654adaE481E4d",
|
||||
"validator_pubkey": "0x82586e0c10f84614733e5752428cbbe809e3699d9a3bc5bc604e81169e6a59304352237e39c8e6644acae5f595f1a864",
|
||||
"amount": "20000000"
|
||||
}
|
||||
],
|
||||
"consolidations": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ func discv4Ping(ctx *cli.Context) error {
|
|||
defer disc.Close()
|
||||
|
||||
start := time.Now()
|
||||
if err := disc.Ping(n); err != nil {
|
||||
if _, err := disc.Ping(n); err != nil {
|
||||
return fmt.Errorf("node didn't respond: %v", err)
|
||||
}
|
||||
fmt.Printf("node responded to ping (RTT %v).\n", time.Since(start))
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@ func discv5Ping(ctx *cli.Context) error {
|
|||
disc, _ := startV5(ctx)
|
||||
defer disc.Close()
|
||||
|
||||
fmt.Println(disc.Ping(n))
|
||||
_, err := disc.Ping(n)
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,9 @@ var stateTestCommand = &cli.Command{
|
|||
Flags: slices.Concat([]cli.Flag{
|
||||
BenchFlag,
|
||||
DumpFlag,
|
||||
forkFlag,
|
||||
HumanReadableFlag,
|
||||
idxFlag,
|
||||
RunFlag,
|
||||
}, traceFlags),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ var (
|
|||
ArgsUsage: "<genesisPath>",
|
||||
Flags: slices.Concat([]cli.Flag{
|
||||
utils.CachePreimagesFlag,
|
||||
utils.OverrideCancun,
|
||||
utils.OverridePrague,
|
||||
utils.OverrideVerkle,
|
||||
}, utils.DatabaseFlags),
|
||||
Description: `
|
||||
|
|
@ -103,11 +103,12 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
|
|||
utils.StateHistoryFlag,
|
||||
}, utils.DatabaseFlags),
|
||||
Description: `
|
||||
The import command imports blocks from an RLP-encoded form. The form can be one file
|
||||
with several RLP-encoded blocks, or several files can be used.
|
||||
The import command allows the import of blocks from an RLP-encoded format. This format can be a single file
|
||||
containing multiple RLP-encoded blocks, or multiple files can be given.
|
||||
|
||||
If only one file is used, import error will result in failure. If several files are used,
|
||||
processing will proceed even if an individual RLP-file import failure occurs.`,
|
||||
If only one file is used, an import error will result in the entire import process failing. If
|
||||
multiple files are processed, the import process will continue even if an individual RLP file fails
|
||||
to import successfully.`,
|
||||
}
|
||||
exportCommand = &cli.Command{
|
||||
Action: exportChain,
|
||||
|
|
@ -212,9 +213,9 @@ func initGenesis(ctx *cli.Context) error {
|
|||
defer stack.Close()
|
||||
|
||||
var overrides core.ChainOverrides
|
||||
if ctx.IsSet(utils.OverrideCancun.Name) {
|
||||
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||
overrides.OverrideCancun = &v
|
||||
if ctx.IsSet(utils.OverridePrague.Name) {
|
||||
v := ctx.Uint64(utils.OverridePrague.Name)
|
||||
overrides.OverridePrague = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
|
|
@ -319,6 +320,9 @@ func importChain(ctx *cli.Context) error {
|
|||
if err := utils.ImportChain(chain, arg); err != nil {
|
||||
importErr = err
|
||||
log.Error("Import error", "file", arg, "err", err)
|
||||
if err == utils.ErrImportInterrupted {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,9 +183,9 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
|||
// makeFullNode loads geth configuration and creates the Ethereum backend.
|
||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
stack, cfg := makeConfigNode(ctx)
|
||||
if ctx.IsSet(utils.OverrideCancun.Name) {
|
||||
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||
cfg.Eth.OverrideCancun = &v
|
||||
if ctx.IsSet(utils.OverridePrague.Name) {
|
||||
v := ctx.Uint64(utils.OverridePrague.Name)
|
||||
cfg.Eth.OverridePrague = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ var (
|
|||
utils.NoUSBFlag, // deprecated
|
||||
utils.USBFlag,
|
||||
utils.SmartCardDaemonPathFlag,
|
||||
utils.OverrideCancun,
|
||||
utils.OverridePrague,
|
||||
utils.OverrideVerkle,
|
||||
utils.EnablePersonal, // deprecated
|
||||
utils.TxPoolLocalsFlag,
|
||||
|
|
@ -86,6 +86,7 @@ var (
|
|||
utils.SnapshotFlag,
|
||||
utils.TxLookupLimitFlag, // deprecated
|
||||
utils.TransactionHistoryFlag,
|
||||
utils.ChainHistoryFlag,
|
||||
utils.StateHistoryFlag,
|
||||
utils.LightServeFlag, // deprecated
|
||||
utils.LightIngressFlag, // deprecated
|
||||
|
|
|
|||
|
|
@ -55,13 +55,16 @@ const (
|
|||
importBatchSize = 2500
|
||||
)
|
||||
|
||||
// ErrImportInterrupted is returned when the user interrupts the import process.
|
||||
var ErrImportInterrupted = errors.New("interrupted")
|
||||
|
||||
// Fatalf formats a message to standard error and exits the program.
|
||||
// The message is also printed to standard output if standard error
|
||||
// is redirected to a different file.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
w := io.MultiWriter(os.Stdout, os.Stderr)
|
||||
if runtime.GOOS == "windows" {
|
||||
// The SameFile check below doesn't work on Windows.
|
||||
if runtime.GOOS == "windows" || runtime.GOOS == "openbsd" {
|
||||
// The SameFile check below doesn't work on Windows neither OpenBSD.
|
||||
// stdout is unlikely to get redirected though, so just print there.
|
||||
w = os.Stdout
|
||||
} else {
|
||||
|
|
@ -191,7 +194,7 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
|||
for batch := 0; ; batch++ {
|
||||
// Load a batch of RLP blocks.
|
||||
if checkInterrupt() {
|
||||
return errors.New("interrupted")
|
||||
return ErrImportInterrupted
|
||||
}
|
||||
i := 0
|
||||
for ; i < importBatchSize; i++ {
|
||||
|
|
|
|||
|
|
@ -233,9 +233,9 @@ var (
|
|||
Value: 2048,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideCancun = &cli.Uint64Flag{
|
||||
Name: "override.cancun",
|
||||
Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting",
|
||||
OverridePrague = &cli.Uint64Flag{
|
||||
Name: "override.prague",
|
||||
Usage: "Manually specify the Prague fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideVerkle = &cli.Uint64Flag{
|
||||
|
|
@ -272,6 +272,12 @@ var (
|
|||
Value: ethconfig.Defaults.TransactionHistory,
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
ChainHistoryFlag = &cli.StringFlag{
|
||||
Name: "history.chain",
|
||||
Usage: `Blockchain history retention ("all" or "postmerge")`,
|
||||
Value: ethconfig.Defaults.HistoryMode.String(),
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
// Beacon client light sync settings
|
||||
BeaconApiFlag = &cli.StringSliceFlag{
|
||||
Name: "beacon.api",
|
||||
|
|
@ -1566,10 +1572,19 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
if ctx.IsSet(SyncTargetFlag.Name) {
|
||||
cfg.SyncMode = ethconfig.FullSync // dev sync target forces full sync
|
||||
} else if ctx.IsSet(SyncModeFlag.Name) {
|
||||
if err = cfg.SyncMode.UnmarshalText([]byte(ctx.String(SyncModeFlag.Name))); err != nil {
|
||||
Fatalf("invalid --syncmode flag: %v", err)
|
||||
value := ctx.String(SyncModeFlag.Name)
|
||||
if err = cfg.SyncMode.UnmarshalText([]byte(value)); err != nil {
|
||||
Fatalf("--%v: %v", SyncModeFlag.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.IsSet(ChainHistoryFlag.Name) {
|
||||
value := ctx.String(ChainHistoryFlag.Name)
|
||||
if err = cfg.HistoryMode.UnmarshalText([]byte(value)); err != nil {
|
||||
Fatalf("--%s: %v", ChainHistoryFlag.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = ctx.Uint64(NetworkIdFlag.Name)
|
||||
}
|
||||
|
|
@ -2086,7 +2101,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||
gspec = MakeGenesis(ctx)
|
||||
chainDb = MakeChainDatabase(ctx, stack, readonly)
|
||||
)
|
||||
config, err := core.LoadChainConfig(chainDb, gspec)
|
||||
config, _, err := core.LoadChainConfig(chainDb, gspec)
|
||||
if err != nil {
|
||||
Fatalf("%v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ func (s *filterTestSuite) filterFullRange(t *utesting.T) {
|
|||
}
|
||||
|
||||
func (s *filterTestSuite) queryAndCheck(t *utesting.T, query *filterQuery) {
|
||||
query.run(s.cfg.client)
|
||||
query.run(s.cfg.client, s.cfg.historyPruneBlock)
|
||||
if query.Err != nil {
|
||||
t.Errorf("Filter query failed (fromBlock: %d toBlock: %d addresses: %v topics: %v error: %v)", query.FromBlock, query.ToBlock, query.Address, query.Topics, query.Err)
|
||||
return
|
||||
|
|
@ -125,7 +125,7 @@ func (s *filterTestSuite) fullRangeQueryAndCheck(t *utesting.T, query *filterQue
|
|||
Address: query.Address,
|
||||
Topics: query.Topics,
|
||||
}
|
||||
frQuery.run(s.cfg.client)
|
||||
frQuery.run(s.cfg.client, s.cfg.historyPruneBlock)
|
||||
if frQuery.Err != nil {
|
||||
t.Errorf("Full range filter query failed (addresses: %v topics: %v error: %v)", frQuery.Address, frQuery.Topics, frQuery.Err)
|
||||
return
|
||||
|
|
@ -197,7 +197,7 @@ func (fq *filterQuery) calculateHash() common.Hash {
|
|||
return crypto.Keccak256Hash(enc)
|
||||
}
|
||||
|
||||
func (fq *filterQuery) run(client *client) {
|
||||
func (fq *filterQuery) run(client *client, historyPruneBlock *uint64) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
logs, err := client.Eth.FilterLogs(ctx, ethereum.FilterQuery{
|
||||
|
|
@ -207,10 +207,13 @@ func (fq *filterQuery) run(client *client) {
|
|||
Topics: fq.Topics,
|
||||
})
|
||||
if err != nil {
|
||||
if err = validateHistoryPruneErr(fq.Err, uint64(fq.FromBlock), historyPruneBlock); err == errPrunedHistory {
|
||||
return
|
||||
} else if err != nil {
|
||||
fmt.Printf("Filter query failed: fromBlock: %d toBlock: %d addresses: %v topics: %v error: %v\n",
|
||||
fq.FromBlock, fq.ToBlock, fq.Address, fq.Topics, err)
|
||||
}
|
||||
fq.Err = err
|
||||
fmt.Printf("Filter query failed: fromBlock: %d toBlock: %d addresses: %v topics: %v error: %v\n",
|
||||
fq.FromBlock, fq.ToBlock, fq.Address, fq.Topics, err)
|
||||
return
|
||||
}
|
||||
fq.results = logs
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func filterGenCmd(ctx *cli.Context) error {
|
|||
|
||||
f.updateFinalizedBlock()
|
||||
query := f.newQuery()
|
||||
query.run(f.client)
|
||||
query.run(f.client, nil)
|
||||
if query.Err != nil {
|
||||
f.errors = append(f.errors, query)
|
||||
continue
|
||||
|
|
@ -81,7 +81,7 @@ func filterGenCmd(ctx *cli.Context) error {
|
|||
if extQuery == nil {
|
||||
break
|
||||
}
|
||||
extQuery.run(f.client)
|
||||
extQuery.run(f.client, nil)
|
||||
if extQuery.Err == nil && len(extQuery.results) < len(query.results) {
|
||||
extQuery.Err = fmt.Errorf("invalid result length; old range %d %d; old length %d; new range %d %d; new length %d; address %v; Topics %v",
|
||||
query.FromBlock, query.ToBlock, len(query.results),
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func filterPerfCmd(ctx *cli.Context) error {
|
|||
queries[pick] = queries[len(queries)-1]
|
||||
queries = queries[:len(queries)-1]
|
||||
start := time.Now()
|
||||
qt.query.run(cfg.client)
|
||||
qt.query.run(cfg.client, cfg.historyPruneBlock)
|
||||
qt.runtime = append(qt.runtime, time.Since(start))
|
||||
slices.Sort(qt.runtime)
|
||||
qt.medianTime = qt.runtime[len(qt.runtime)/2]
|
||||
|
|
|
|||
|
|
@ -108,8 +108,11 @@ func (s *historyTestSuite) testGetBlockByHash(t *utesting.T) {
|
|||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
b, err := s.cfg.client.getBlockByHash(ctx, bhash, false)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
if err = validateHistoryPruneErr(err, num, s.cfg.historyPruneBlock); err == errPrunedHistory {
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("block %d (hash %v): error %v", num, bhash, err)
|
||||
continue
|
||||
}
|
||||
if b == nil {
|
||||
t.Errorf("block %d (hash %v): not found", num, bhash)
|
||||
|
|
@ -127,8 +130,11 @@ func (s *historyTestSuite) testGetBlockByNumber(t *utesting.T) {
|
|||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
b, err := s.cfg.client.getBlockByNumber(ctx, num, false)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
if err = validateHistoryPruneErr(err, num, s.cfg.historyPruneBlock); err == errPrunedHistory {
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("block %d (hash %v): error %v", num, bhash, err)
|
||||
continue
|
||||
}
|
||||
if b == nil {
|
||||
t.Errorf("block %d (hash %v): not found", num, bhash)
|
||||
|
|
@ -146,8 +152,11 @@ func (s *historyTestSuite) testGetBlockTransactionCountByHash(t *utesting.T) {
|
|||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
count, err := s.cfg.client.getBlockTransactionCountByHash(ctx, bhash)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
if err = validateHistoryPruneErr(err, num, s.cfg.historyPruneBlock); err == errPrunedHistory {
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("block %d (hash %v): error %v", num, bhash, err)
|
||||
continue
|
||||
}
|
||||
expectedCount := uint64(s.tests.TxCounts[i])
|
||||
if count != expectedCount {
|
||||
|
|
@ -162,8 +171,11 @@ func (s *historyTestSuite) testGetBlockTransactionCountByNumber(t *utesting.T) {
|
|||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
count, err := s.cfg.client.getBlockTransactionCountByNumber(ctx, num)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
if err = validateHistoryPruneErr(err, num, s.cfg.historyPruneBlock); err == errPrunedHistory {
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("block %d (hash %v): error %v", num, bhash, err)
|
||||
continue
|
||||
}
|
||||
expectedCount := uint64(s.tests.TxCounts[i])
|
||||
if count != expectedCount {
|
||||
|
|
@ -178,8 +190,11 @@ func (s *historyTestSuite) testGetBlockReceiptsByHash(t *utesting.T) {
|
|||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
receipts, err := s.cfg.client.getBlockReceipts(ctx, bhash)
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
if err = validateHistoryPruneErr(err, num, s.cfg.historyPruneBlock); err == errPrunedHistory {
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("block %d (hash %v): error %v", num, bhash, err)
|
||||
continue
|
||||
}
|
||||
hash := calcReceiptsHash(receipts)
|
||||
expectedHash := s.tests.ReceiptsHashes[i]
|
||||
|
|
@ -195,8 +210,11 @@ func (s *historyTestSuite) testGetBlockReceiptsByNumber(t *utesting.T) {
|
|||
for i, num := range s.tests.BlockNumbers {
|
||||
bhash := s.tests.BlockHashes[i]
|
||||
receipts, err := s.cfg.client.getBlockReceipts(ctx, hexutil.Uint64(num))
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
if err = validateHistoryPruneErr(err, num, s.cfg.historyPruneBlock); err == errPrunedHistory {
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("block %d (hash %v): error %v", num, bhash, err)
|
||||
continue
|
||||
}
|
||||
hash := calcReceiptsHash(receipts)
|
||||
expectedHash := s.tests.ReceiptsHashes[i]
|
||||
|
|
@ -218,8 +236,11 @@ func (s *historyTestSuite) testGetTransactionByBlockHashAndIndex(t *utesting.T)
|
|||
}
|
||||
|
||||
tx, err := s.cfg.client.getTransactionByBlockHashAndIndex(ctx, bhash, uint64(txIndex))
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
if err = validateHistoryPruneErr(err, num, s.cfg.historyPruneBlock); err == errPrunedHistory {
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("block %d (hash %v): error %v", num, bhash, err)
|
||||
continue
|
||||
}
|
||||
if tx == nil {
|
||||
t.Errorf("block %d (hash %v): txIndex %d not found", num, bhash, txIndex)
|
||||
|
|
@ -243,8 +264,11 @@ func (s *historyTestSuite) testGetTransactionByBlockNumberAndIndex(t *utesting.T
|
|||
}
|
||||
|
||||
tx, err := s.cfg.client.getTransactionByBlockNumberAndIndex(ctx, num, uint64(txIndex))
|
||||
if err != nil {
|
||||
t.Fatalf("block %d (hash %v): error %v", num, bhash, err)
|
||||
if err = validateHistoryPruneErr(err, num, s.cfg.historyPruneBlock); err == errPrunedHistory {
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("block %d (hash %v): error %v", num, bhash, err)
|
||||
continue
|
||||
}
|
||||
if tx == nil {
|
||||
t.Errorf("block %d (hash %v): txIndex %d not found", num, bhash, txIndex)
|
||||
|
|
|
|||
|
|
@ -18,13 +18,17 @@ package main
|
|||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
@ -77,10 +81,31 @@ var (
|
|||
|
||||
// testConfig holds the parameters for testing.
|
||||
type testConfig struct {
|
||||
client *client
|
||||
fsys fs.FS
|
||||
filterQueryFile string
|
||||
historyTestFile string
|
||||
client *client
|
||||
fsys fs.FS
|
||||
filterQueryFile string
|
||||
historyTestFile string
|
||||
historyPruneBlock *uint64
|
||||
}
|
||||
|
||||
var errPrunedHistory = fmt.Errorf("attempt to access pruned history")
|
||||
|
||||
// validateHistoryPruneErr checks whether the given error is caused by access
|
||||
// to history before the pruning threshold block (it is an rpc.Error with code 4444).
|
||||
// In this case, errPrunedHistory is returned.
|
||||
// If the error is a pruned history error that occurs when accessing a block past the
|
||||
// historyPrune block, an error is returned.
|
||||
// Otherwise, the original value of err is returned.
|
||||
func validateHistoryPruneErr(err error, blockNum uint64, historyPruneBlock *uint64) error {
|
||||
if err != nil {
|
||||
if rpcErr, ok := err.(rpc.Error); ok && rpcErr.ErrorCode() == 4444 {
|
||||
if historyPruneBlock != nil && blockNum > *historyPruneBlock {
|
||||
return fmt.Errorf("pruned history error returned after pruning threshold")
|
||||
}
|
||||
return errPrunedHistory
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
||||
|
|
@ -98,10 +123,14 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
|||
cfg.fsys = builtinTestFiles
|
||||
cfg.filterQueryFile = "queries/filter_queries_mainnet.json"
|
||||
cfg.historyTestFile = "queries/history_mainnet.json"
|
||||
cfg.historyPruneBlock = new(uint64)
|
||||
*cfg.historyPruneBlock = ethconfig.HistoryPrunePoints[params.MainnetGenesisHash].BlockNumber
|
||||
case ctx.Bool(testSepoliaFlag.Name):
|
||||
cfg.fsys = builtinTestFiles
|
||||
cfg.filterQueryFile = "queries/filter_queries_sepolia.json"
|
||||
cfg.historyTestFile = "queries/history_sepolia.json"
|
||||
cfg.historyPruneBlock = new(uint64)
|
||||
*cfg.historyPruneBlock = ethconfig.HistoryPrunePoints[params.SepoliaGenesisHash].BlockNumber
|
||||
default:
|
||||
cfg.fsys = os.DirFS(".")
|
||||
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
|
||||
|
|
|
|||
|
|
@ -154,6 +154,10 @@ type CacheConfig struct {
|
|||
|
||||
SnapshotNoBuild bool // Whether the background generation is allowed
|
||||
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
|
||||
|
||||
// This defines the cutoff block for history expiry.
|
||||
// Blocks before this number may be unavailable in the chain database.
|
||||
HistoryPruningCutoff uint64
|
||||
}
|
||||
|
||||
// triedbConfig derives the configures for trie database.
|
||||
|
|
@ -231,14 +235,15 @@ type BlockChain struct {
|
|||
statedb *state.CachingDB // State database to reuse between imports (contains state cache)
|
||||
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||
|
||||
hc *HeaderChain
|
||||
rmLogsFeed event.Feed
|
||||
chainFeed event.Feed
|
||||
chainHeadFeed event.Feed
|
||||
logsFeed event.Feed
|
||||
blockProcFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
genesisBlock *types.Block
|
||||
hc *HeaderChain
|
||||
rmLogsFeed event.Feed
|
||||
chainFeed event.Feed
|
||||
chainHeadFeed event.Feed
|
||||
logsFeed event.Feed
|
||||
blockProcFeed event.Feed
|
||||
blockProcCounter int32
|
||||
scope event.SubscriptionScope
|
||||
genesisBlock *types.Block
|
||||
|
||||
// This mutex synchronizes chain write operations.
|
||||
// Readers don't need to take it, they can just read the database.
|
||||
|
|
@ -1570,8 +1575,6 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
if len(chain) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
bc.blockProcFeed.Send(true)
|
||||
defer bc.blockProcFeed.Send(false)
|
||||
|
||||
// Do a sanity check that the provided chain is actually ordered and linked.
|
||||
for i := 1; i < len(chain); i++ {
|
||||
|
|
@ -1611,6 +1614,16 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
|||
if bc.insertStopped() {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
if atomic.AddInt32(&bc.blockProcCounter, 1) == 1 {
|
||||
bc.blockProcFeed.Send(true)
|
||||
}
|
||||
defer func() {
|
||||
if atomic.AddInt32(&bc.blockProcCounter, -1) == 0 {
|
||||
bc.blockProcFeed.Send(false)
|
||||
}
|
||||
}()
|
||||
|
||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
||||
SenderCacher().RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number(), chain[0].Time()), chain)
|
||||
|
||||
|
|
@ -2519,3 +2532,9 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
|
|||
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
|
||||
return time.Duration(bc.flushInterval.Load())
|
||||
}
|
||||
|
||||
// HistoryPruningCutoff returns the configured history pruning point.
|
||||
// Blocks before this might not be available in the database.
|
||||
func (bc *BlockChain) HistoryPruningCutoff() uint64 {
|
||||
return bc.cacheConfig.HistoryPruningCutoff
|
||||
}
|
||||
|
|
|
|||
156
core/filtermaps/chain_view.go
Normal file
156
core/filtermaps/chain_view.go
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2025 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// blockchain represents the underlying blockchain of ChainView.
|
||||
type blockchain interface {
|
||||
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||
GetCanonicalHash(number uint64) common.Hash
|
||||
GetReceiptsByHash(hash common.Hash) types.Receipts
|
||||
}
|
||||
|
||||
// ChainView represents an immutable view of a chain with a block id and a set
|
||||
// of receipts associated to each block number and a block hash associated with
|
||||
// all block numbers except the head block. This is because in the future
|
||||
// ChainView might represent a view where the head block is currently being
|
||||
// created. Block id is a unique identifier that can also be calculated for the
|
||||
// head block.
|
||||
// Note that the view's head does not have to be the current canonical head
|
||||
// of the underlying blockchain, it should only possess the block headers
|
||||
// and receipts up until the expected chain view head.
|
||||
type ChainView struct {
|
||||
chain blockchain
|
||||
headNumber uint64
|
||||
hashes []common.Hash // block hashes starting backwards from headNumber until first canonical hash
|
||||
}
|
||||
|
||||
// NewChainView creates a new ChainView.
|
||||
func NewChainView(chain blockchain, number uint64, hash common.Hash) *ChainView {
|
||||
cv := &ChainView{
|
||||
chain: chain,
|
||||
headNumber: number,
|
||||
hashes: []common.Hash{hash},
|
||||
}
|
||||
cv.extendNonCanonical()
|
||||
return cv
|
||||
}
|
||||
|
||||
// getBlockHash returns the block hash belonging to the given block number.
|
||||
// Note that the hash of the head block is not returned because ChainView might
|
||||
// represent a view where the head block is currently being created.
|
||||
func (cv *ChainView) getBlockHash(number uint64) common.Hash {
|
||||
if number >= cv.headNumber {
|
||||
panic("invalid block number")
|
||||
}
|
||||
return cv.blockHash(number)
|
||||
}
|
||||
|
||||
// getBlockId returns the unique block id belonging to the given block number.
|
||||
// Note that it is currently equal to the block hash. In the future it might
|
||||
// be a different id for future blocks if the log index root becomes part of
|
||||
// consensus and therefore rendering the index with the new head will happen
|
||||
// before the hash of that new head is available.
|
||||
func (cv *ChainView) getBlockId(number uint64) common.Hash {
|
||||
if number > cv.headNumber {
|
||||
panic("invalid block number")
|
||||
}
|
||||
return cv.blockHash(number)
|
||||
}
|
||||
|
||||
// getReceipts returns the set of receipts belonging to the block at the given
|
||||
// block number.
|
||||
func (cv *ChainView) getReceipts(number uint64) types.Receipts {
|
||||
if number > cv.headNumber {
|
||||
panic("invalid block number")
|
||||
}
|
||||
return cv.chain.GetReceiptsByHash(cv.blockHash(number))
|
||||
}
|
||||
|
||||
// limitedView returns a new chain view that is a truncated version of the parent view.
|
||||
func (cv *ChainView) limitedView(newHead uint64) *ChainView {
|
||||
if newHead >= cv.headNumber {
|
||||
return cv
|
||||
}
|
||||
return NewChainView(cv.chain, newHead, cv.blockHash(newHead))
|
||||
}
|
||||
|
||||
// equalViews returns true if the two chain views are equivalent.
|
||||
func equalViews(cv1, cv2 *ChainView) bool {
|
||||
if cv1 == nil || cv2 == nil {
|
||||
return false
|
||||
}
|
||||
return cv1.headNumber == cv2.headNumber && cv1.getBlockId(cv1.headNumber) == cv2.getBlockId(cv2.headNumber)
|
||||
}
|
||||
|
||||
// matchViews returns true if the two chain views are equivalent up until the
|
||||
// specified block number. If the specified number is higher than one of the
|
||||
// heads then false is returned.
|
||||
func matchViews(cv1, cv2 *ChainView, number uint64) bool {
|
||||
if cv1 == nil || cv2 == nil {
|
||||
return false
|
||||
}
|
||||
if cv1.headNumber < number || cv2.headNumber < number {
|
||||
return false
|
||||
}
|
||||
if number == cv1.headNumber || number == cv2.headNumber {
|
||||
return cv1.getBlockId(number) == cv2.getBlockId(number)
|
||||
}
|
||||
return cv1.getBlockHash(number) == cv2.getBlockHash(number)
|
||||
}
|
||||
|
||||
// extendNonCanonical checks whether the previously known reverse list of head
|
||||
// hashes still ends with one that is canonical on the underlying blockchain.
|
||||
// If necessary then it traverses further back on the header chain and adds
|
||||
// more hashes to the list.
|
||||
func (cv *ChainView) extendNonCanonical() bool {
|
||||
for {
|
||||
hash, number := cv.hashes[len(cv.hashes)-1], cv.headNumber-uint64(len(cv.hashes)-1)
|
||||
if cv.chain.GetCanonicalHash(number) == hash {
|
||||
return true
|
||||
}
|
||||
if number == 0 {
|
||||
log.Error("Unknown genesis block hash found")
|
||||
return false
|
||||
}
|
||||
header := cv.chain.GetHeader(hash, number)
|
||||
if header == nil {
|
||||
log.Error("Header not found", "number", number, "hash", hash)
|
||||
return false
|
||||
}
|
||||
cv.hashes = append(cv.hashes, header.ParentHash)
|
||||
}
|
||||
}
|
||||
|
||||
// blockHash returns the given block hash without doing the head number check.
|
||||
func (cv *ChainView) blockHash(number uint64) common.Hash {
|
||||
if number+uint64(len(cv.hashes)) <= cv.headNumber {
|
||||
hash := cv.chain.GetCanonicalHash(number)
|
||||
if !cv.extendNonCanonical() {
|
||||
return common.Hash{}
|
||||
}
|
||||
if number+uint64(len(cv.hashes)) <= cv.headNumber {
|
||||
return hash
|
||||
}
|
||||
}
|
||||
return cv.hashes[cv.headNumber-number]
|
||||
}
|
||||
63
core/filtermaps/checkpoints.go
Normal file
63
core/filtermaps/checkpoints.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2025 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// checkpointList lists checkpoints for finalized epochs of a given chain.
|
||||
// This allows the indexer to start indexing from the latest available
|
||||
// checkpoint and then index tail epochs in reverse order.
|
||||
type checkpointList []epochCheckpoint
|
||||
|
||||
// epochCheckpoint specified the last block of the epoch and the first log
|
||||
// value index where that block starts. This allows a log value iterator to
|
||||
// be initialized at the epoch boundary.
|
||||
type epochCheckpoint struct {
|
||||
BlockNumber uint64 // block that generated the last log value of the given epoch
|
||||
BlockId common.Hash
|
||||
FirstIndex uint64 // first log value index of the given block
|
||||
}
|
||||
|
||||
//go:embed checkpoints_mainnet.json
|
||||
var checkpointsMainnetJSON []byte
|
||||
|
||||
//go:embed checkpoints_sepolia.json
|
||||
var checkpointsSepoliaJSON []byte
|
||||
|
||||
//go:embed checkpoints_holesky.json
|
||||
var checkpointsHoleskyJSON []byte
|
||||
|
||||
// checkpoints lists sets of checkpoints for multiple chains. The matching
|
||||
// checkpoint set is autodetected by the indexer once the canonical chain is
|
||||
// known.
|
||||
var checkpoints = []checkpointList{
|
||||
decodeCheckpoints(checkpointsMainnetJSON),
|
||||
decodeCheckpoints(checkpointsSepoliaJSON),
|
||||
decodeCheckpoints(checkpointsHoleskyJSON),
|
||||
}
|
||||
|
||||
func decodeCheckpoints(encoded []byte) (result checkpointList) {
|
||||
if err := json.Unmarshal(encoded, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
20
core/filtermaps/checkpoints_holesky.json
Normal file
20
core/filtermaps/checkpoints_holesky.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[
|
||||
{"blockNumber": 814411, "blockId": "0xf763e96fc3920359c5f706803024b78e83796a3a8563bb5a83c3ddd7cbfde287", "firstIndex": 67107637},
|
||||
{"blockNumber": 914278, "blockId": "0x0678cf8d53c0d6d27896df657d98cc73bc63ca468b6295068003938ef9b0f927", "firstIndex": 134217671},
|
||||
{"blockNumber": 1048874, "blockId": "0x3620c3d52a40ff4d9fc58c3104cfa2f327f55592caf6a2394c207a5e00b4f740", "firstIndex": 201326382},
|
||||
{"blockNumber": 1144441, "blockId": "0x438fb42850f5a0d8e1666de598a4d0106b62da0f7448c62fe029b8cbad35d08d", "firstIndex": 268435440},
|
||||
{"blockNumber": 1230411, "blockId": "0xf0ee07e60a93910723b259473a253dd9cf674e8b78c4f153b32ad7032efffeeb", "firstIndex": 335543079},
|
||||
{"blockNumber": 1309112, "blockId": "0xc1646e5ef4b4343880a85b1a4111e3321d609a1225e9cebbe10d1c7abf99e58d", "firstIndex": 402653100},
|
||||
{"blockNumber": 1380522, "blockId": "0x1617cae91989d97ac6335c4217aa6cc7f7f4c2837e20b3b5211d98d6f9e97e44", "firstIndex": 469761917},
|
||||
{"blockNumber": 1476962, "blockId": "0xd978455d2618d093dfc685d7f43f61be6dae0fa8a9cb915ae459aa6e0a5525f0", "firstIndex": 536870773},
|
||||
{"blockNumber": 1533518, "blockId": "0xe7d39d71bd9d5f1f3157c35e0329531a7950a19e3042407e38948b89b5384f78", "firstIndex": 603979664},
|
||||
{"blockNumber": 1613787, "blockId": "0xa793168d135c075732a618ec367faaed5f359ffa81898c73cb4ec54ec2caa696", "firstIndex": 671088003},
|
||||
{"blockNumber": 1719099, "blockId": "0xc4394c71a8a24efe64c5ff2afcdd1594f3708524e6084aa7dadd862bd704ab03", "firstIndex": 738196914},
|
||||
{"blockNumber": 1973165, "blockId": "0xee3a9e959a437c707a3036736ec8d42a9261ac6100972c26f65eedcde315a81d", "firstIndex": 805306333},
|
||||
{"blockNumber": 2274844, "blockId": "0x76e2d33653ed9282c63ad09d721e1f2e29064aa9c26202e20fc4cc73e8dfe5f6", "firstIndex": 872415141},
|
||||
{"blockNumber": 2530503, "blockId": "0x59f4e45345f8b8f848be5004fe75c4a28f651864256c3aa9b2da63369432b718", "firstIndex": 939523693},
|
||||
{"blockNumber": 2781903, "blockId": "0xc981e91c6fb69c5e8146ead738fcfc561831f11d7786d39c7fa533966fc37675", "firstIndex": 1006632906},
|
||||
{"blockNumber": 3101713, "blockId": "0xc7baa577c91d8439e3fc79002d2113d07ca54a4724bf2f1f5af937b7ba8e1f32", "firstIndex": 1073741382},
|
||||
{"blockNumber": 3221770, "blockId": "0xa6b8240b7883fcc71aa5001b5ba66c889975c5217e14c16edebdd6f6e23a9424", "firstIndex": 1140850360}
|
||||
]
|
||||
|
||||
256
core/filtermaps/checkpoints_mainnet.json
Normal file
256
core/filtermaps/checkpoints_mainnet.json
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
[
|
||||
{"blockNumber": 4166218, "blockId": "0xdd767e0426256179125551e8e40f33565a96d1c94076c7746fa79d767ed4ad65", "firstIndex": 67108680},
|
||||
{"blockNumber": 4514014, "blockId": "0x33a0879bdabea4a7a3f2b424388cbcbf2fbd519bddadf13752a259049c78e95d", "firstIndex": 134217343},
|
||||
{"blockNumber": 4817415, "blockId": "0x4f0e8c7dd04fbe0985b9394575b19f13ea66a2a628fa5b08178ce4b138c6db80", "firstIndex": 201326352},
|
||||
{"blockNumber": 5087733, "blockId": "0xc84cd5e9cda999c919803c7a53a23bb77a18827fbde401d3463f1e9e52536424", "firstIndex": 268435343},
|
||||
{"blockNumber": 5306107, "blockId": "0x13f028b5fc055d23f55a92a2eeecfbcfbda8a08e4cd519ce451ba2e70428f5f9", "firstIndex": 335544094},
|
||||
{"blockNumber": 5509918, "blockId": "0x1ed770a58a7b4d4a828b7bb44c8820a674d562b23a6a0139981abe4c489d4dad", "firstIndex": 402652853},
|
||||
{"blockNumber": 5670390, "blockId": "0x3923ee6a62e6cc5132afdadf1851ae4e73148e6fbe0a8319cafd2a120c98efa3", "firstIndex": 469761897},
|
||||
{"blockNumber": 5826139, "blockId": "0xe61bc6ef03c333805f26319e1688f82553f98aa5e902b200e0621a3371b69050", "firstIndex": 536870853},
|
||||
{"blockNumber": 5953029, "blockId": "0x43d710b1b7243b848400975048ccefdfaba091c692c7f01c619d988886cc160f", "firstIndex": 603979580},
|
||||
{"blockNumber": 6102846, "blockId": "0xa100b2018f6545cc689656b4b846677b138955b7efd30e850cd14c246430ba18", "firstIndex": 671088291},
|
||||
{"blockNumber": 6276718, "blockId": "0xb832ac448b06c104ba50faefd58b0b94d53c0fba5cb268086adad4db99c2f35f", "firstIndex": 738197399},
|
||||
{"blockNumber": 6448696, "blockId": "0x48e8ae6f729ad6c76b6cf632bd52a6df7886ed55be09d43c5004fcc1463e533b", "firstIndex": 805305988},
|
||||
{"blockNumber": 6655974, "blockId": "0xac395971a6ffc30f807848f68b97b2834f8ea13478a7615860b6a69e3d0823ca", "firstIndex": 872415033},
|
||||
{"blockNumber": 6873949, "blockId": "0xc522ddb1113b1e9a87b2bdcb11ce78756beba6454a890122f121a032b5769354", "firstIndex": 939523784},
|
||||
{"blockNumber": 7080953, "blockId": "0x3606de577d80120d1edbb64bad7fa6795e788bae342866a98cc58ce2f7575045", "firstIndex": 1006632796},
|
||||
{"blockNumber": 7267002, "blockId": "0xad770882a69d216e955e34fef84851e56c0de82deacd6187a7a41f6170cd6c6d", "firstIndex": 1073741045},
|
||||
{"blockNumber": 7466708, "blockId": "0x17a48817b3a65aba333a5b56f3ff2e86fbcc19e184b046a5305a5182fdd8eb8a", "firstIndex": 1140850680},
|
||||
{"blockNumber": 7661807, "blockId": "0xa74731ee775fbd3f4d9313c68562737dd7c8d2c9eb968791d8abe167e16ddc96", "firstIndex": 1207959112},
|
||||
{"blockNumber": 7834556, "blockId": "0xe4b4812448075508cb05a0e3257f91b49509dc78cd963676a633864db6e78956", "firstIndex": 1275068095},
|
||||
{"blockNumber": 7990068, "blockId": "0x07bd4ca38abb4584a6209e04035646aa545ebbb6c948d438d4c25bfd9cb205fa", "firstIndex": 1342176620},
|
||||
{"blockNumber": 8143032, "blockId": "0x0e3149e9637290b044ee693b8fcb66e23d22db3ad0bdda32962138ba18e59f3f", "firstIndex": 1409285949},
|
||||
{"blockNumber": 8297660, "blockId": "0x34cd24f80247f7dfaf316b2e637f4b62f72ecc90703014fb25cb98ad044fc2c0", "firstIndex": 1476394911},
|
||||
{"blockNumber": 8465137, "blockId": "0x4452fa296498248d7f10c9dc6ec1e4ae7503aa07f491e6d38b21aea5d2c658a8", "firstIndex": 1543503744},
|
||||
{"blockNumber": 8655820, "blockId": "0x7bdb9008b30be420f7152cc294ac6e5328eed5b4abd954a34105de3da24f3cc6", "firstIndex": 1610612619},
|
||||
{"blockNumber": 8807187, "blockId": "0xde03e3bfddc722c019f0b59bc55efabcd5ab68c6711f4c08d0390a56f396590d", "firstIndex": 1677721589},
|
||||
{"blockNumber": 8911171, "blockId": "0xe44f342de74ab05a2a994f8841bdf88f720b9dc260177ba4030d0f7077901324", "firstIndex": 1744830310},
|
||||
{"blockNumber": 8960320, "blockId": "0x79764f9ff6e0fe4848eda1805687872021076e4e603112861af84181395ac559", "firstIndex": 1811938893},
|
||||
{"blockNumber": 9085994, "blockId": "0x24a101d1c8a63367a0953d10dc79c3b587a93bd7fd382084708adefce0b8363f", "firstIndex": 1879047965},
|
||||
{"blockNumber": 9230924, "blockId": "0xb176a98d3acd855cbb75265fb6be955a8d51abc771e021e13275d5b3ecb07eeb", "firstIndex": 1946156668},
|
||||
{"blockNumber": 9390535, "blockId": "0x640f5e2d511a5141878d57ae7a619f19b72a2bd3ef019cf0a22d74d93d9acf07", "firstIndex": 2013265733},
|
||||
{"blockNumber": 9515674, "blockId": "0xff4a7b6b21aeaeb6e1a75ecd22b1f34c058a0ce1477ce90a8ce78165fd1d0941", "firstIndex": 2080374553},
|
||||
{"blockNumber": 9659426, "blockId": "0xc351455249343b41e9171e183612b68c3c895271c62bd2c53d9e3ab1aa865aa1", "firstIndex": 2147483567},
|
||||
{"blockNumber": 9794018, "blockId": "0xde98035b4b7f9449c256239b65c7ff2c0330de44dee190106d0a96fb6f683238", "firstIndex": 2214592213},
|
||||
{"blockNumber": 9923840, "blockId": "0x881da313a1e2b6fab58a1d6fa65b5dacfdc9d68a3112a647104955b5233f84e3", "firstIndex": 2281701302},
|
||||
{"blockNumber": 10042435, "blockId": "0x451f6459640a6f54e2a535cc3a49cfc469861da3ddc101840ab3aef9e17fa424", "firstIndex": 2348810174},
|
||||
{"blockNumber": 10168883, "blockId": "0x5d16ff5adf0df1e4dc810da60af37399ef733be7870f21112b8c2cfff4995dd9", "firstIndex": 2415918783},
|
||||
{"blockNumber": 10289554, "blockId": "0x85d5690f15a787c43b9a49e8dd6e324f0b3e0c9796d07c0cfb128e5c168f5488", "firstIndex": 2483027930},
|
||||
{"blockNumber": 10386676, "blockId": "0x20f675ea72db448024a8a0b8e3ec180cac37a5910575bc32f8d9f5cdfe3c2649", "firstIndex": 2550136212},
|
||||
{"blockNumber": 10479675, "blockId": "0x014abb07acf2330cc78800ca1f564928f2daccca4b389bf5c59f4b840d843ec0", "firstIndex": 2617245218},
|
||||
{"blockNumber": 10562661, "blockId": "0xd437607a3f81ce8b7c605e167ce5e52bf8a3e02cdc646997bd0ccc57a50ad7d1", "firstIndex": 2684354520},
|
||||
{"blockNumber": 10641508, "blockId": "0x2e8ab6470c29f90ac23dcfc58310f0208f5d0e752a0c7982a77a223eca104082", "firstIndex": 2751462730},
|
||||
{"blockNumber": 10717156, "blockId": "0x8820447b6429dd12be603c1c130be532e9db065bb4bc6b2a9d4551794d63789a", "firstIndex": 2818571831},
|
||||
{"blockNumber": 10784549, "blockId": "0xc557daab80a7cdc963d62aa881faf3ab1baceff8e027046bcd203e432e0983b3", "firstIndex": 2885680800},
|
||||
{"blockNumber": 10848651, "blockId": "0xede1b0de5db6685a6f589096ceb8fccb08d3ff60e8b00a93caa4a775b48e07fc", "firstIndex": 2952789740},
|
||||
{"blockNumber": 10909166, "blockId": "0x989db675899d13323006a4d6174557e3c5501c672afd60d8bd902fc98d37e92e", "firstIndex": 3019897599},
|
||||
{"blockNumber": 10972902, "blockId": "0x5484050cc2c7d774bc5cd6af1c2ef8c19d1de12dabe25867c9b365924ea10434", "firstIndex": 3087007422},
|
||||
{"blockNumber": 11036597, "blockId": "0x1e3686e19056587c385262d5b0a07b3ec04e804c2d59e9aaca1e5876e78f69ae", "firstIndex": 3154116231},
|
||||
{"blockNumber": 11102520, "blockId": "0x339cf302fe813cce3bb9318b860dfa8be7f688413f38a6ea1987a1b84d742b4b", "firstIndex": 3221224863},
|
||||
{"blockNumber": 11168162, "blockId": "0xc0fa21ea090627610bcac4732dff702633f310cabafc42bc500d3d4805198fe0", "firstIndex": 3288334273},
|
||||
{"blockNumber": 11233707, "blockId": "0x491c37a479b8cf22eaa3654ae34c5ddc4627df8c58ca8a6979159e1710428576", "firstIndex": 3355442691},
|
||||
{"blockNumber": 11300526, "blockId": "0xb7366d2a24df99002cffe0c9a00959c93ef0dcfc3fd17389e2020bf5caa788eb", "firstIndex": 3422551480},
|
||||
{"blockNumber": 11367621, "blockId": "0xce53df5080c5b5238bb7717dfbfd88c2f574cfbb3d91f92b57171a00e9776cd2", "firstIndex": 3489660710},
|
||||
{"blockNumber": 11431881, "blockId": "0x2a08ff9c4f6fd152166213d902f0870822429f01d5f90e384ac54a3eac0ceb3a", "firstIndex": 3556768626},
|
||||
{"blockNumber": 11497107, "blockId": "0x1f99c6b65f2b1cb06ed1786c6a0274ff1b9eacab6cb729fcd386f10ebbd88123", "firstIndex": 3623878389},
|
||||
{"blockNumber": 11560104, "blockId": "0xebe6924817bbdfe52af49667da1376bae5a2994b375d4b996e8ff2683744e37a", "firstIndex": 3690986640},
|
||||
{"blockNumber": 11625129, "blockId": "0xbe6eee325329ee2fe632d8576864c29dd1c79bab891dc0a22d5b2ac87618d26e", "firstIndex": 3758095773},
|
||||
{"blockNumber": 11690397, "blockId": "0xc28bf55f858ddf5b82d1ceb3b5258b90a9ca34df8863a1c652c4d359f5748fdf", "firstIndex": 3825204492},
|
||||
{"blockNumber": 11755087, "blockId": "0x0c10cde6ce1bbe24dc57347fe4aaebc17b7d8e8d7d97e3db573133477f494740", "firstIndex": 3892314051},
|
||||
{"blockNumber": 11819674, "blockId": "0x36b694a1776c94e4c6ae4a410931b2086de47a83e437517040e3290ce9afff67", "firstIndex": 3959422445},
|
||||
{"blockNumber": 11883358, "blockId": "0x21f447aca9ddf94ed71df9fa3648a12acc2ba603f89f24c4784936864c41945f", "firstIndex": 4026531743},
|
||||
{"blockNumber": 11948524, "blockId": "0x71a52b6cce80d3a552b0daa18beb952facf81a89bc7ca769d08ac297f317507a", "firstIndex": 4093640009},
|
||||
{"blockNumber": 12013168, "blockId": "0x9a7fb369b8d8cd0edd0d890d636096f20c63abb7eb5798ad1e578cac599e3db8", "firstIndex": 4160748475},
|
||||
{"blockNumber": 12078711, "blockId": "0x5de09329413b0c2f58d926f225197552a335ba3d5544d7bdb45e7574f78c9b8d", "firstIndex": 4227858275},
|
||||
{"blockNumber": 12143640, "blockId": "0xbeafc0e1e0586f5a95f00f2a796d7df122c79c187aa2d917129297f24b8306bd", "firstIndex": 4294967145},
|
||||
{"blockNumber": 12208005, "blockId": "0x052487095cdd4a604808e6c14e30fb68b3fa546d35585b315f287219d38ef77c", "firstIndex": 4362075289},
|
||||
{"blockNumber": 12272465, "blockId": "0x82c8a50413bd67a0d6f53b085adcd9ae8c25ecc07ed766fa80297a8dcae63b29", "firstIndex": 4429184610},
|
||||
{"blockNumber": 12329418, "blockId": "0x294c147e48d32c217ff3f27a3c8c989f15eee57a911408ec4c28d4f13a36bb3b", "firstIndex": 4496292968},
|
||||
{"blockNumber": 12382388, "blockId": "0x8c2555965ff735690d2d94ececc48df4700e079c7b21b8e601a30d4e99bc4b5b", "firstIndex": 4563401809},
|
||||
{"blockNumber": 12437052, "blockId": "0x2e38362031f36a0f3394da619dcc03be03c19700594cbd1df84c2c476a87de63", "firstIndex": 4630511012},
|
||||
{"blockNumber": 12490026, "blockId": "0x122749c02a55c9c2a1e69068f54b6c1d25419eb743e3553aba91acf1daeadc35", "firstIndex": 4697619920},
|
||||
{"blockNumber": 12541747, "blockId": "0xfb9f12aa2902da798ac05fab425434f8c7ce98050d67d416dbb32f98c21f66f7", "firstIndex": 4764728267},
|
||||
{"blockNumber": 12597413, "blockId": "0x9a7a399c2904ac8d0fec580550525e7e1a73d8f65f739bf7c05d86e389d0d3f7", "firstIndex": 4831837757},
|
||||
{"blockNumber": 12651950, "blockId": "0xb78dcb572cdafb9c4e2f3863ef518a3b2df0cd4f76faa26a423b2ca0c1cde734", "firstIndex": 4898946491},
|
||||
{"blockNumber": 12706472, "blockId": "0xfd21f41ec6b0c39287d7d48c134d1212a261c53d65db99739994b003150bbad1", "firstIndex": 4966054796},
|
||||
{"blockNumber": 12762929, "blockId": "0xc94d994bc40b2ae7dc23cf2b92cc01e84915f090bb57c0d9a67584bd564d3916", "firstIndex": 5033164307},
|
||||
{"blockNumber": 12816689, "blockId": "0x7770c72f22cbf6ccf7ab85d203088f7ede89632cf0042c690102f926a90bd09d", "firstIndex": 5100273412},
|
||||
{"blockNumber": 12872408, "blockId": "0x2e008b8c952d828875d777f7912f472af96ffc977f2ceae884006682cab6b8ed", "firstIndex": 5167381625},
|
||||
{"blockNumber": 12929718, "blockId": "0x85eb0ed3c5910c6a01b65ef0a5b76c59c2cdb5094e6e27eb87c751d77bcc2c88", "firstIndex": 5234491305},
|
||||
{"blockNumber": 12988757, "blockId": "0xdf12045bea73af18d4e71f8be8e334160f78b85f96a3535a4056409d8b61355a", "firstIndex": 5301600237},
|
||||
{"blockNumber": 13049172, "blockId": "0xf07608d97a101cd9a95fee9d9062a15bcb333263e555f8cfa31da037e0468f30", "firstIndex": 5368709080},
|
||||
{"blockNumber": 13108936, "blockId": "0x42739341db582d2f39b91ec9e8cc758777ca3f6ff9f25cd98883619fd5f026a7", "firstIndex": 5435817013},
|
||||
{"blockNumber": 13175495, "blockId": "0x564f25eacb229350b7c648b5828169e7a0344ae62e866206828e2cfad8947f10", "firstIndex": 5502926476},
|
||||
{"blockNumber": 13237721, "blockId": "0x0973425abec0fa6319701b46e07c2373b0580e3adbed6900aad27d5bf26dcb95", "firstIndex": 5570035419},
|
||||
{"blockNumber": 13298771, "blockId": "0xf3a16fec5be808c9f7782fb578dc8cef7f8e2110f7289bd03c0cc13977dd1518", "firstIndex": 5637143840},
|
||||
{"blockNumber": 13361281, "blockId": "0x3c0b6364201ca9221b61af3de27a3a87e111870b8c7efc43a6d8496e98c68690", "firstIndex": 5704253046},
|
||||
{"blockNumber": 13421819, "blockId": "0x2f472e57997b95558b99e3e5e7e0e8d4dbf8b71c081aac6536c9ff5925dac2ce", "firstIndex": 5771361231},
|
||||
{"blockNumber": 13480620, "blockId": "0xc4d689e87464a0c83c661c8e3a0614c370631de857f7e385b161dfe8bacd3e71", "firstIndex": 5838469468},
|
||||
{"blockNumber": 13535793, "blockId": "0xe7674bacc8edce9fb3efd59b92c97da48fe7ace1de314b4a67d7d032fc3bb680", "firstIndex": 5905578026},
|
||||
{"blockNumber": 13590588, "blockId": "0x6a3e86bdce7dd7d8792e1af9156edd8c3ffee7c20fed97001f58a9a2699f6594", "firstIndex": 5972687757},
|
||||
{"blockNumber": 13646707, "blockId": "0xab404a5d3709cf571b04e9493f37116eeb5dd2bc9dc10c48387c1e0199013d69", "firstIndex": 6039797165},
|
||||
{"blockNumber": 13703025, "blockId": "0x20e2fde15b8fe56f5dd7ab0f324c552038167ed44864bf3978e531ae68d6d138", "firstIndex": 6106905803},
|
||||
{"blockNumber": 13761024, "blockId": "0x2ae49275e13e780f1d29aea8507b2a708ff7bfe977efac93e050273b8b3a8164", "firstIndex": 6174015107},
|
||||
{"blockNumber": 13819468, "blockId": "0xb9d19cb31dedb1128b11cad9ffd6e58c70fe7ba65ba68f1ac63668ac5160ad85", "firstIndex": 6241124350},
|
||||
{"blockNumber": 13877932, "blockId": "0x80b1ff0bb069a8479360a15eaa84ba30da02cfacadc564837f4b1c90478addb8", "firstIndex": 6308232256},
|
||||
{"blockNumber": 13935384, "blockId": "0xe1f5469a559a6114dd469af61b118b9d9551a69bbd49a4e88f2a2d724830c871", "firstIndex": 6375341632},
|
||||
{"blockNumber": 13994042, "blockId": "0x25188fb75f2328c870ade7c38ef42ff5fddef9c4e364eebe4c5d8d9cc3ecabab", "firstIndex": 6442449799},
|
||||
{"blockNumber": 14051123, "blockId": "0xf4ef2bce9ee9222bdcf6b3a0c204676d9345e211e10c983e523930274e041ef1", "firstIndex": 6509559107},
|
||||
{"blockNumber": 14109189, "blockId": "0x80b730c28f75d8cb5ec2fb736341cd87cb4ecb2c9c614e0a4ecc0f9812675d50", "firstIndex": 6576667347},
|
||||
{"blockNumber": 14166822, "blockId": "0xf662a24b91684fa8ac462b31071f406de8d6183dba46d30d690f4407bc6af36f", "firstIndex": 6643777079},
|
||||
{"blockNumber": 14222488, "blockId": "0x7333e324c96b12f11a38d1fc2ddb4860e018b90f5dc10f3dbe19f7679bb95535", "firstIndex": 6710885890},
|
||||
{"blockNumber": 14277180, "blockId": "0x4373c1000e8e10179657689e2f0e42f88bd1601ecb4a5d83970d10287f6654cc", "firstIndex": 6777994595},
|
||||
{"blockNumber": 14331080, "blockId": "0x9c708a750a3f284ec0ee950110b36fd488cb1ec24cd0c2ea72c19551ec5c42a5", "firstIndex": 6845103719},
|
||||
{"blockNumber": 14384243, "blockId": "0x34ce7503b76335aa18dec880b0cefd388a29e0fcff6f2e1ddda8fb8c0ac1daf0", "firstIndex": 6912212376},
|
||||
{"blockNumber": 14437670, "blockId": "0x79842efd3e406b41f51935fe2e6ad20a7dd5a9db2280ebd7f602ed93da1e3c24", "firstIndex": 6979320543},
|
||||
{"blockNumber": 14489204, "blockId": "0xcd12addf0afdc229e9fe3bd0a34677a3826c5e78d4baf715f8ed36b736d6627a", "firstIndex": 7046430591},
|
||||
{"blockNumber": 14541688, "blockId": "0x55f617abf208a73fc467e8cb5feead586b671dbb0f6281570b3c44b8eabb2b9e", "firstIndex": 7113538755},
|
||||
{"blockNumber": 14594551, "blockId": "0xc7211bf772e93c8c2f945fcb6098b47c3455604cb8b94a505cb5cb720914c369", "firstIndex": 7180646025},
|
||||
{"blockNumber": 14645065, "blockId": "0x6d5b0326f4b22e2b0196986a514f23ec6e9a62f70f53300a22b21ff661a6ef7e", "firstIndex": 7247756883},
|
||||
{"blockNumber": 14695926, "blockId": "0x0a77272250e43b4bb46c02eb76944881a3c6b00a21bb9086a8229199bd62d97a", "firstIndex": 7314865843},
|
||||
{"blockNumber": 14746330, "blockId": "0xd677fdbaf8efb1bfdc138ac6b2bd5d0e890a29acb1f52f40169181ad517b0d31", "firstIndex": 7381974956},
|
||||
{"blockNumber": 14798546, "blockId": "0xbb277e8623acd2ce2340cf32f6c0ddab70fd95d862287f68a3c37250a70619cd", "firstIndex": 7449082890},
|
||||
{"blockNumber": 14848230, "blockId": "0x587b39f11bdaa2091291c7c3947e88df2e91e7997f2375dfd43b6e310a538582", "firstIndex": 7516192636},
|
||||
{"blockNumber": 14897646, "blockId": "0xf5b5c9d0c024ca0c0f0c6171871f609687f4ccb064ededbd61176cf23a9011e8", "firstIndex": 7583299602},
|
||||
{"blockNumber": 14950782, "blockId": "0x50549486afaf92a4c3520012b325e914ef77a82e4d6530a71f9b1cca31bfae18", "firstIndex": 7650409868},
|
||||
{"blockNumber": 15004101, "blockId": "0x7edac55dea3ee4308db60b9bc0524836226fe301e085b3ce39105bd145ba7fc3", "firstIndex": 7717517503},
|
||||
{"blockNumber": 15056903, "blockId": "0xb4cfd02d435718598179cdba3f5c11eb8653fe97ec8d89c60673e3e07b8dfc94", "firstIndex": 7784627997},
|
||||
{"blockNumber": 15108302, "blockId": "0x53c77a7de4515e9e93467a76f04cc401834bcdd64e9dfa03cf6d2844a6930293", "firstIndex": 7851736988},
|
||||
{"blockNumber": 15159526, "blockId": "0x1a31ad84b423254d7ff24e7eca54048ed8cc13cec5eb7289bf3f98ed4de9f724", "firstIndex": 7918844431},
|
||||
{"blockNumber": 15211013, "blockId": "0xe5d491e1d6cc5322454143b915c106be1bf28114a41b054ba5e5cfe0abecafba", "firstIndex": 7985953942},
|
||||
{"blockNumber": 15264389, "blockId": "0xd9939bb9e58e95d2672c1148b4ec5730204527d3f3fc98ca03a67dc85cf3d710", "firstIndex": 8053063187},
|
||||
{"blockNumber": 15315862, "blockId": "0x7254f99c4bb05235d5b437984c9132164e33182d4ce11a3847999da5c28b4092", "firstIndex": 8120172147},
|
||||
{"blockNumber": 15364726, "blockId": "0x11b57547579d9009679e327f57e308fe86856391805bc3c86e7b39daae890f52", "firstIndex": 8187281042},
|
||||
{"blockNumber": 15412886, "blockId": "0xbe3602b1dbef9015a3ec7968ac7652edf4424934b6bf7b713b99d8556f1d9444", "firstIndex": 8254390023},
|
||||
{"blockNumber": 15462792, "blockId": "0x3348ca4e14ac8d3c6ac6df676deaf3e3b5e0a11b599f73bd9739b74ebd693efe", "firstIndex": 8321499024},
|
||||
{"blockNumber": 15509914, "blockId": "0xbc98fd6b71438d5a169f9373172fea799fa3d22a8e6fe648d35e1070f2261113", "firstIndex": 8388606521},
|
||||
{"blockNumber": 15558748, "blockId": "0x5fa2cf499276ae74a5b8618990e71ed11a063619afe25c01b46e6252eba14c19", "firstIndex": 8455716577},
|
||||
{"blockNumber": 15604217, "blockId": "0x78a608e13d2eb3c5fed81a19b829ede88071cf01ea9ff58112a7472435f97c30", "firstIndex": 8522825668},
|
||||
{"blockNumber": 15651869, "blockId": "0xd465d861d925d1475440782ff16c2b3361ba3c8e169d7cc90eb8dfc0f31b0aac", "firstIndex": 8589934080},
|
||||
{"blockNumber": 15700968, "blockId": "0x71e3def131271e02c06ca945d14a995703a48faac1334a9e2e2321edd0b504d0", "firstIndex": 8657043390},
|
||||
{"blockNumber": 15762986, "blockId": "0x9b1b51dca2eae29162ca66968a77b45175f134b44aea3defadcb924f83e0b944", "firstIndex": 8724151376},
|
||||
{"blockNumber": 15814455, "blockId": "0x3c04a509cb6304d3df4bef57e0119d9e615ab737ec0b4a7deada6e5f57d9f873", "firstIndex": 8791260562},
|
||||
{"blockNumber": 15865639, "blockId": "0x9e9e26148c774518ecf362c0e7c65a5c1b054a8a3e4e36036c70e273fac6147c", "firstIndex": 8858368894},
|
||||
{"blockNumber": 15920564, "blockId": "0x9efe1d4dbfd9aa891ac0cffd3e1422a27ba2ea4add211b6900a2242cdb0f0ca0", "firstIndex": 8925477950},
|
||||
{"blockNumber": 15974371, "blockId": "0xc63ccef7bc35a0b431a411f99fe581b322d00cfc6422d078696808a5658a32ac", "firstIndex": 8992587107},
|
||||
{"blockNumber": 16032913, "blockId": "0x3e60957224964669a8646914e3166553b9f4256d5be160b17995d838af3ef137", "firstIndex": 9059696632},
|
||||
{"blockNumber": 16091057, "blockId": "0x12b346047bb49063ab6d9e737775924cf05c52114202ddb1a2bdaf9caabbfe0c", "firstIndex": 9126804912},
|
||||
{"blockNumber": 16150977, "blockId": "0x49318a32ff0ce979c4061c1c34db2a94fb06e7669c93742b75aff14a134fa598", "firstIndex": 9193913896},
|
||||
{"blockNumber": 16207432, "blockId": "0xf7870865edf81be4389a0be01468da959de703df0d431610814d16ed480176e4", "firstIndex": 9261019778},
|
||||
{"blockNumber": 16262582, "blockId": "0x25818e0f4d54af6c44ef7b23add34409a47de3ab1c905889478f3ec8ad173ec3", "firstIndex": 9328131320},
|
||||
{"blockNumber": 16319695, "blockId": "0x25de4b1c18cc503f5d12b4fa9072d33a11fa503a3dbeb9ab3d016b57c1e5cd4d", "firstIndex": 9395240790},
|
||||
{"blockNumber": 16373605, "blockId": "0x3794a5e0d2aa10baf1e6a5ec623d6089fdd39799eff633017d8df5144526939f", "firstIndex": 9462349509},
|
||||
{"blockNumber": 16423494, "blockId": "0xe0217d947ba3865dfc9288e0c890b0996457bb9d18467bd125e86bbb0052b57f", "firstIndex": 9529458033},
|
||||
{"blockNumber": 16474853, "blockId": "0xd454f033d190f22f9e56f0209ea1eeb3b6257805d5d88650d2759eb4d24821b7", "firstIndex": 9596567055},
|
||||
{"blockNumber": 16525689, "blockId": "0x8a23cbbf3e258e13f5a1ada434366796cb4a3e5b1062455582fb2bc3ab991541", "firstIndex": 9663674943},
|
||||
{"blockNumber": 16574203, "blockId": "0xc1a5b7d26e8222bd2d56ef4108f75d69f7c116707d348950834e00962241a4f8", "firstIndex": 9730785112},
|
||||
{"blockNumber": 16622622, "blockId": "0x3ddb3ef7a4309bd788258fb0d62613c89a0b4de715f4e12f6017a194d19d6481", "firstIndex": 9797893665},
|
||||
{"blockNumber": 16672585, "blockId": "0x8aa5e9f72b261f9e2a9eb768483d1bbd84d3a88fdb1346f6a9a7f262fd28ba41", "firstIndex": 9865002893},
|
||||
{"blockNumber": 16720124, "blockId": "0x2128f8baf264166e37554d5c31a06de58d9ccfb663117358251da548a23a060f", "firstIndex": 9932111275},
|
||||
{"blockNumber": 16769162, "blockId": "0x6b3e849482d3222032740ad6b8f98e24636c82682a6a3572b1ef76dfebc66821", "firstIndex": 9999217824},
|
||||
{"blockNumber": 16818311, "blockId": "0xe45f57381978a2bfc85bd20af1c41e2b630412642ac4f606b477f05f030ef5d9", "firstIndex": 10066328668},
|
||||
{"blockNumber": 16869531, "blockId": "0xa154555266d24dc1f4885af5fafcf8cab3de788998cf69e1d28f56aa13a40c43", "firstIndex": 10133437302},
|
||||
{"blockNumber": 16921611, "blockId": "0xf1f829b4ab5eec6e243916dd530993fa11eef5510fd730e8d09ead6b380355a1", "firstIndex": 10200547185},
|
||||
{"blockNumber": 16974870, "blockId": "0x1a33202b95926ae4cb8e6e99d8d150f3c50d817b3a316452bdf428c971dabde5", "firstIndex": 10267655914},
|
||||
{"blockNumber": 17031277, "blockId": "0x706c9dd0dc81e7ac29d2ea0f826e6b8a1dcb5adb1b904ff6e43260729c9fd0a7", "firstIndex": 10334764934},
|
||||
{"blockNumber": 17086330, "blockId": "0x085a80cafe96b520105b9a1f8e7a2bbc9474da24da7e6344ca7c4d32db822f92", "firstIndex": 10401871892},
|
||||
{"blockNumber": 17141311, "blockId": "0x33ec6513dfa515bc5f6356476b4eb075a8064181d6aaf6aa1a1e18887e342f74", "firstIndex": 10468982364},
|
||||
{"blockNumber": 17190907, "blockId": "0x6f41273d3bf30d3347e7eb68872a49b3ac947f314543478be7a28a55e5c41a3c", "firstIndex": 10536090817},
|
||||
{"blockNumber": 17237199, "blockId": "0x9a87a14a128c0345a366940f821a14f16719de628658ac0628e410a72d723e90", "firstIndex": 10603200178},
|
||||
{"blockNumber": 17287181, "blockId": "0x9c6e78adcf562ac63c103e3e5a02f025023079aca79bdd6ef18f7bd2a6271c29", "firstIndex": 10670309183},
|
||||
{"blockNumber": 17338652, "blockId": "0x1b747da97b2397a293602af57514dab4ca1010bb6c601ff05cb2012dd1124ebb", "firstIndex": 10737418023},
|
||||
{"blockNumber": 17389337, "blockId": "0xbc3c0ca1e5989605b9b59c94b418562eb17ccbce30e45ac8531cf0b3867a6b2c", "firstIndex": 10804522857},
|
||||
{"blockNumber": 17442261, "blockId": "0x1ec341be1cbd09f559bfa3d3e39a341d8e21052eeb7880931d43d086651733b7", "firstIndex": 10871635535},
|
||||
{"blockNumber": 17497787, "blockId": "0x6069880d486f2548599df1e14e12752d3eb9bc99843a98cd6631c22be1b58554", "firstIndex": 10938744657},
|
||||
{"blockNumber": 17554322, "blockId": "0x69b2564bc00b1f310f6b416912869d7530d7864bf7d70d55c7ace554f129b989", "firstIndex": 11005852829},
|
||||
{"blockNumber": 17608492, "blockId": "0x7d590653d5fa52c0d3ee453a77d2088504f57adcef35cd57c567afb554608457", "firstIndex": 11072961972},
|
||||
{"blockNumber": 17664272, "blockId": "0xdc16159d3500cdc7410873102f41fc55de2a8a41e3779c4b70e6224a541e2b9e", "firstIndex": 11140070967},
|
||||
{"blockNumber": 17715101, "blockId": "0x655e33c4e81182464ea0b0e1fdbc53ce53902431db5107326b816091a4564652", "firstIndex": 11207179487},
|
||||
{"blockNumber": 17764042, "blockId": "0x54439184f31cd83ba06b48b6dbfdd744ae7246355be1327b44744058711d05c0", "firstIndex": 11274287303},
|
||||
{"blockNumber": 17814383, "blockId": "0xfb453bc951360c76fb09bb1b9a3e39d23ececa0adb93368cc3f41f0457845089", "firstIndex": 11341397984},
|
||||
{"blockNumber": 17864648, "blockId": "0x32a68823ef4ec0cbab2fe50c97e3f462b575e8b117da40d00c710b4c66ee1d6d", "firstIndex": 11408505657},
|
||||
{"blockNumber": 17913366, "blockId": "0x04b944aab8a4ff91b77c2191817cf051766100c227616a3746af53407e740124", "firstIndex": 11475614351},
|
||||
{"blockNumber": 17961690, "blockId": "0x08bee7cc0b764106ca01dd5370b617879487ffb423688c96e948dce125990f45", "firstIndex": 11542723488},
|
||||
{"blockNumber": 18011048, "blockId": "0x94c39d3a64f3e9a91b1d98554cd29e1390e30fa61cfa4e909c503eee2fd9f165", "firstIndex": 11609833142},
|
||||
{"blockNumber": 18061209, "blockId": "0x2ee9ade68955c030488c8a30537bdf948355f7dd5ae64942b5bfce1be6650e19", "firstIndex": 11676941316},
|
||||
{"blockNumber": 18111692, "blockId": "0xd6c4fd0c1cc20ed5e7960bb5043e9e5e9c66a4d2ec5709ac9797fff678435640", "firstIndex": 11744050346},
|
||||
{"blockNumber": 18166212, "blockId": "0x3262588c2ef79a3b3f6a3db6435202d22f5667cd48c136b0797404901525c9ff", "firstIndex": 11811159686},
|
||||
{"blockNumber": 18218743, "blockId": "0x935bd9a4164ff7ecd09a37b916ce5bf78487bd19377b5b17be153e39318aee74", "firstIndex": 11878268593},
|
||||
{"blockNumber": 18271236, "blockId": "0xe58ebb821f27e3665898f390802a3d129d217b3a3ee36d890a85cf22a0a8aa33", "firstIndex": 11945376750},
|
||||
{"blockNumber": 18323007, "blockId": "0x3997a841468efa1bc614bfc3de4502274901b04b428f87a1f3086dfd78cda1eb", "firstIndex": 12012485748},
|
||||
{"blockNumber": 18372443, "blockId": "0xc44a13a5d02e8dc39f355de5e21ce7bb311ce7f4d9114ff480dce235a169e416", "firstIndex": 12079595370},
|
||||
{"blockNumber": 18421829, "blockId": "0x7da63a0b613d8745597b2ac64fd5cc8b2fb14b24d163b12a0a39d7d3d4ff7b5c", "firstIndex": 12146703582},
|
||||
{"blockNumber": 18471706, "blockId": "0xd632a1893f415ff618f4b612a7687e6af1f12feeed81f46f0022090829c1eb4c", "firstIndex": 12213812677},
|
||||
{"blockNumber": 18522301, "blockId": "0x44fa2cf08145ae40e8e42f4e6b4ab7df360a17c5a065ce45fcc41b51bee011f4", "firstIndex": 12280921639},
|
||||
{"blockNumber": 18572935, "blockId": "0x72b8ab4c78c90425ee054b4806a8be703da0febdf1d51866358ec2bd21ba9529", "firstIndex": 12348029751},
|
||||
{"blockNumber": 18623431, "blockId": "0x8c4cb2f13501d9788820280c6f16692d0737258c3896f1e4bded32d838febf7f", "firstIndex": 12415138965},
|
||||
{"blockNumber": 18675470, "blockId": "0x523b73c19ea8b3ae32ef141a83ef9855e667ebf51443cfcabd1a06659359062a", "firstIndex": 12482247454},
|
||||
{"blockNumber": 18725728, "blockId": "0x0cfbd131eb5dad51488238079fba29a63eebb5c32d1a543cb072e48dc2104ef3", "firstIndex": 12549356369},
|
||||
{"blockNumber": 18778387, "blockId": "0xc4906c77af8058b9f172a4f0e8788c7887f05caa5ac752b38b5387080f74ae49", "firstIndex": 12616465992},
|
||||
{"blockNumber": 18835044, "blockId": "0x49c5e07f409a841dc81f3ef8417f1951f8fcc13c90134f9d2a0cd11938f9fa36", "firstIndex": 12683575082},
|
||||
{"blockNumber": 18883308, "blockId": "0x386a58dd5f79a419eeb05075b07b3ff3bc836a265c9688854a504223b1d6a830", "firstIndex": 12750683753},
|
||||
{"blockNumber": 18933635, "blockId": "0xd3881292147589bd2e192769e5c9175b5d03a453fe1ef3c4b5b6858ac9402a2f", "firstIndex": 12817792470},
|
||||
{"blockNumber": 18988254, "blockId": "0xcbe72dfa15428ac21b9c59c703ceaa0eb4b2205927687261d7aaed3dbb3783ea", "firstIndex": 12884882858},
|
||||
{"blockNumber": 19041325, "blockId": "0x92b077e1c2f8819da728f0307c914fdcd57eba14ea07d9a45c28d1ed8ffff576", "firstIndex": 12952010530},
|
||||
{"blockNumber": 19089163, "blockId": "0x43f8ab2d3dfc34c8e18cba903074d54e235dc546f19c4eb78245a522c266c84e", "firstIndex": 13019119228},
|
||||
{"blockNumber": 19140629, "blockId": "0xab7b7ae5424b18105a13b657fa6099d4ab67fde5baff39fe6e4de707397e995c", "firstIndex": 13086228236},
|
||||
{"blockNumber": 19192118, "blockId": "0x451327e6a5cf6ce1c8c14c01687dc5f719f3c2176f46bac4f264616256e30d1c", "firstIndex": 13153337116},
|
||||
{"blockNumber": 19237836, "blockId": "0x9b260d6be369557d1dc88aca423e2697e697d941d1b726c183015b5649e248c8", "firstIndex": 13220445421},
|
||||
{"blockNumber": 19291271, "blockId": "0x4878c28d79e1f71bc11e062eb61cb52ae6a18b670b0f9bea38b477944615078e", "firstIndex": 13287554254},
|
||||
{"blockNumber": 19344448, "blockId": "0x56243b9ad863bf90953fe9aa6e64a426629384db1190e70dce79575d30595f7e", "firstIndex": 13354663659},
|
||||
{"blockNumber": 19394948, "blockId": "0x195173b64dda7908d6aa39a63c8bdd29ec181d401e369d513be1308550d0ddcb", "firstIndex": 13421771935},
|
||||
{"blockNumber": 19443075, "blockId": "0xd39c1d60996475e65d1ab5b4e755f510ca466564a8155d35db6667988d6c0e44", "firstIndex": 13488880427},
|
||||
{"blockNumber": 19488383, "blockId": "0x28956eb8856fa8db59c02585016b8baf43bc44bc35b00bdaf8a6babe51101c5c", "firstIndex": 13555977105},
|
||||
{"blockNumber": 19534584, "blockId": "0x2421c97b0f140185d4c20943cd4ed7d7424468482feb76e3003a1cc69da3fd7b", "firstIndex": 13623097580},
|
||||
{"blockNumber": 19579602, "blockId": "0x25f96529028e9f51c59aec9ce8de282b7dd67066fd46a1694130698ed0f40d8b", "firstIndex": 13690207623},
|
||||
{"blockNumber": 19621517, "blockId": "0x4f6f6e0a0488f3d51823bc4b07c292348c259b1866968f77ee76b66b37101c75", "firstIndex": 13757315529},
|
||||
{"blockNumber": 19665085, "blockId": "0x00f9315f89681b44bff46f1bad8894bc6dfae1c459d3d6520f9881861304a496", "firstIndex": 13824425382},
|
||||
{"blockNumber": 19709229, "blockId": "0x24e022b21ae1ba8a3e8c87cb9734aa1d1810fc4a69fe147d3ebb1ff0df8bcc15", "firstIndex": 13891534799},
|
||||
{"blockNumber": 19755387, "blockId": "0x77f184b7183b1a351760d242041249464b42cfaa6fbc4326f352b06bb3b21a02", "firstIndex": 13958642483},
|
||||
{"blockNumber": 19803894, "blockId": "0xf37eb1b054a6d61272940361f386eb744cded84d15c3250a7eabadede257371c", "firstIndex": 14025751618},
|
||||
{"blockNumber": 19847885, "blockId": "0x4659649fa8a3b4bbe8978673ba9a22ae20352c7052b676d373b5a51b1967ffa4", "firstIndex": 14092848654},
|
||||
{"blockNumber": 19894193, "blockId": "0x15606bdc0f1a710bd69443c7154d4979aece9329977b65990c9b39d6df84ed5c", "firstIndex": 14159970181},
|
||||
{"blockNumber": 19938551, "blockId": "0x6a8f4571924ed902bd8e71bf8ed9cc9d72cabeabc410277c8f0fc2b477d00eb7", "firstIndex": 14227077892},
|
||||
{"blockNumber": 19985354, "blockId": "0x7b6fb6376410b4d9e5d7ee02f78b2054e005dd2976eea47fc714f66b967dc285", "firstIndex": 14294187965},
|
||||
{"blockNumber": 20028440, "blockId": "0x9b37440b71c24756b8855b8012432b84276ae94c80aa1ccc8b70a7705992103c", "firstIndex": 14361296503},
|
||||
{"blockNumber": 20071780, "blockId": "0xa2ed129f343f3d60419772ec5635edcd36b8680c9419b6626e2bc84b230c709b", "firstIndex": 14428405230},
|
||||
{"blockNumber": 20113832, "blockId": "0xe7a610e8bcbf8ded141ebc7142de03dfc54b1bcc79e3bf8d07fad4e42b665bba", "firstIndex": 14495512019},
|
||||
{"blockNumber": 20156854, "blockId": "0xbe09704f65a70ef8843d9c8e511ddc989ea139dbe94cdfe37f52b03620d62385", "firstIndex": 14562622430},
|
||||
{"blockNumber": 20200135, "blockId": "0x9a58c34d5f77342e94065d119905c000223cd988c4b11f1539fff20737159630", "firstIndex": 14629731923},
|
||||
{"blockNumber": 20244389, "blockId": "0x1e733f0db9ef21183107259b3c2408c78fa5a01469928cd295f3ea7e8eedda45", "firstIndex": 14696840011},
|
||||
{"blockNumber": 20288489, "blockId": "0xb5ad7edd86b181226c8c7be0a08069e3955234e797426843fff9de0f57ec59cc", "firstIndex": 14763949714},
|
||||
{"blockNumber": 20333582, "blockId": "0x8040c209f5cd1738ee0f85c2f1db7c43a420d148680c7390fd1701b9f0bb671a", "firstIndex": 14831058335},
|
||||
{"blockNumber": 20377087, "blockId": "0x08fdc4cd246b6ae9d4a45646b0aed6af3bb330eb6cd4c8b93646157e7b002b84", "firstIndex": 14898167722},
|
||||
{"blockNumber": 20421699, "blockId": "0x5a2912b5fc2f02df33b655155990f92dcaacda5b75427fe3d87fb38f36b1c17d", "firstIndex": 14965275691},
|
||||
{"blockNumber": 20467194, "blockId": "0x3deaf4325c461004b090b0261996c645ab529c1471feaf7dc2bbe1f128180297", "firstIndex": 15032385211},
|
||||
{"blockNumber": 20512397, "blockId": "0x37e39697ec1b7683a6202be250ffaee7a1102e8030f87550b94af05ec66cec83", "firstIndex": 15099493973},
|
||||
{"blockNumber": 20557443, "blockId": "0x8e9c04468f3111eab8b1f6a58b277862c624861c237cadecc53ec249bd811bda", "firstIndex": 15166602882},
|
||||
{"blockNumber": 20595899, "blockId": "0x9787555fe57e4650002257eb2c88f1ef435b99d406e33fe2f889be180123ef25", "firstIndex": 15233709908},
|
||||
{"blockNumber": 20638606, "blockId": "0x70681cffd159ce2e580dbbbe8fa6b5343dbcb081429cdda6c577e615bef4ef05", "firstIndex": 15300820678},
|
||||
{"blockNumber": 20683605, "blockId": "0xb32662d5e241132ffe2249caea67f5746a6f4382297b2ac87c81e2794faf1f7a", "firstIndex": 15367929350},
|
||||
{"blockNumber": 20728630, "blockId": "0x15a817c846928b673032d5eacd0cff7a04217d268457aa30a322ecca32be4d49", "firstIndex": 15435037830},
|
||||
{"blockNumber": 20771519, "blockId": "0x542bc7b9804bbc45f4be470f4dc56f215a4dec71fed71eba2ffc804afd262b95", "firstIndex": 15502145990},
|
||||
{"blockNumber": 20815097, "blockId": "0x798cdd51c964fcf18561d70095d9613b84ba836817972799c9dfd0bfbe1e042b", "firstIndex": 15569256033},
|
||||
{"blockNumber": 20857859, "blockId": "0xfb5bb066d419a651d8e0186569eb4e8d8bcd5181d8f02e0d578b5dfe2fc738dd", "firstIndex": 15636364671},
|
||||
{"blockNumber": 20896890, "blockId": "0x834b8d6fad779e4cf8214128f6c93d7387b6d6279e517f6f0a284b5d831cc3ae", "firstIndex": 15703472902},
|
||||
{"blockNumber": 20939387, "blockId": "0x7adee7c78420c711efa216c61e0b561e581d7ff0331efd91ee16a609b34cfdc2", "firstIndex": 15770582325},
|
||||
{"blockNumber": 20981303, "blockId": "0x6f5d7b0cc6dad5eb258176e07de21795a8347d68f7303f06934046e0236bea6d", "firstIndex": 15837691713},
|
||||
{"blockNumber": 21023216, "blockId": "0x96cfe35a45df1297a36f42c59ebe706ab0473dfbf59ce910b5c5a8dbf696de1c", "firstIndex": 15904799667},
|
||||
{"blockNumber": 21068378, "blockId": "0x93753875ff330d922b23f823203198f3b1bb8833367c6b6a8f896ff54be2c12d", "firstIndex": 15971909040},
|
||||
{"blockNumber": 21112445, "blockId": "0x6ac02fa6ae486b86aba562eaf6f3d883befaa8ebedcfd8d74bdb7368d42deee3", "firstIndex": 16039003625},
|
||||
{"blockNumber": 21155992, "blockId": "0x25f76896b4b693bafb79e9a535e2bf00ed62a577e35209749346e8e79a60bb71", "firstIndex": 16106126344},
|
||||
{"blockNumber": 21200962, "blockId": "0x725f2befe913cb2659d262e2d3b6f79a706b31c557d52669471da22347ec8287", "firstIndex": 16173235265},
|
||||
{"blockNumber": 21244663, "blockId": "0x6778c4194f54e70939da38853daddb22bfaf160d35617ab05d0f5c476741147b", "firstIndex": 16240344735},
|
||||
{"blockNumber": 21290273, "blockId": "0x433ac819c40bd3061205fe0ece0645eec73f54a0a5c1559c981f983345bc0154", "firstIndex": 16307453543},
|
||||
{"blockNumber": 21336156, "blockId": "0x261dc8c1639d505624150d2388d15ed10bfb4c3ce9c0c327a4ec26531689a097", "firstIndex": 16374562466},
|
||||
{"blockNumber": 21378880, "blockId": "0x5c78b2b70553140dfdfdd4f415b98f88e74f74662315834038fd99042277d917", "firstIndex": 16441671104},
|
||||
{"blockNumber": 21421613, "blockId": "0x854532f9d1c77627b763f9cbc7099a653d59554ed57fa763bc218834c82955fe", "firstIndex": 16508780351},
|
||||
{"blockNumber": 21466875, "blockId": "0xb8b83cc62084e948235ef4b5973bf7fd988fa28bcaa72f7d38ad8e50de729618", "firstIndex": 16575888599},
|
||||
{"blockNumber": 21511942, "blockId": "0xe806a28bc1b7f8cd752c8ceedbe081d49773d4558a9fb95e3357c0c07172522d", "firstIndex": 16642996907},
|
||||
{"blockNumber": 21550291, "blockId": "0x1f3e26d303e7a2a9b0614f12f62b189da365b3947c5fe2d99ed2711b37fe7daa", "firstIndex": 16710106826},
|
||||
{"blockNumber": 21592690, "blockId": "0xa1408cfbc693faee4425e8fd9e83a181be535c33f874b56c3a7a114404c4f686", "firstIndex": 16777215566},
|
||||
{"blockNumber": 21636275, "blockId": "0x704734c2d0351f8ccd38721a9a4b80c063368afaaa857518d98498180a502bba", "firstIndex": 16844323959},
|
||||
{"blockNumber": 21681066, "blockId": "0x1e738568ed393395c498b109ad61c0286747318aae0364936f19a7b6aba94aef", "firstIndex": 16911433076},
|
||||
{"blockNumber": 21725592, "blockId": "0xee87b7948e25a7498a247c616a0fbaa27f21b004e11fc56f2a20c03791ed8122", "firstIndex": 16978540993}
|
||||
]
|
||||
|
||||
63
core/filtermaps/checkpoints_sepolia.json
Normal file
63
core/filtermaps/checkpoints_sepolia.json
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
[
|
||||
{"blockNumber": 3246675, "blockId": "0x36bf7de9e1f151963088ca3efa206b6e78411d699d2f64f3bf86895294275e0b", "firstIndex": 67107286},
|
||||
{"blockNumber": 3575582, "blockId": "0x08931012467636d3b67ae187790951daed2bb6423f9cd94e166df787b856788d", "firstIndex": 134217672},
|
||||
{"blockNumber": 3694264, "blockId": "0x1f35f276a3c78e5942ee285fcbd0c687691853c599a2f5b174ea88f653bc9514", "firstIndex": 201326578},
|
||||
{"blockNumber": 3725632, "blockId": "0x3bcb264c56c3eeab6c8588145f09dff3fb5f821d9fc1e7b92264b14314dae553", "firstIndex": 268433636},
|
||||
{"blockNumber": 3795390, "blockId": "0x2d1ef2815bb8e018b275fa65540b98265285016aff12596bd89a3b1442d248eb", "firstIndex": 335542953},
|
||||
{"blockNumber": 3856683, "blockId": "0x8a9a46d6f53975cd9ec829c3c307a99fb62b8428cefb63ffe06d17143649c3ee", "firstIndex": 402648835},
|
||||
{"blockNumber": 3869370, "blockId": "0x2e8c04e7e5e96d09260b65d77b1770b4105b0db2ee7d638c48f086b8afac17db", "firstIndex": 469759276},
|
||||
{"blockNumber": 3938357, "blockId": "0xf20f2cdbcc412d5340e31955d14a6526ea748ba99b5ec70b6615bdb18bcd4cfb", "firstIndex": 536868027},
|
||||
{"blockNumber": 3984894, "blockId": "0x0bcd886b3cebb884d5beeaf5ad15ee1514968b5ad07177297c7d9c00f27aa406", "firstIndex": 603968430},
|
||||
{"blockNumber": 4002664, "blockId": "0x7d3575b6ca685468fa5a5fa9ff9d5fac4415b0a67a3ed87d3530f127db32fff4", "firstIndex": 671088417},
|
||||
{"blockNumber": 4113187, "blockId": "0x3a5313ac5b602134bb73535b22801261e891ccb7bd660ab20e0a536dc46d3e13", "firstIndex": 738197016},
|
||||
{"blockNumber": 4260758, "blockId": "0xe30fb9a304d3602896a5716d310f67ba34ccef7f809a3ead4b2d991cb9ee4eb0", "firstIndex": 805306270},
|
||||
{"blockNumber": 4391131, "blockId": "0x3958478c1c3be9b7caedbcc96230ed446d711e56580e324bc2fcf903fc87c90f", "firstIndex": 872415115},
|
||||
{"blockNumber": 4515650, "blockId": "0x46a3a7b97a9dff4ef4dc2c1cc5cd501f2182d9548655b77b5e07a2dbb41071a4", "firstIndex": 939523930},
|
||||
{"blockNumber": 4634818, "blockId": "0x2197d0dd3925c1d7ba3e2c4eef20035b68efc0a2506f76ddd9e481e0ce8ca6e1", "firstIndex": 1006628557},
|
||||
{"blockNumber": 4718295, "blockId": "0xcce7bb4af1a41e6056ef68192e60c738be01ac3e071ed1ec52cead08a39995ce", "firstIndex": 1073734698},
|
||||
{"blockNumber": 4753438, "blockId": "0xa60e043728a369cdf39a399bd7a903085ee9386f38176947578e5692b4b01f65", "firstIndex": 1140843192},
|
||||
{"blockNumber": 4786522, "blockId": "0x10629cadc00e65f193fa4d10ecd2bf1855e442814c4a409d19aae9eb895dce13", "firstIndex": 1207956586},
|
||||
{"blockNumber": 4811706, "blockId": "0xf1e94111f0086733bdcb4a653486a8b94ec998b61dda0af0fd465c9b4e344f87", "firstIndex": 1275058221},
|
||||
{"blockNumber": 4841796, "blockId": "0xa530f7dd72881ac831affdc579c9d75f6d4b6853b1f1894d320bd9047df5f9eb", "firstIndex": 1342177155},
|
||||
{"blockNumber": 4914835, "blockId": "0xbd8321e354f72c4190225f8ed63d4aba794b3b568677d985e099cb62d9d36bae", "firstIndex": 1409286143},
|
||||
{"blockNumber": 4992519, "blockId": "0x4a06a5a4aa5bc52151937cc1c0f8da691a0282e94aab8b73b9faa87da8d028de", "firstIndex": 1476384367},
|
||||
{"blockNumber": 5088668, "blockId": "0xb7d5ee03c08ed3936348eeb3931be8f804e61f2b09debf305967c6a7bbf007e0", "firstIndex": 1543502599},
|
||||
{"blockNumber": 5155029, "blockId": "0x84f590dfc2e11f1ca53c1757ac3c508d56f55ee24d6ca5501895974be4250d76", "firstIndex": 1610605837},
|
||||
{"blockNumber": 5204413, "blockId": "0xeaf2c3fb6f927c16d38fab08b34303867b87470558612404c7f9e3256b80c5b9", "firstIndex": 1677720841},
|
||||
{"blockNumber": 5269957, "blockId": "0x596e0b2e8e4c18c803b61767320fe32c063153d870c94e4a08e9a68cbaa582a9", "firstIndex": 1744825147},
|
||||
{"blockNumber": 5337678, "blockId": "0x7b2d54f8af1ecaaaab994e137d4421d8236c1c10d9a7bdcb9e5500db7a3fe9a3", "firstIndex": 1811939316},
|
||||
{"blockNumber": 5399058, "blockId": "0xb61ef16d55c96682fb62b0110a2dbc50d8eff2526be4121ece3690700611c71b", "firstIndex": 1879046044},
|
||||
{"blockNumber": 5422707, "blockId": "0xdabcab7c0cc9cb9f22f7507a1076c87831cb1afed9d0aa5bcd93f22266720c91", "firstIndex": 1946156915},
|
||||
{"blockNumber": 5454264, "blockId": "0xe1bde812906605ce662f5fd9f01b49c7331fb25f52ab5b12d35ea2b4da5458fe", "firstIndex": 2013259168},
|
||||
{"blockNumber": 5498898, "blockId": "0x9533d9c5353d22f8a235e95831cfbf4d5a7220a430ca23494526a9d3aa866fe8", "firstIndex": 2080374321},
|
||||
{"blockNumber": 5554801, "blockId": "0xe7b320bbecb19f1e99dd6ce4aed1efc754d7b2022e1f80389e8a21413c465f55", "firstIndex": 2147476253},
|
||||
{"blockNumber": 5594725, "blockId": "0xce6750be4a5b3e0fe152dd02308e94f7d56b254852a7e9acef6e14105053d7d1", "firstIndex": 2214591591},
|
||||
{"blockNumber": 5645198, "blockId": "0x5d42d39999c546f37001d5f613732fb54032384dd71a686d3664d2c8a1337752", "firstIndex": 2281696503},
|
||||
{"blockNumber": 5687659, "blockId": "0x3ed941be39a33ffa69cf3531a67f5a25f712ba05db890ff377f60d26842e4b1c", "firstIndex": 2348801751},
|
||||
{"blockNumber": 5727823, "blockId": "0xaf699b6c4cd58181bd609a66990b8edb5d1b94d5ff1ab732ded35ce7b8373353", "firstIndex": 2415917178},
|
||||
{"blockNumber": 5784505, "blockId": "0x621c740d04ea41f70a2f0537e21e5b96169aea8a8efee0ae5527717e5c40aa64", "firstIndex": 2483024581},
|
||||
{"blockNumber": 5843958, "blockId": "0xec122204a4e4698748f55a1c9f8582c46bacda029aee4de1a234e67e3288e6b1", "firstIndex": 2550136761},
|
||||
{"blockNumber": 5906359, "blockId": "0x8af5ce73fbd7a6110fb8b19b75a7322456ece88fcfa1614c745f1a65f4e915c1", "firstIndex": 2617245617},
|
||||
{"blockNumber": 5977944, "blockId": "0xbc8186258298a4f376124989cfb7b22c2bea6603a5245bb6c505c5fc45844bbd", "firstIndex": 2684350982},
|
||||
{"blockNumber": 6051571, "blockId": "0x54f9df9d9d73d1aa1cfcd6f372377c6013ecba2a1ed158d3c304f4fca51dae58", "firstIndex": 2751463209},
|
||||
{"blockNumber": 6118461, "blockId": "0xfea757fad3f763c62a514a9c24924934539ca56620bd811f83e9cc2e671f0cf0", "firstIndex": 2818572283},
|
||||
{"blockNumber": 6174385, "blockId": "0x2d8d0226e58f7516c13f9e1c9cf3ea65bb520fa1dfd7249dc9ea34a4e1fd430d", "firstIndex": 2885681036},
|
||||
{"blockNumber": 6276318, "blockId": "0xa922e9d54fd062b658c4866ed218632ddd51f250d671628a42968bb912d3ed5d", "firstIndex": 2952789983},
|
||||
{"blockNumber": 6368452, "blockId": "0x8d3d7466a7c9ca7298f82c37c38b0f64ec04522d2ed2e2349f8edc020c57f2c4", "firstIndex": 3019898695},
|
||||
{"blockNumber": 6470810, "blockId": "0x9887c35542835ee81153fa0e4d8a9e6f170b6e14fc78d8c7f3d900d0a70434f1", "firstIndex": 3087007578},
|
||||
{"blockNumber": 6553334, "blockId": "0x7b0d89a0282c18785fcc108dbdc9d45dd9d63b7084ddc676df9e9504585a5969", "firstIndex": 3154115987},
|
||||
{"blockNumber": 6663825, "blockId": "0xff6cec99324a89d6d36275c17a4569f0cba203fe5b0350f155a7d5445e0ed419", "firstIndex": 3221224775},
|
||||
{"blockNumber": 6767082, "blockId": "0xe10a96a7194f98bf262f0cb1cdfb4d3b9a2072139dfcbe3f1eb01419e353044e", "firstIndex": 3288334139},
|
||||
{"blockNumber": 6886709, "blockId": "0x20f6a5d986913025ad5b6b6387d818e49a3caf838326f4002c1439ca61313be5", "firstIndex": 3355442979},
|
||||
{"blockNumber": 6978948, "blockId": "0xd7c3024765245ec49e6a48b076d540bc91f57f2ccc125e17d60dd37bb669f843", "firstIndex": 3422551908},
|
||||
{"blockNumber": 7098891, "blockId": "0x05114c037e1b4d69a46d74a974be9bce45e87ad2226a59b44dd17f98dd2fd0d1", "firstIndex": 3489659530},
|
||||
{"blockNumber": 7203157, "blockId": "0xc0f610014fcd9f2850274b58179d474f0947676fd0639b2884316467c631811d", "firstIndex": 3556769512},
|
||||
{"blockNumber": 7256735, "blockId": "0x0324c15b3b23fd82c2962dd167618e77e60ebeac5a2c87f672caddc9732337b3", "firstIndex": 3623876508},
|
||||
{"blockNumber": 7307851, "blockId": "0x8e23280d1a3aec877d7758413ed20299d381aa43e7e2fc6f381ad96e8ff0acef", "firstIndex": 3690987098},
|
||||
{"blockNumber": 7369389, "blockId": "0xbf6436eb2b88539945d6673141a14cb79ffc1e7db2b57176acf8e02ff3b6fcd3", "firstIndex": 3758096287},
|
||||
{"blockNumber": 7445220, "blockId": "0x147619f74815283d834ac08ff494fb4791207b3949c64b2623f11ff6141ee7a7", "firstIndex": 3825204992},
|
||||
{"blockNumber": 7511632, "blockId": "0x5094d64868f419e6ac3d253d19d5feda76564a0d56d7bbf8a822dff1c2261b30", "firstIndex": 3892314047},
|
||||
{"blockNumber": 7557280, "blockId": "0x54aba9351a1ba51873645221aa7c991024da1fe468a600ddb6e2559351d9c28f", "firstIndex": 3959422859},
|
||||
{"blockNumber": 7606304, "blockId": "0xbbe2fed08cf0b0ff2cb6ae9fd7257843f77a04a7d4cafb06d7a4bedea6ab0c98", "firstIndex": 4026531690}
|
||||
]
|
||||
|
||||
726
core/filtermaps/filtermaps.go
Normal file
726
core/filtermaps/filtermaps.go
Normal file
|
|
@ -0,0 +1,726 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/leveldb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
const (
|
||||
cachedLastBlocks = 1000 // last block of map pointers
|
||||
cachedLvPointers = 1000 // first log value pointer of block pointers
|
||||
cachedBaseRows = 100 // groups of base layer filter row data
|
||||
cachedFilterMaps = 3 // complete filter maps (cached by map renderer)
|
||||
cachedRenderSnapshots = 8 // saved map renderer data at block boundaries
|
||||
)
|
||||
|
||||
// FilterMaps is the in-memory representation of the log index structure that is
|
||||
// responsible for building and updating the index according to the canonical
|
||||
// chain.
|
||||
//
|
||||
// Note that FilterMaps implements the same data structure as proposed in EIP-7745
|
||||
// without the tree hashing and consensus changes:
|
||||
// https://eips.ethereum.org/EIPS/eip-7745
|
||||
type FilterMaps struct {
|
||||
// If disabled is set, log indexing is fully disabled.
|
||||
// This is configured by the --history.logs.disable Geth flag.
|
||||
// We chose to implement disabling this way because it requires less special
|
||||
// case logic in eth/filters.
|
||||
disabled bool
|
||||
|
||||
closeCh chan struct{}
|
||||
closeWg sync.WaitGroup
|
||||
history uint64
|
||||
exportFileName string
|
||||
Params
|
||||
|
||||
db ethdb.KeyValueStore
|
||||
|
||||
// fields written by the indexer and read by matcher backend. Indexer can
|
||||
// read them without a lock and write them under indexLock write lock.
|
||||
// Matcher backend can read them under indexLock read lock.
|
||||
indexLock sync.RWMutex
|
||||
indexedRange filterMapsRange
|
||||
indexedView *ChainView // always consistent with the log index
|
||||
|
||||
// also accessed by indexer and matcher backend but no locking needed.
|
||||
filterMapCache *lru.Cache[uint32, filterMap]
|
||||
lastBlockCache *lru.Cache[uint32, lastBlockOfMap]
|
||||
lvPointerCache *lru.Cache[uint64, uint64]
|
||||
baseRowsCache *lru.Cache[uint64, [][]uint32]
|
||||
|
||||
// the matchers set and the fields of FilterMapsMatcherBackend instances are
|
||||
// read and written both by exported functions and the indexer.
|
||||
// Note that if both indexLock and matchersLock needs to be locked then
|
||||
// indexLock should be locked first.
|
||||
matchersLock sync.Mutex
|
||||
matchers map[*FilterMapsMatcherBackend]struct{}
|
||||
|
||||
// fields only accessed by the indexer (no mutex required).
|
||||
renderSnapshots *lru.Cache[uint64, *renderedMap]
|
||||
startedHeadIndex, startedTailIndex, startedTailUnindex bool
|
||||
startedHeadIndexAt, startedTailIndexAt, startedTailUnindexAt time.Time
|
||||
loggedHeadIndex, loggedTailIndex bool
|
||||
lastLogHeadIndex, lastLogTailIndex time.Time
|
||||
ptrHeadIndex, ptrTailIndex, ptrTailUnindexBlock uint64
|
||||
ptrTailUnindexMap uint32
|
||||
|
||||
targetView *ChainView
|
||||
matcherSyncRequest *FilterMapsMatcherBackend
|
||||
finalBlock, lastFinal uint64
|
||||
lastFinalEpoch uint32
|
||||
stop bool
|
||||
targetViewCh chan *ChainView
|
||||
finalBlockCh chan uint64
|
||||
blockProcessingCh chan bool
|
||||
blockProcessing bool
|
||||
matcherSyncCh chan *FilterMapsMatcherBackend
|
||||
waitIdleCh chan chan bool
|
||||
tailRenderer *mapRenderer
|
||||
|
||||
// test hooks
|
||||
testDisableSnapshots, testSnapshotUsed bool
|
||||
}
|
||||
|
||||
// filterMap is a full or partial in-memory representation of a filter map where
|
||||
// rows are allowed to have a nil value meaning the row is not stored in the
|
||||
// structure. Note that therefore a known empty row should be represented with
|
||||
// a zero-length slice.
|
||||
// It can be used as a memory cache or an overlay while preparing a batch of
|
||||
// changes to the structure. In either case a nil value should be interpreted
|
||||
// as transparent (uncached/unchanged).
|
||||
type filterMap []FilterRow
|
||||
|
||||
// copy returns a copy of the given filter map. Note that the row slices are
|
||||
// copied but their contents are not. This permits extending the rows further
|
||||
// (which happens during map rendering) without affecting the validity of
|
||||
// copies made for snapshots during rendering.
|
||||
func (fm filterMap) copy() filterMap {
|
||||
c := make(filterMap, len(fm))
|
||||
copy(c, fm)
|
||||
return c
|
||||
}
|
||||
|
||||
// FilterRow encodes a single row of a filter map as a list of column indices.
|
||||
// Note that the values are always stored in the same order as they were added
|
||||
// and if the same column index is added twice, it is also stored twice.
|
||||
// Order of column indices and potential duplications do not matter when searching
|
||||
// for a value but leaving the original order makes reverting to a previous state
|
||||
// simpler.
|
||||
type FilterRow []uint32
|
||||
|
||||
// Equal returns true if the given filter rows are equivalent.
|
||||
func (a FilterRow) Equal(b FilterRow) bool {
|
||||
return slices.Equal(a, b)
|
||||
}
|
||||
|
||||
// filterMapsRange describes the rendered range of filter maps and the range
|
||||
// of fully rendered blocks.
|
||||
type filterMapsRange struct {
|
||||
initialized bool
|
||||
headBlockIndexed bool
|
||||
headBlockDelimiter uint64 // zero if afterLastIndexedBlock != targetBlockNumber
|
||||
// if initialized then all maps are rendered between firstRenderedMap and
|
||||
// afterLastRenderedMap-1
|
||||
firstRenderedMap, afterLastRenderedMap uint32
|
||||
// if tailPartialEpoch > 0 then maps between firstRenderedMap-mapsPerEpoch and
|
||||
// firstRenderedMap-mapsPerEpoch+tailPartialEpoch-1 are rendered
|
||||
tailPartialEpoch uint32
|
||||
// if initialized then all log values belonging to blocks between
|
||||
// firstIndexedBlock and afterLastIndexedBlock are fully rendered
|
||||
// blockLvPointers are available between firstIndexedBlock and afterLastIndexedBlock-1
|
||||
firstIndexedBlock, afterLastIndexedBlock uint64
|
||||
}
|
||||
|
||||
// hasIndexedBlocks returns true if the range has at least one fully indexed block.
|
||||
func (fmr *filterMapsRange) hasIndexedBlocks() bool {
|
||||
return fmr.initialized && fmr.afterLastIndexedBlock > fmr.firstIndexedBlock
|
||||
}
|
||||
|
||||
// lastBlockOfMap is used for caching the (number, id) pairs belonging to the
|
||||
// last block of each map.
|
||||
type lastBlockOfMap struct {
|
||||
number uint64
|
||||
id common.Hash
|
||||
}
|
||||
|
||||
// Config contains the configuration options for NewFilterMaps.
|
||||
type Config struct {
|
||||
History uint64 // number of historical blocks to index
|
||||
Disabled bool // disables indexing completely
|
||||
|
||||
// This option enables the checkpoint JSON file generator.
|
||||
// If set, the given file will be updated with checkpoint information.
|
||||
ExportFileName string
|
||||
}
|
||||
|
||||
// NewFilterMaps creates a new FilterMaps and starts the indexer.
|
||||
func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, params Params, config Config) *FilterMaps {
|
||||
rs, initialized, err := rawdb.ReadFilterMapsRange(db)
|
||||
if err != nil {
|
||||
log.Error("Error reading log index range", "error", err)
|
||||
}
|
||||
params.deriveFields()
|
||||
f := &FilterMaps{
|
||||
db: db,
|
||||
closeCh: make(chan struct{}),
|
||||
waitIdleCh: make(chan chan bool),
|
||||
targetViewCh: make(chan *ChainView, 1),
|
||||
finalBlockCh: make(chan uint64, 1),
|
||||
blockProcessingCh: make(chan bool, 1),
|
||||
history: config.History,
|
||||
disabled: config.Disabled,
|
||||
exportFileName: config.ExportFileName,
|
||||
Params: params,
|
||||
indexedRange: filterMapsRange{
|
||||
initialized: initialized,
|
||||
headBlockIndexed: rs.HeadBlockIndexed,
|
||||
headBlockDelimiter: rs.HeadBlockDelimiter,
|
||||
firstIndexedBlock: rs.FirstIndexedBlock,
|
||||
afterLastIndexedBlock: rs.AfterLastIndexedBlock,
|
||||
firstRenderedMap: rs.FirstRenderedMap,
|
||||
afterLastRenderedMap: rs.AfterLastRenderedMap,
|
||||
tailPartialEpoch: rs.TailPartialEpoch,
|
||||
},
|
||||
matcherSyncCh: make(chan *FilterMapsMatcherBackend),
|
||||
matchers: make(map[*FilterMapsMatcherBackend]struct{}),
|
||||
filterMapCache: lru.NewCache[uint32, filterMap](cachedFilterMaps),
|
||||
lastBlockCache: lru.NewCache[uint32, lastBlockOfMap](cachedLastBlocks),
|
||||
lvPointerCache: lru.NewCache[uint64, uint64](cachedLvPointers),
|
||||
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
||||
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
||||
}
|
||||
|
||||
// Set initial indexer target.
|
||||
f.targetView = initView
|
||||
if f.indexedRange.initialized {
|
||||
f.indexedView = f.initChainView(f.targetView)
|
||||
f.indexedRange.headBlockIndexed = f.indexedRange.afterLastIndexedBlock == f.indexedView.headNumber+1
|
||||
if !f.indexedRange.headBlockIndexed {
|
||||
f.indexedRange.headBlockDelimiter = 0
|
||||
}
|
||||
}
|
||||
if f.indexedRange.hasIndexedBlocks() {
|
||||
log.Info("Initialized log indexer",
|
||||
"first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1,
|
||||
"first map", f.indexedRange.firstRenderedMap, "last map", f.indexedRange.afterLastRenderedMap-1,
|
||||
"head indexed", f.indexedRange.headBlockIndexed)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Start starts the indexer.
|
||||
func (f *FilterMaps) Start() {
|
||||
if !f.testDisableSnapshots && f.indexedRange.initialized && f.indexedRange.headBlockIndexed &&
|
||||
f.indexedRange.firstRenderedMap < f.indexedRange.afterLastRenderedMap {
|
||||
// previous target head rendered; load last map as snapshot
|
||||
if err := f.loadHeadSnapshot(); err != nil {
|
||||
log.Error("Could not load head filter map snapshot", "error", err)
|
||||
}
|
||||
}
|
||||
f.closeWg.Add(1)
|
||||
go f.indexerLoop()
|
||||
}
|
||||
|
||||
// Stop ensures that the indexer is fully stopped before returning.
|
||||
func (f *FilterMaps) Stop() {
|
||||
close(f.closeCh)
|
||||
f.closeWg.Wait()
|
||||
}
|
||||
|
||||
// initChainView returns a chain view consistent with both the current target
|
||||
// view and the current state of the log index as found in the database, based
|
||||
// on the last block of stored maps.
|
||||
// Note that the returned view might be shorter than the existing index if
|
||||
// the latest maps are not consistent with targetView.
|
||||
func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView {
|
||||
mapIndex := f.indexedRange.afterLastRenderedMap
|
||||
for {
|
||||
var ok bool
|
||||
mapIndex, ok = f.lastMapBoundaryBefore(mapIndex)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(mapIndex)
|
||||
if err != nil {
|
||||
log.Error("Could not initialize indexed chain view", "error", err)
|
||||
break
|
||||
}
|
||||
if lastBlockNumber <= chainView.headNumber && chainView.getBlockId(lastBlockNumber) == lastBlockId {
|
||||
return chainView.limitedView(lastBlockNumber)
|
||||
}
|
||||
}
|
||||
return chainView.limitedView(0)
|
||||
}
|
||||
|
||||
// reset un-initializes the FilterMaps structure and removes all related data from
|
||||
// the database. The function returns true if everything was successfully removed.
|
||||
func (f *FilterMaps) reset() bool {
|
||||
f.indexLock.Lock()
|
||||
f.indexedRange = filterMapsRange{}
|
||||
f.indexedView = nil
|
||||
f.filterMapCache.Purge()
|
||||
f.renderSnapshots.Purge()
|
||||
f.lastBlockCache.Purge()
|
||||
f.lvPointerCache.Purge()
|
||||
f.baseRowsCache.Purge()
|
||||
f.indexLock.Unlock()
|
||||
// deleting the range first ensures that resetDb will be called again at next
|
||||
// startup and any leftover data will be removed even if it cannot finish now.
|
||||
rawdb.DeleteFilterMapsRange(f.db)
|
||||
return f.removeDbWithPrefix([]byte(rawdb.FilterMapsPrefix), "Resetting log index database")
|
||||
}
|
||||
|
||||
// init initializes an empty log index according to the current targetView.
|
||||
func (f *FilterMaps) init() error {
|
||||
f.indexLock.Lock()
|
||||
defer f.indexLock.Unlock()
|
||||
|
||||
var bestIdx, bestLen int
|
||||
for idx, checkpointList := range checkpoints {
|
||||
// binary search for the last matching epoch head
|
||||
min, max := 0, len(checkpointList)
|
||||
for min < max {
|
||||
mid := (min + max + 1) / 2
|
||||
cp := checkpointList[mid-1]
|
||||
if cp.BlockNumber <= f.targetView.headNumber && f.targetView.getBlockId(cp.BlockNumber) == cp.BlockId {
|
||||
min = mid
|
||||
} else {
|
||||
max = mid - 1
|
||||
}
|
||||
}
|
||||
if max > bestLen {
|
||||
bestIdx, bestLen = idx, max
|
||||
}
|
||||
}
|
||||
batch := f.db.NewBatch()
|
||||
for epoch := range bestLen {
|
||||
cp := checkpoints[bestIdx][epoch]
|
||||
f.storeLastBlockOfMap(batch, (uint32(epoch+1)<<f.logMapsPerEpoch)-1, cp.BlockNumber, cp.BlockId)
|
||||
f.storeBlockLvPointer(batch, cp.BlockNumber, cp.FirstIndex)
|
||||
}
|
||||
fmr := filterMapsRange{
|
||||
initialized: true,
|
||||
}
|
||||
if bestLen > 0 {
|
||||
cp := checkpoints[bestIdx][bestLen-1]
|
||||
fmr.firstIndexedBlock = cp.BlockNumber + 1
|
||||
fmr.afterLastIndexedBlock = cp.BlockNumber + 1
|
||||
fmr.firstRenderedMap = uint32(bestLen) << f.logMapsPerEpoch
|
||||
fmr.afterLastRenderedMap = uint32(bestLen) << f.logMapsPerEpoch
|
||||
}
|
||||
f.setRange(batch, f.targetView, fmr)
|
||||
return batch.Write()
|
||||
}
|
||||
|
||||
// removeDbWithPrefix removes data with the given prefix from the database and
|
||||
// returns true if everything was successfully removed.
|
||||
func (f *FilterMaps) removeDbWithPrefix(prefix []byte, action string) bool {
|
||||
it := f.db.NewIterator(prefix, nil)
|
||||
hasData := it.Next()
|
||||
it.Release()
|
||||
if !hasData {
|
||||
return true
|
||||
}
|
||||
|
||||
end := bytes.Clone(prefix)
|
||||
end[len(end)-1]++
|
||||
start := time.Now()
|
||||
var retry bool
|
||||
for {
|
||||
err := f.db.DeleteRange(prefix, end)
|
||||
if err == nil {
|
||||
log.Info(action+" finished", "elapsed", time.Since(start))
|
||||
return true
|
||||
}
|
||||
if err != leveldb.ErrTooManyKeys {
|
||||
log.Error(action+" failed", "error", err)
|
||||
return false
|
||||
}
|
||||
select {
|
||||
case <-f.closeCh:
|
||||
return false
|
||||
default:
|
||||
}
|
||||
if !retry {
|
||||
log.Info(action + " in progress...")
|
||||
retry = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setRange updates the indexed chain view and covered range and also adds the
|
||||
// changes to the given batch.
|
||||
// Note that this function assumes that the index write lock is being held.
|
||||
func (f *FilterMaps) setRange(batch ethdb.KeyValueWriter, newView *ChainView, newRange filterMapsRange) {
|
||||
f.indexedView = newView
|
||||
f.indexedRange = newRange
|
||||
f.updateMatchersValidRange()
|
||||
if newRange.initialized {
|
||||
rs := rawdb.FilterMapsRange{
|
||||
HeadBlockIndexed: newRange.headBlockIndexed,
|
||||
HeadBlockDelimiter: newRange.headBlockDelimiter,
|
||||
FirstIndexedBlock: newRange.firstIndexedBlock,
|
||||
AfterLastIndexedBlock: newRange.afterLastIndexedBlock,
|
||||
FirstRenderedMap: newRange.firstRenderedMap,
|
||||
AfterLastRenderedMap: newRange.afterLastRenderedMap,
|
||||
TailPartialEpoch: newRange.tailPartialEpoch,
|
||||
}
|
||||
rawdb.WriteFilterMapsRange(batch, rs)
|
||||
} else {
|
||||
rawdb.DeleteFilterMapsRange(batch)
|
||||
}
|
||||
}
|
||||
|
||||
// getLogByLvIndex returns the log at the given log value index. If the index does
|
||||
// not point to the first log value entry of a log then no log and no error are
|
||||
// returned as this can happen when the log value index was a false positive.
|
||||
// Note that this function assumes that the log index structure is consistent
|
||||
// with the canonical chain at the point where the given log value index points.
|
||||
// If this is not the case then an invalid result or an error may be returned.
|
||||
// Note that this function assumes that the indexer read lock is being held when
|
||||
// called from outside the indexerLoop goroutine.
|
||||
func (f *FilterMaps) getLogByLvIndex(lvIndex uint64) (*types.Log, error) {
|
||||
mapIndex := uint32(lvIndex >> f.logValuesPerMap)
|
||||
if mapIndex < f.indexedRange.firstRenderedMap || mapIndex >= f.indexedRange.afterLastRenderedMap {
|
||||
return nil, nil
|
||||
}
|
||||
// find possible block range based on map to block pointers
|
||||
lastBlockNumber, _, err := f.getLastBlockOfMap(mapIndex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve last block of map %d containing searched log value index %d: %v", mapIndex, lvIndex, err)
|
||||
}
|
||||
var firstBlockNumber uint64
|
||||
if mapIndex > 0 {
|
||||
firstBlockNumber, _, err = f.getLastBlockOfMap(mapIndex - 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve last block of map %d before searched log value index %d: %v", mapIndex, lvIndex, err)
|
||||
}
|
||||
}
|
||||
if firstBlockNumber < f.indexedRange.firstIndexedBlock {
|
||||
firstBlockNumber = f.indexedRange.firstIndexedBlock
|
||||
}
|
||||
// find block with binary search based on block to log value index pointers
|
||||
for firstBlockNumber < lastBlockNumber {
|
||||
midBlockNumber := (firstBlockNumber + lastBlockNumber + 1) / 2
|
||||
midLvPointer, err := f.getBlockLvPointer(midBlockNumber)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve log value pointer of block %d while binary searching log value index %d: %v", midBlockNumber, lvIndex, err)
|
||||
}
|
||||
if lvIndex < midLvPointer {
|
||||
lastBlockNumber = midBlockNumber - 1
|
||||
} else {
|
||||
firstBlockNumber = midBlockNumber
|
||||
}
|
||||
}
|
||||
// get block receipts
|
||||
receipts := f.indexedView.getReceipts(firstBlockNumber)
|
||||
if receipts == nil {
|
||||
return nil, fmt.Errorf("failed to retrieve receipts for block %d containing searched log value index %d: %v", firstBlockNumber, lvIndex, err)
|
||||
}
|
||||
lvPointer, err := f.getBlockLvPointer(firstBlockNumber)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve log value pointer of block %d containing searched log value index %d: %v", firstBlockNumber, lvIndex, err)
|
||||
}
|
||||
// iterate through receipts to find the exact log starting at lvIndex
|
||||
for _, receipt := range receipts {
|
||||
for _, log := range receipt.Logs {
|
||||
if lvPointer > lvIndex {
|
||||
// lvIndex does not point to the first log value (address value)
|
||||
// generated by a log as true matches should always do, so it
|
||||
// is considered a false positive (no log and no error returned).
|
||||
return nil, nil
|
||||
}
|
||||
if lvPointer == lvIndex {
|
||||
return log, nil // potential match
|
||||
}
|
||||
lvPointer += uint64(len(log.Topics) + 1)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// getFilterMap fetches an entire filter map from the database.
|
||||
func (f *FilterMaps) getFilterMap(mapIndex uint32) (filterMap, error) {
|
||||
if fm, ok := f.filterMapCache.Get(mapIndex); ok {
|
||||
return fm, nil
|
||||
}
|
||||
fm := make(filterMap, f.mapHeight)
|
||||
for rowIndex := range fm {
|
||||
var err error
|
||||
fm[rowIndex], err = f.getFilterMapRow(mapIndex, uint32(rowIndex), false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load filter map %d from database: %v", mapIndex, err)
|
||||
}
|
||||
}
|
||||
f.filterMapCache.Add(mapIndex, fm)
|
||||
return fm, nil
|
||||
}
|
||||
|
||||
// getFilterMapRow fetches the given filter map row. If baseLayerOnly is true
|
||||
// then only the first baseRowLength entries are returned.
|
||||
func (f *FilterMaps) getFilterMapRow(mapIndex, rowIndex uint32, baseLayerOnly bool) (FilterRow, error) {
|
||||
baseMapRowIndex := f.mapRowIndex(mapIndex&-f.baseRowGroupLength, rowIndex)
|
||||
baseRows, ok := f.baseRowsCache.Get(baseMapRowIndex)
|
||||
if !ok {
|
||||
var err error
|
||||
baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve filter map %d base rows %d: %v", mapIndex, rowIndex, err)
|
||||
}
|
||||
f.baseRowsCache.Add(baseMapRowIndex, baseRows)
|
||||
}
|
||||
baseRow := baseRows[mapIndex&(f.baseRowGroupLength-1)]
|
||||
if baseLayerOnly {
|
||||
return baseRow, nil
|
||||
}
|
||||
extRow, err := rawdb.ReadFilterMapExtRow(f.db, f.mapRowIndex(mapIndex, rowIndex), f.logMapWidth)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve filter map %d extended row %d: %v", mapIndex, rowIndex, err)
|
||||
}
|
||||
return FilterRow(append(baseRow, extRow...)), nil
|
||||
}
|
||||
|
||||
// storeFilterMapRows stores a set of filter map rows at the corresponding map
|
||||
// indices and a shared row index.
|
||||
func (f *FilterMaps) storeFilterMapRows(batch ethdb.Batch, mapIndices []uint32, rowIndex uint32, rows []FilterRow) error {
|
||||
for len(mapIndices) > 0 {
|
||||
baseMapIndex := mapIndices[0] & -f.baseRowGroupLength
|
||||
groupLength := 1
|
||||
for groupLength < len(mapIndices) && mapIndices[groupLength]&-f.baseRowGroupLength == baseMapIndex {
|
||||
groupLength++
|
||||
}
|
||||
if err := f.storeFilterMapRowsOfGroup(batch, mapIndices[:groupLength], rowIndex, rows[:groupLength]); err != nil {
|
||||
return err
|
||||
}
|
||||
mapIndices, rows = mapIndices[groupLength:], rows[groupLength:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// storeFilterMapRowsOfGroup stores a set of filter map rows at map indices
|
||||
// belonging to the same base row group.
|
||||
func (f *FilterMaps) storeFilterMapRowsOfGroup(batch ethdb.Batch, mapIndices []uint32, rowIndex uint32, rows []FilterRow) error {
|
||||
baseMapIndex := mapIndices[0] & -f.baseRowGroupLength
|
||||
baseMapRowIndex := f.mapRowIndex(baseMapIndex, rowIndex)
|
||||
var baseRows [][]uint32
|
||||
if uint32(len(mapIndices)) != f.baseRowGroupLength { // skip base rows read if all rows are replaced
|
||||
var ok bool
|
||||
baseRows, ok = f.baseRowsCache.Get(baseMapRowIndex)
|
||||
if !ok {
|
||||
var err error
|
||||
baseRows, err = rawdb.ReadFilterMapBaseRows(f.db, baseMapRowIndex, f.baseRowGroupLength, f.logMapWidth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve filter map %d base rows %d for modification: %v", mapIndices[0]&-f.baseRowGroupLength, rowIndex, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
baseRows = make([][]uint32, f.baseRowGroupLength)
|
||||
}
|
||||
for i, mapIndex := range mapIndices {
|
||||
if mapIndex&-f.baseRowGroupLength != baseMapIndex {
|
||||
panic("mapIndices are not in the same base row group")
|
||||
}
|
||||
baseRow := []uint32(rows[i])
|
||||
var extRow FilterRow
|
||||
if uint32(len(rows[i])) > f.baseRowLength {
|
||||
extRow = baseRow[f.baseRowLength:]
|
||||
baseRow = baseRow[:f.baseRowLength]
|
||||
}
|
||||
baseRows[mapIndex&(f.baseRowGroupLength-1)] = baseRow
|
||||
rawdb.WriteFilterMapExtRow(batch, f.mapRowIndex(mapIndex, rowIndex), extRow, f.logMapWidth)
|
||||
}
|
||||
f.baseRowsCache.Add(baseMapRowIndex, baseRows)
|
||||
rawdb.WriteFilterMapBaseRows(batch, baseMapRowIndex, baseRows, f.logMapWidth)
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapRowIndex calculates the unified storage index where the given row of the
|
||||
// given map is stored. Note that this indexing scheme is the same as the one
|
||||
// proposed in EIP-7745 for tree-hashing the filter map structure and for the
|
||||
// same data proximity reasons it is also suitable for database representation.
|
||||
// See also:
|
||||
// https://eips.ethereum.org/EIPS/eip-7745#hash-tree-structure
|
||||
func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 {
|
||||
epochIndex, mapSubIndex := mapIndex>>f.logMapsPerEpoch, mapIndex&(f.mapsPerEpoch-1)
|
||||
return (uint64(epochIndex)<<f.logMapHeight+uint64(rowIndex))<<f.logMapsPerEpoch + uint64(mapSubIndex)
|
||||
}
|
||||
|
||||
// getBlockLvPointer returns the starting log value index where the log values
|
||||
// generated by the given block are located. If blockNumber is beyond the current
|
||||
// head then the first unoccupied log value index is returned.
|
||||
// Note that this function assumes that the indexer read lock is being held when
|
||||
// called from outside the indexerLoop goroutine.
|
||||
func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) {
|
||||
if blockNumber >= f.indexedRange.afterLastIndexedBlock && f.indexedRange.headBlockIndexed {
|
||||
return f.indexedRange.headBlockDelimiter, nil
|
||||
}
|
||||
if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok {
|
||||
return lvPointer, nil
|
||||
}
|
||||
lvPointer, err := rawdb.ReadBlockLvPointer(f.db, blockNumber)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to retrieve log value pointer of block %d: %v", blockNumber, err)
|
||||
}
|
||||
f.lvPointerCache.Add(blockNumber, lvPointer)
|
||||
return lvPointer, nil
|
||||
}
|
||||
|
||||
// storeBlockLvPointer stores the starting log value index where the log values
|
||||
// generated by the given block are located.
|
||||
func (f *FilterMaps) storeBlockLvPointer(batch ethdb.Batch, blockNumber, lvPointer uint64) {
|
||||
f.lvPointerCache.Add(blockNumber, lvPointer)
|
||||
rawdb.WriteBlockLvPointer(batch, blockNumber, lvPointer)
|
||||
}
|
||||
|
||||
// deleteBlockLvPointer deletes the starting log value index where the log values
|
||||
// generated by the given block are located.
|
||||
func (f *FilterMaps) deleteBlockLvPointer(batch ethdb.Batch, blockNumber uint64) {
|
||||
f.lvPointerCache.Remove(blockNumber)
|
||||
rawdb.DeleteBlockLvPointer(batch, blockNumber)
|
||||
}
|
||||
|
||||
// getLastBlockOfMap returns the number and id of the block that generated the
|
||||
// last log value entry of the given map.
|
||||
func (f *FilterMaps) getLastBlockOfMap(mapIndex uint32) (uint64, common.Hash, error) {
|
||||
if lastBlock, ok := f.lastBlockCache.Get(mapIndex); ok {
|
||||
return lastBlock.number, lastBlock.id, nil
|
||||
}
|
||||
number, id, err := rawdb.ReadFilterMapLastBlock(f.db, mapIndex)
|
||||
if err != nil {
|
||||
return 0, common.Hash{}, fmt.Errorf("failed to retrieve last block of map %d: %v", mapIndex, err)
|
||||
}
|
||||
f.lastBlockCache.Add(mapIndex, lastBlockOfMap{number: number, id: id})
|
||||
return number, id, nil
|
||||
}
|
||||
|
||||
// storeLastBlockOfMap stores the number of the block that generated the last
|
||||
// log value entry of the given map.
|
||||
func (f *FilterMaps) storeLastBlockOfMap(batch ethdb.Batch, mapIndex uint32, number uint64, id common.Hash) {
|
||||
f.lastBlockCache.Add(mapIndex, lastBlockOfMap{number: number, id: id})
|
||||
rawdb.WriteFilterMapLastBlock(batch, mapIndex, number, id)
|
||||
}
|
||||
|
||||
// deleteLastBlockOfMap deletes the number of the block that generated the last
|
||||
// log value entry of the given map.
|
||||
func (f *FilterMaps) deleteLastBlockOfMap(batch ethdb.Batch, mapIndex uint32) {
|
||||
f.lastBlockCache.Remove(mapIndex)
|
||||
rawdb.DeleteFilterMapLastBlock(batch, mapIndex)
|
||||
}
|
||||
|
||||
// deleteTailEpoch deletes index data from the earliest, either fully or partially
|
||||
// indexed epoch. The last block pointer for the last map of the epoch and the
|
||||
// corresponding block log value pointer are retained as these are always assumed
|
||||
// to be available for each epoch.
|
||||
func (f *FilterMaps) deleteTailEpoch(epoch uint32) error {
|
||||
f.indexLock.Lock()
|
||||
defer f.indexLock.Unlock()
|
||||
|
||||
firstMap := epoch << f.logMapsPerEpoch
|
||||
lastBlock, _, err := f.getLastBlockOfMap(firstMap + f.mapsPerEpoch - 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve last block of deleted epoch %d: %v", epoch, err)
|
||||
}
|
||||
var firstBlock uint64
|
||||
if epoch > 0 {
|
||||
firstBlock, _, err = f.getLastBlockOfMap(firstMap - 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve last block before deleted epoch %d: %v", epoch, err)
|
||||
}
|
||||
firstBlock++
|
||||
}
|
||||
fmr := f.indexedRange
|
||||
if f.indexedRange.firstRenderedMap == firstMap &&
|
||||
f.indexedRange.afterLastRenderedMap > firstMap+f.mapsPerEpoch &&
|
||||
f.indexedRange.tailPartialEpoch == 0 {
|
||||
fmr.firstRenderedMap = firstMap + f.mapsPerEpoch
|
||||
fmr.firstIndexedBlock = lastBlock + 1
|
||||
} else if f.indexedRange.firstRenderedMap == firstMap+f.mapsPerEpoch {
|
||||
fmr.tailPartialEpoch = 0
|
||||
} else {
|
||||
return errors.New("invalid tail epoch number")
|
||||
}
|
||||
f.setRange(f.db, f.indexedView, fmr)
|
||||
rawdb.DeleteFilterMapRows(f.db, f.mapRowIndex(firstMap, 0), f.mapRowIndex(firstMap+f.mapsPerEpoch, 0))
|
||||
for mapIndex := firstMap; mapIndex < firstMap+f.mapsPerEpoch; mapIndex++ {
|
||||
f.filterMapCache.Remove(mapIndex)
|
||||
}
|
||||
rawdb.DeleteFilterMapLastBlocks(f.db, firstMap, firstMap+f.mapsPerEpoch-1) // keep last enrty
|
||||
for mapIndex := firstMap; mapIndex < firstMap+f.mapsPerEpoch-1; mapIndex++ {
|
||||
f.lastBlockCache.Remove(mapIndex)
|
||||
}
|
||||
rawdb.DeleteBlockLvPointers(f.db, firstBlock, lastBlock) // keep last enrty
|
||||
for blockNumber := firstBlock; blockNumber < lastBlock; blockNumber++ {
|
||||
f.lvPointerCache.Remove(blockNumber)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// exportCheckpoints exports epoch checkpoints in the format used by checkpoints.go.
|
||||
func (f *FilterMaps) exportCheckpoints() {
|
||||
finalLvPtr, err := f.getBlockLvPointer(f.finalBlock + 1)
|
||||
if err != nil {
|
||||
log.Error("Error fetching log value pointer of finalized block", "block", f.finalBlock, "error", err)
|
||||
return
|
||||
}
|
||||
epochCount := uint32(finalLvPtr >> (f.logValuesPerMap + f.logMapsPerEpoch))
|
||||
if epochCount == f.lastFinalEpoch {
|
||||
return
|
||||
}
|
||||
w, err := os.Create(f.exportFileName)
|
||||
if err != nil {
|
||||
log.Error("Error creating checkpoint export file", "name", f.exportFileName, "error", err)
|
||||
return
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
log.Info("Exporting log index checkpoints", "epochs", epochCount, "file", f.exportFileName)
|
||||
w.WriteString("[\n")
|
||||
comma := ","
|
||||
for epoch := uint32(0); epoch < epochCount; epoch++ {
|
||||
lastBlock, lastBlockId, err := f.getLastBlockOfMap((epoch+1)<<f.logMapsPerEpoch - 1)
|
||||
if err != nil {
|
||||
log.Error("Error fetching last block of epoch", "epoch", epoch, "error", err)
|
||||
return
|
||||
}
|
||||
lvPtr, err := f.getBlockLvPointer(lastBlock)
|
||||
if err != nil {
|
||||
log.Error("Error fetching log value pointer of last block", "block", lastBlock, "error", err)
|
||||
return
|
||||
}
|
||||
if epoch == epochCount-1 {
|
||||
comma = ""
|
||||
}
|
||||
w.WriteString(fmt.Sprintf("{\"blockNumber\": %d, \"blockId\": \"0x%064x\", \"firstIndex\": %d}%s\n", lastBlock, lastBlockId, lvPtr, comma))
|
||||
}
|
||||
w.WriteString("]\n")
|
||||
f.lastFinalEpoch = epochCount
|
||||
}
|
||||
395
core/filtermaps/indexer.go
Normal file
395
core/filtermaps/indexer.go
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
const (
|
||||
logFrequency = time.Second * 20 // log info frequency during long indexing/unindexing process
|
||||
headLogDelay = time.Second // head indexing log info delay (do not log if finished faster)
|
||||
)
|
||||
|
||||
// updateLoop initializes and updates the log index structure according to the
|
||||
// current targetView.
|
||||
func (f *FilterMaps) indexerLoop() {
|
||||
defer f.closeWg.Done()
|
||||
|
||||
if f.disabled {
|
||||
f.reset()
|
||||
return
|
||||
}
|
||||
log.Info("Started log indexer")
|
||||
|
||||
for !f.stop {
|
||||
if !f.indexedRange.initialized {
|
||||
if err := f.init(); err != nil {
|
||||
log.Error("Error initializing log index", "error", err)
|
||||
f.waitForEvent()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if !f.targetHeadIndexed() {
|
||||
if !f.tryIndexHead() {
|
||||
f.waitForEvent()
|
||||
}
|
||||
} else {
|
||||
if f.finalBlock != f.lastFinal {
|
||||
if f.exportFileName != "" {
|
||||
f.exportCheckpoints()
|
||||
}
|
||||
f.lastFinal = f.finalBlock
|
||||
}
|
||||
if f.tryIndexTail() && f.tryUnindexTail() {
|
||||
f.waitForEvent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetTargetView sets a new target chain view for the indexer to render.
|
||||
// Note that SetTargetView never blocks.
|
||||
func (f *FilterMaps) SetTargetView(targetView *ChainView) {
|
||||
if targetView == nil {
|
||||
panic("nil targetView")
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-f.targetViewCh:
|
||||
case f.targetViewCh <- targetView:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetFinalBlock sets the finalized block number used for exporting checkpoints.
|
||||
// Note that SetFinalBlock never blocks.
|
||||
func (f *FilterMaps) SetFinalBlock(finalBlock uint64) {
|
||||
for {
|
||||
select {
|
||||
case <-f.finalBlockCh:
|
||||
case f.finalBlockCh <- finalBlock:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetBlockProcessing sets the block processing flag that temporarily suspends
|
||||
// log index rendering.
|
||||
// Note that SetBlockProcessing never blocks.
|
||||
func (f *FilterMaps) SetBlockProcessing(blockProcessing bool) {
|
||||
for {
|
||||
select {
|
||||
case <-f.blockProcessingCh:
|
||||
case f.blockProcessingCh <- blockProcessing:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WaitIdle blocks until the indexer is in an idle state while synced up to the
|
||||
// latest targetView.
|
||||
func (f *FilterMaps) WaitIdle() {
|
||||
if f.disabled {
|
||||
f.closeWg.Wait()
|
||||
return
|
||||
}
|
||||
for {
|
||||
ch := make(chan bool)
|
||||
f.waitIdleCh <- ch
|
||||
if <-ch {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// waitForEvent blocks until an event happens that the indexer might react to.
|
||||
func (f *FilterMaps) waitForEvent() {
|
||||
for !f.stop && (f.blockProcessing || f.targetHeadIndexed()) {
|
||||
f.processSingleEvent(true)
|
||||
}
|
||||
}
|
||||
|
||||
// processEvents processes all events, blocking only if a block processing is
|
||||
// happening and indexing should be suspended.
|
||||
func (f *FilterMaps) processEvents() {
|
||||
for !f.stop && f.processSingleEvent(f.blockProcessing) {
|
||||
}
|
||||
}
|
||||
|
||||
// processSingleEvent processes a single event either in a blocking or
|
||||
// non-blocking manner.
|
||||
func (f *FilterMaps) processSingleEvent(blocking bool) bool {
|
||||
if f.matcherSyncRequest != nil && f.targetHeadIndexed() {
|
||||
f.matcherSyncRequest.synced()
|
||||
f.matcherSyncRequest = nil
|
||||
}
|
||||
if blocking {
|
||||
select {
|
||||
case targetView := <-f.targetViewCh:
|
||||
f.setTargetView(targetView)
|
||||
case f.finalBlock = <-f.finalBlockCh:
|
||||
case f.matcherSyncRequest = <-f.matcherSyncCh:
|
||||
case f.blockProcessing = <-f.blockProcessingCh:
|
||||
case <-f.closeCh:
|
||||
f.stop = true
|
||||
case ch := <-f.waitIdleCh:
|
||||
select {
|
||||
case targetView := <-f.targetViewCh:
|
||||
f.setTargetView(targetView)
|
||||
default:
|
||||
}
|
||||
ch <- !f.blockProcessing && f.targetHeadIndexed()
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case targetView := <-f.targetViewCh:
|
||||
f.setTargetView(targetView)
|
||||
case f.finalBlock = <-f.finalBlockCh:
|
||||
case f.matcherSyncRequest = <-f.matcherSyncCh:
|
||||
case f.blockProcessing = <-f.blockProcessingCh:
|
||||
case <-f.closeCh:
|
||||
f.stop = true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// setTargetView updates the target chain view of the iterator.
|
||||
func (f *FilterMaps) setTargetView(targetView *ChainView) {
|
||||
f.targetView = targetView
|
||||
}
|
||||
|
||||
// tryIndexHead tries to render head maps according to the current targetView
|
||||
// and returns true if successful.
|
||||
func (f *FilterMaps) tryIndexHead() bool {
|
||||
headRenderer, err := f.renderMapsBefore(math.MaxUint32)
|
||||
if err != nil {
|
||||
log.Error("Error creating log index head renderer", "error", err)
|
||||
return false
|
||||
}
|
||||
if headRenderer == nil {
|
||||
return true
|
||||
}
|
||||
if !f.startedHeadIndex {
|
||||
f.lastLogHeadIndex = time.Now()
|
||||
f.startedHeadIndexAt = f.lastLogHeadIndex
|
||||
f.startedHeadIndex = true
|
||||
f.ptrHeadIndex = f.indexedRange.afterLastIndexedBlock
|
||||
}
|
||||
if _, err := headRenderer.run(func() bool {
|
||||
f.processEvents()
|
||||
return f.stop
|
||||
}, func() {
|
||||
f.tryUnindexTail()
|
||||
if f.indexedRange.hasIndexedBlocks() && f.indexedRange.afterLastIndexedBlock >= f.ptrHeadIndex &&
|
||||
((!f.loggedHeadIndex && time.Since(f.startedHeadIndexAt) > headLogDelay) ||
|
||||
time.Since(f.lastLogHeadIndex) > logFrequency) {
|
||||
log.Info("Log index head rendering in progress",
|
||||
"first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1,
|
||||
"processed", f.indexedRange.afterLastIndexedBlock-f.ptrHeadIndex,
|
||||
"remaining", f.indexedView.headNumber+1-f.indexedRange.afterLastIndexedBlock,
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
||||
f.loggedHeadIndex = true
|
||||
f.lastLogHeadIndex = time.Now()
|
||||
}
|
||||
}); err != nil {
|
||||
log.Error("Log index head rendering failed", "error", err)
|
||||
return false
|
||||
}
|
||||
if f.loggedHeadIndex {
|
||||
log.Info("Log index head rendering finished",
|
||||
"first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1,
|
||||
"processed", f.indexedRange.afterLastIndexedBlock-f.ptrHeadIndex,
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
||||
}
|
||||
f.loggedHeadIndex, f.startedHeadIndex = false, false
|
||||
return true
|
||||
}
|
||||
|
||||
// tryIndexTail tries to render tail epochs until the tail target block is
|
||||
// indexed and returns true if successful.
|
||||
// Note that tail indexing is only started if the log index head is fully
|
||||
// rendered according to targetView and is suspended as soon as the targetView
|
||||
// is changed.
|
||||
func (f *FilterMaps) tryIndexTail() bool {
|
||||
for firstEpoch := f.indexedRange.firstRenderedMap >> f.logMapsPerEpoch; firstEpoch > 0 && f.needTailEpoch(firstEpoch-1); {
|
||||
f.processEvents()
|
||||
if f.stop || !f.targetHeadIndexed() {
|
||||
return false
|
||||
}
|
||||
// resume process if tail rendering was interrupted because of head rendering
|
||||
tailRenderer := f.tailRenderer
|
||||
f.tailRenderer = nil
|
||||
if tailRenderer != nil && tailRenderer.afterLastMap != f.indexedRange.firstRenderedMap {
|
||||
tailRenderer = nil
|
||||
}
|
||||
if tailRenderer == nil {
|
||||
var err error
|
||||
tailRenderer, err = f.renderMapsBefore(f.indexedRange.firstRenderedMap)
|
||||
if err != nil {
|
||||
log.Error("Error creating log index tail renderer", "error", err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if tailRenderer == nil {
|
||||
return true
|
||||
}
|
||||
if !f.startedTailIndex {
|
||||
f.lastLogTailIndex = time.Now()
|
||||
f.startedTailIndexAt = f.lastLogTailIndex
|
||||
f.startedTailIndex = true
|
||||
f.ptrTailIndex = f.indexedRange.firstIndexedBlock - f.tailPartialBlocks()
|
||||
}
|
||||
done, err := tailRenderer.run(func() bool {
|
||||
f.processEvents()
|
||||
return f.stop || !f.targetHeadIndexed()
|
||||
}, func() {
|
||||
tpb, ttb := f.tailPartialBlocks(), f.tailTargetBlock()
|
||||
remaining := uint64(1)
|
||||
if f.indexedRange.firstIndexedBlock > ttb+tpb {
|
||||
remaining = f.indexedRange.firstIndexedBlock - ttb - tpb
|
||||
}
|
||||
if f.indexedRange.hasIndexedBlocks() && f.ptrTailIndex >= f.indexedRange.firstIndexedBlock &&
|
||||
(!f.loggedTailIndex || time.Since(f.lastLogTailIndex) > logFrequency) {
|
||||
log.Info("Log index tail rendering in progress",
|
||||
"first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1,
|
||||
"processed", f.ptrTailIndex-f.indexedRange.firstIndexedBlock+tpb,
|
||||
"remaining", remaining,
|
||||
"next tail epoch percentage", f.indexedRange.tailPartialEpoch*100/f.mapsPerEpoch,
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt)))
|
||||
f.loggedTailIndex = true
|
||||
f.lastLogTailIndex = time.Now()
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Log index tail rendering failed", "error", err)
|
||||
}
|
||||
if !done {
|
||||
f.tailRenderer = tailRenderer // only keep tail renderer if interrupted by stopCb
|
||||
return false
|
||||
}
|
||||
}
|
||||
if f.loggedTailIndex {
|
||||
log.Info("Log index tail rendering finished",
|
||||
"first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1,
|
||||
"processed", f.ptrTailIndex-f.indexedRange.firstIndexedBlock,
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt)))
|
||||
f.loggedTailIndex = false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// tryUnindexTail removes entire epochs of log index data as long as the first
|
||||
// fully indexed block is at least as old as the tail target.
|
||||
// Note that unindexing is very quick as it only removes continuous ranges of
|
||||
// data from the database and is also called while running head indexing.
|
||||
func (f *FilterMaps) tryUnindexTail() bool {
|
||||
for {
|
||||
firstEpoch := (f.indexedRange.firstRenderedMap - f.indexedRange.tailPartialEpoch) >> f.logMapsPerEpoch
|
||||
if f.needTailEpoch(firstEpoch) {
|
||||
break
|
||||
}
|
||||
f.processEvents()
|
||||
if f.stop {
|
||||
return false
|
||||
}
|
||||
if !f.startedTailUnindex {
|
||||
f.startedTailUnindexAt = time.Now()
|
||||
f.startedTailUnindex = true
|
||||
f.ptrTailUnindexMap = f.indexedRange.firstRenderedMap - f.indexedRange.tailPartialEpoch
|
||||
f.ptrTailUnindexBlock = f.indexedRange.firstIndexedBlock - f.tailPartialBlocks()
|
||||
}
|
||||
if err := f.deleteTailEpoch(firstEpoch); err != nil {
|
||||
log.Error("Log index tail epoch unindexing failed", "error", err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if f.startedTailUnindex {
|
||||
log.Info("Log index tail unindexing finished",
|
||||
"first block", f.indexedRange.firstIndexedBlock, "last block", f.indexedRange.afterLastIndexedBlock-1,
|
||||
"removed maps", f.indexedRange.firstRenderedMap-f.ptrTailUnindexMap,
|
||||
"removed blocks", f.indexedRange.firstIndexedBlock-f.tailPartialBlocks()-f.ptrTailUnindexBlock,
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedTailUnindexAt)))
|
||||
f.startedTailUnindex = false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// needTailEpoch returns true if the given tail epoch needs to be kept
|
||||
// according to the current tail target, false if it can be removed.
|
||||
func (f *FilterMaps) needTailEpoch(epoch uint32) bool {
|
||||
firstEpoch := f.indexedRange.firstRenderedMap >> f.logMapsPerEpoch
|
||||
if epoch > firstEpoch {
|
||||
return true
|
||||
}
|
||||
if epoch+1 < firstEpoch {
|
||||
return false
|
||||
}
|
||||
tailTarget := f.tailTargetBlock()
|
||||
if tailTarget < f.indexedRange.firstIndexedBlock {
|
||||
return true
|
||||
}
|
||||
tailLvIndex, err := f.getBlockLvPointer(tailTarget)
|
||||
if err != nil {
|
||||
log.Error("Could not get log value index of tail block", "error", err)
|
||||
return true
|
||||
}
|
||||
return uint64(epoch+1)<<(f.logValuesPerMap+f.logMapsPerEpoch) >= tailLvIndex
|
||||
}
|
||||
|
||||
// tailTargetBlock returns the target value for the tail block number according
|
||||
// to the log history parameter and the current index head.
|
||||
func (f *FilterMaps) tailTargetBlock() uint64 {
|
||||
if f.history == 0 || f.indexedView.headNumber < f.history {
|
||||
return 0
|
||||
}
|
||||
return f.indexedView.headNumber + 1 - f.history
|
||||
}
|
||||
|
||||
// tailPartialBlocks returns the number of rendered blocks in the partially
|
||||
// rendered next tail epoch.
|
||||
func (f *FilterMaps) tailPartialBlocks() uint64 {
|
||||
if f.indexedRange.tailPartialEpoch == 0 {
|
||||
return 0
|
||||
}
|
||||
end, _, err := f.getLastBlockOfMap(f.indexedRange.firstRenderedMap - f.mapsPerEpoch + f.indexedRange.tailPartialEpoch - 1)
|
||||
if err != nil {
|
||||
log.Error("Error fetching last block of map", "mapIndex", f.indexedRange.firstRenderedMap-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch-1, "error", err)
|
||||
}
|
||||
var start uint64
|
||||
if f.indexedRange.firstRenderedMap-f.mapsPerEpoch > 0 {
|
||||
start, _, err = f.getLastBlockOfMap(f.indexedRange.firstRenderedMap - f.mapsPerEpoch - 1)
|
||||
if err != nil {
|
||||
log.Error("Error fetching last block of map", "mapIndex", f.indexedRange.firstRenderedMap-f.mapsPerEpoch-1, "error", err)
|
||||
}
|
||||
}
|
||||
return end - start
|
||||
}
|
||||
|
||||
// targetHeadIndexed returns true if the current log index is consistent with
|
||||
// targetView with its head block fully rendered.
|
||||
func (f *FilterMaps) targetHeadIndexed() bool {
|
||||
return equalViews(f.targetView, f.indexedView) && f.indexedRange.headBlockIndexed
|
||||
}
|
||||
444
core/filtermaps/indexer_test.go
Normal file
444
core/filtermaps/indexer_test.go
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var testParams = Params{
|
||||
logMapHeight: 2,
|
||||
logMapWidth: 24,
|
||||
logMapsPerEpoch: 4,
|
||||
logValuesPerMap: 4,
|
||||
baseRowGroupLength: 4,
|
||||
baseRowLengthRatio: 2,
|
||||
logLayerDiff: 2,
|
||||
}
|
||||
|
||||
func TestIndexerRandomRange(t *testing.T) {
|
||||
ts := newTestSetup(t)
|
||||
defer ts.close()
|
||||
|
||||
forks := make([][]common.Hash, 10)
|
||||
ts.chain.addBlocks(1000, 5, 2, 4, false) // 51 log values per block
|
||||
for i := range forks {
|
||||
if i != 0 {
|
||||
forkBlock := rand.Intn(1000)
|
||||
ts.chain.setHead(forkBlock)
|
||||
ts.chain.addBlocks(1000-forkBlock, 5, 2, 4, false) // 51 log values per block
|
||||
}
|
||||
forks[i] = ts.chain.getCanonicalChain()
|
||||
}
|
||||
lvPerBlock := uint64(51)
|
||||
ts.setHistory(0, false)
|
||||
var (
|
||||
history int
|
||||
noHistory bool
|
||||
fork, head = len(forks) - 1, 1000
|
||||
checkSnapshot bool
|
||||
)
|
||||
ts.fm.WaitIdle()
|
||||
for i := 0; i < 200; i++ {
|
||||
switch rand.Intn(3) {
|
||||
case 0:
|
||||
// change history settings
|
||||
switch rand.Intn(10) {
|
||||
case 0:
|
||||
history, noHistory = 0, false
|
||||
case 1:
|
||||
history, noHistory = 0, true
|
||||
default:
|
||||
history, noHistory = rand.Intn(1000)+1, false
|
||||
}
|
||||
ts.testDisableSnapshots = rand.Intn(2) == 0
|
||||
ts.setHistory(uint64(history), noHistory)
|
||||
case 1:
|
||||
// change head to random position of random fork
|
||||
fork, head = rand.Intn(len(forks)), rand.Intn(1001)
|
||||
ts.chain.setCanonicalChain(forks[fork][:head+1])
|
||||
case 2:
|
||||
if head < 1000 {
|
||||
checkSnapshot = !noHistory && head != 0 // no snapshot generated for block 0
|
||||
// add blocks after the current head
|
||||
head += rand.Intn(1000-head) + 1
|
||||
ts.fm.testSnapshotUsed = false
|
||||
ts.chain.setCanonicalChain(forks[fork][:head+1])
|
||||
}
|
||||
}
|
||||
ts.fm.WaitIdle()
|
||||
if checkSnapshot {
|
||||
if ts.fm.testSnapshotUsed == ts.fm.testDisableSnapshots {
|
||||
ts.t.Fatalf("Invalid snapshot used state after head extension (used: %v, disabled: %v)", ts.fm.testSnapshotUsed, ts.fm.testDisableSnapshots)
|
||||
}
|
||||
checkSnapshot = false
|
||||
}
|
||||
if noHistory {
|
||||
if ts.fm.indexedRange.initialized {
|
||||
t.Fatalf("filterMapsRange initialized while indexing is disabled")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !ts.fm.indexedRange.initialized {
|
||||
t.Fatalf("filterMapsRange not initialized while indexing is enabled")
|
||||
}
|
||||
var tailBlock uint64
|
||||
if history > 0 && history <= head {
|
||||
tailBlock = uint64(head + 1 - history)
|
||||
}
|
||||
var tailEpoch uint32
|
||||
if tailBlock > 0 {
|
||||
tailLvPtr := (tailBlock - 1) * lvPerBlock // no logs in genesis block, only delimiter
|
||||
tailEpoch = uint32(tailLvPtr >> (testParams.logValuesPerMap + testParams.logMapsPerEpoch))
|
||||
}
|
||||
var expTailBlock uint64
|
||||
if tailEpoch > 0 {
|
||||
tailLvPtr := uint64(tailEpoch) << (testParams.logValuesPerMap + testParams.logMapsPerEpoch) // first available lv ptr
|
||||
// (expTailBlock-1)*lvPerBlock >= tailLvPtr
|
||||
expTailBlock = (tailLvPtr + lvPerBlock*2 - 1) / lvPerBlock
|
||||
}
|
||||
if ts.fm.indexedRange.afterLastIndexedBlock != uint64(head+1) {
|
||||
ts.t.Fatalf("Invalid index head (expected #%d, got #%d)", head, ts.fm.indexedRange.afterLastIndexedBlock-1)
|
||||
}
|
||||
if ts.fm.indexedRange.headBlockDelimiter != uint64(head)*lvPerBlock {
|
||||
ts.t.Fatalf("Invalid index head delimiter pointer (expected %d, got %d)", uint64(head)*lvPerBlock, ts.fm.indexedRange.headBlockDelimiter)
|
||||
}
|
||||
if ts.fm.indexedRange.firstIndexedBlock != expTailBlock {
|
||||
ts.t.Fatalf("Invalid index tail block (expected #%d, got #%d)", expTailBlock, ts.fm.indexedRange.firstIndexedBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexerCompareDb(t *testing.T) {
|
||||
ts := newTestSetup(t)
|
||||
defer ts.close()
|
||||
|
||||
ts.chain.addBlocks(500, 10, 3, 4, true)
|
||||
ts.setHistory(0, false)
|
||||
ts.fm.WaitIdle()
|
||||
// revert points are stored after block 500
|
||||
ts.chain.addBlocks(500, 10, 3, 4, true)
|
||||
ts.fm.WaitIdle()
|
||||
chain1 := ts.chain.getCanonicalChain()
|
||||
ts.storeDbHash("chain 1 [0, 1000]")
|
||||
|
||||
ts.chain.setHead(600)
|
||||
ts.fm.WaitIdle()
|
||||
ts.storeDbHash("chain 1/2 [0, 600]")
|
||||
|
||||
ts.chain.addBlocks(600, 10, 3, 4, true)
|
||||
ts.fm.WaitIdle()
|
||||
chain2 := ts.chain.getCanonicalChain()
|
||||
ts.storeDbHash("chain 2 [0, 1200]")
|
||||
|
||||
ts.chain.setHead(600)
|
||||
ts.fm.WaitIdle()
|
||||
ts.checkDbHash("chain 1/2 [0, 600]")
|
||||
|
||||
ts.setHistory(800, false)
|
||||
ts.chain.setCanonicalChain(chain1)
|
||||
ts.fm.WaitIdle()
|
||||
ts.storeDbHash("chain 1 [201, 1000]")
|
||||
|
||||
ts.setHistory(0, false)
|
||||
ts.fm.WaitIdle()
|
||||
ts.checkDbHash("chain 1 [0, 1000]")
|
||||
|
||||
ts.setHistory(800, false)
|
||||
ts.chain.setCanonicalChain(chain2)
|
||||
ts.fm.WaitIdle()
|
||||
ts.storeDbHash("chain 2 [401, 1200]")
|
||||
|
||||
ts.setHistory(0, true)
|
||||
ts.fm.WaitIdle()
|
||||
ts.storeDbHash("no index")
|
||||
|
||||
ts.chain.setCanonicalChain(chain2[:501])
|
||||
ts.setHistory(0, false)
|
||||
ts.fm.WaitIdle()
|
||||
ts.chain.setCanonicalChain(chain2)
|
||||
ts.fm.WaitIdle()
|
||||
ts.checkDbHash("chain 2 [0, 1200]")
|
||||
|
||||
ts.chain.setCanonicalChain(chain1)
|
||||
ts.fm.WaitIdle()
|
||||
ts.setHistory(800, false)
|
||||
ts.fm.WaitIdle()
|
||||
ts.checkDbHash("chain 1 [201, 1000]")
|
||||
|
||||
ts.chain.setCanonicalChain(chain2)
|
||||
ts.fm.WaitIdle()
|
||||
ts.checkDbHash("chain 2 [401, 1200]")
|
||||
|
||||
ts.setHistory(0, true)
|
||||
ts.fm.WaitIdle()
|
||||
ts.checkDbHash("no index")
|
||||
}
|
||||
|
||||
type testSetup struct {
|
||||
t *testing.T
|
||||
fm *FilterMaps
|
||||
db ethdb.Database
|
||||
chain *testChain
|
||||
params Params
|
||||
dbHashes map[string]common.Hash
|
||||
testDisableSnapshots bool
|
||||
}
|
||||
|
||||
func newTestSetup(t *testing.T) *testSetup {
|
||||
params := testParams
|
||||
params.deriveFields()
|
||||
ts := &testSetup{
|
||||
t: t,
|
||||
db: rawdb.NewMemoryDatabase(),
|
||||
params: params,
|
||||
dbHashes: make(map[string]common.Hash),
|
||||
}
|
||||
ts.chain = ts.newTestChain()
|
||||
return ts
|
||||
}
|
||||
|
||||
func (ts *testSetup) setHistory(history uint64, noHistory bool) {
|
||||
if ts.fm != nil {
|
||||
ts.fm.Stop()
|
||||
}
|
||||
head := ts.chain.CurrentBlock()
|
||||
view := NewChainView(ts.chain, head.Number.Uint64(), head.Hash())
|
||||
config := Config{
|
||||
History: history,
|
||||
Disabled: noHistory,
|
||||
}
|
||||
ts.fm = NewFilterMaps(ts.db, view, ts.params, config)
|
||||
ts.fm.testDisableSnapshots = ts.testDisableSnapshots
|
||||
ts.fm.Start()
|
||||
}
|
||||
|
||||
func (ts *testSetup) storeDbHash(id string) {
|
||||
dbHash := ts.fmDbHash()
|
||||
for otherId, otherHash := range ts.dbHashes {
|
||||
if otherHash == dbHash {
|
||||
ts.t.Fatalf("Unexpected equal database hashes `%s` and `%s`", id, otherId)
|
||||
}
|
||||
}
|
||||
ts.dbHashes[id] = dbHash
|
||||
}
|
||||
|
||||
func (ts *testSetup) checkDbHash(id string) {
|
||||
if ts.fmDbHash() != ts.dbHashes[id] {
|
||||
ts.t.Fatalf("Database `%s` hash mismatch", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *testSetup) fmDbHash() common.Hash {
|
||||
hasher := sha256.New()
|
||||
it := ts.db.NewIterator(nil, nil)
|
||||
for it.Next() {
|
||||
hasher.Write(it.Key())
|
||||
hasher.Write(it.Value())
|
||||
}
|
||||
it.Release()
|
||||
var result common.Hash
|
||||
hasher.Sum(result[:0])
|
||||
return result
|
||||
}
|
||||
|
||||
func (ts *testSetup) close() {
|
||||
if ts.fm != nil {
|
||||
ts.fm.Stop()
|
||||
}
|
||||
ts.db.Close()
|
||||
ts.chain.db.Close()
|
||||
}
|
||||
|
||||
type testChain struct {
|
||||
ts *testSetup
|
||||
db ethdb.Database
|
||||
lock sync.RWMutex
|
||||
canonical []common.Hash
|
||||
blocks map[common.Hash]*types.Block
|
||||
receipts map[common.Hash]types.Receipts
|
||||
}
|
||||
|
||||
func (ts *testSetup) newTestChain() *testChain {
|
||||
return &testChain{
|
||||
ts: ts,
|
||||
blocks: make(map[common.Hash]*types.Block),
|
||||
receipts: make(map[common.Hash]types.Receipts),
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *testChain) CurrentBlock() *types.Header {
|
||||
tc.lock.RLock()
|
||||
defer tc.lock.RUnlock()
|
||||
|
||||
if len(tc.canonical) == 0 {
|
||||
return nil
|
||||
}
|
||||
return tc.blocks[tc.canonical[len(tc.canonical)-1]].Header()
|
||||
}
|
||||
|
||||
func (tc *testChain) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||
tc.lock.RLock()
|
||||
defer tc.lock.RUnlock()
|
||||
|
||||
if block := tc.blocks[hash]; block != nil {
|
||||
return block.Header()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tc *testChain) GetCanonicalHash(number uint64) common.Hash {
|
||||
tc.lock.RLock()
|
||||
defer tc.lock.RUnlock()
|
||||
|
||||
if uint64(len(tc.canonical)) <= number {
|
||||
return common.Hash{}
|
||||
}
|
||||
return tc.canonical[number]
|
||||
}
|
||||
|
||||
func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
tc.lock.RLock()
|
||||
defer tc.lock.RUnlock()
|
||||
|
||||
return tc.receipts[hash]
|
||||
}
|
||||
|
||||
func (tc *testChain) addBlocks(count, maxTxPerBlock, maxLogsPerReceipt, maxTopicsPerLog int, random bool) {
|
||||
tc.lock.Lock()
|
||||
blockGen := func(i int, gen *core.BlockGen) {
|
||||
var txCount int
|
||||
if random {
|
||||
txCount = rand.Intn(maxTxPerBlock + 1)
|
||||
} else {
|
||||
txCount = maxTxPerBlock
|
||||
}
|
||||
for k := txCount; k > 0; k-- {
|
||||
receipt := types.NewReceipt(nil, false, 0)
|
||||
var logCount int
|
||||
if random {
|
||||
logCount = rand.Intn(maxLogsPerReceipt + 1)
|
||||
} else {
|
||||
logCount = maxLogsPerReceipt
|
||||
}
|
||||
receipt.Logs = make([]*types.Log, logCount)
|
||||
for i := range receipt.Logs {
|
||||
log := &types.Log{}
|
||||
receipt.Logs[i] = log
|
||||
crand.Read(log.Address[:])
|
||||
var topicCount int
|
||||
if random {
|
||||
topicCount = rand.Intn(maxTopicsPerLog + 1)
|
||||
} else {
|
||||
topicCount = maxTopicsPerLog
|
||||
}
|
||||
log.Topics = make([]common.Hash, topicCount)
|
||||
for j := range log.Topics {
|
||||
crand.Read(log.Topics[j][:])
|
||||
}
|
||||
}
|
||||
gen.AddUncheckedReceipt(receipt)
|
||||
gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
blocks []*types.Block
|
||||
receipts []types.Receipts
|
||||
engine = ethash.NewFaker()
|
||||
)
|
||||
|
||||
if len(tc.canonical) == 0 {
|
||||
gspec := &core.Genesis{
|
||||
Alloc: types.GenesisAlloc{},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Config: params.TestChainConfig,
|
||||
}
|
||||
tc.db, blocks, receipts = core.GenerateChainWithGenesis(gspec, engine, count, blockGen)
|
||||
gblock := gspec.ToBlock()
|
||||
ghash := gblock.Hash()
|
||||
tc.canonical = []common.Hash{ghash}
|
||||
tc.blocks[ghash] = gblock
|
||||
tc.receipts[ghash] = types.Receipts{}
|
||||
} else {
|
||||
blocks, receipts = core.GenerateChain(params.TestChainConfig, tc.blocks[tc.canonical[len(tc.canonical)-1]], engine, tc.db, count, blockGen)
|
||||
}
|
||||
|
||||
for i, block := range blocks {
|
||||
num, hash := int(block.NumberU64()), block.Hash()
|
||||
if len(tc.canonical) != num {
|
||||
panic("canonical chain length mismatch")
|
||||
}
|
||||
tc.canonical = append(tc.canonical, hash)
|
||||
tc.blocks[hash] = block
|
||||
if receipts[i] != nil {
|
||||
tc.receipts[hash] = receipts[i]
|
||||
} else {
|
||||
tc.receipts[hash] = types.Receipts{}
|
||||
}
|
||||
}
|
||||
tc.lock.Unlock()
|
||||
tc.setTargetHead()
|
||||
}
|
||||
|
||||
func (tc *testChain) setHead(headNum int) {
|
||||
tc.lock.Lock()
|
||||
tc.canonical = tc.canonical[:headNum+1]
|
||||
tc.lock.Unlock()
|
||||
tc.setTargetHead()
|
||||
}
|
||||
|
||||
func (tc *testChain) setTargetHead() {
|
||||
head := tc.CurrentBlock()
|
||||
if tc.ts.fm != nil {
|
||||
if !tc.ts.fm.disabled {
|
||||
//tc.ts.fm.targetViewCh <- NewChainView(tc, head.Number.Uint64(), head.Hash())
|
||||
tc.ts.fm.SetTargetView(NewChainView(tc, head.Number.Uint64(), head.Hash()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *testChain) getCanonicalChain() []common.Hash {
|
||||
tc.lock.RLock()
|
||||
defer tc.lock.RUnlock()
|
||||
|
||||
cc := make([]common.Hash, len(tc.canonical))
|
||||
copy(cc, tc.canonical)
|
||||
return cc
|
||||
}
|
||||
|
||||
// restore an earlier state of the chain
|
||||
func (tc *testChain) setCanonicalChain(cc []common.Hash) {
|
||||
tc.lock.Lock()
|
||||
tc.canonical = make([]common.Hash, len(cc))
|
||||
copy(tc.canonical, cc)
|
||||
tc.lock.Unlock()
|
||||
tc.setTargetHead()
|
||||
}
|
||||
768
core/filtermaps/map_renderer.go
Normal file
768
core/filtermaps/map_renderer.go
Normal file
|
|
@ -0,0 +1,768 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
const (
|
||||
maxMapsPerBatch = 32 // maximum number of maps rendered in memory
|
||||
valuesPerCallback = 1024 // log values processed per event process callback
|
||||
cachedRowMappings = 10000 // log value to row mappings cached during rendering
|
||||
|
||||
// Number of rows written to db in a single batch.
|
||||
// The map renderer splits up writes like this to ensure that regular
|
||||
// block processing latency is not affected by large batch writes.
|
||||
rowsPerBatch = 1024
|
||||
)
|
||||
|
||||
var (
|
||||
errChainUpdate = errors.New("rendered section of chain updated")
|
||||
)
|
||||
|
||||
// mapRenderer represents a process that renders filter maps in a specified
|
||||
// range according to the actual targetView.
|
||||
type mapRenderer struct {
|
||||
f *FilterMaps
|
||||
afterLastMap uint32
|
||||
currentMap *renderedMap
|
||||
finishedMaps map[uint32]*renderedMap
|
||||
firstFinished, afterLastFinished uint32
|
||||
iterator *logIterator
|
||||
}
|
||||
|
||||
// renderedMap represents a single filter map that is being rendered in memory.
|
||||
type renderedMap struct {
|
||||
filterMap filterMap
|
||||
mapIndex uint32
|
||||
lastBlock uint64
|
||||
lastBlockId common.Hash
|
||||
blockLvPtrs []uint64 // start pointers of blocks starting in this map; last one is lastBlock
|
||||
finished bool // iterator finished; all values rendered
|
||||
headDelimiter uint64 // if finished then points to the future block delimiter of the head block
|
||||
}
|
||||
|
||||
// firstBlock returns the first block number that starts in the given map.
|
||||
func (r *renderedMap) firstBlock() uint64 {
|
||||
return r.lastBlock + 1 - uint64(len(r.blockLvPtrs))
|
||||
}
|
||||
|
||||
// renderMapsBefore creates a mapRenderer that renders the log index until the
|
||||
// specified map index boundary, starting from the latest available starting
|
||||
// point that is consistent with the current targetView.
|
||||
// The renderer ensures that filterMapsRange, indexedView and the actual map
|
||||
// data are always consistent with each other. If afterLastMap is greater than
|
||||
// the latest existing rendered map then indexedView is updated to targetView,
|
||||
// otherwise it is checked that the rendered range is consistent with both
|
||||
// views.
|
||||
func (f *FilterMaps) renderMapsBefore(afterLastMap uint32) (*mapRenderer, error) {
|
||||
nextMap, startBlock, startLvPtr, err := f.lastCanonicalMapBoundaryBefore(afterLastMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if snapshot := f.lastCanonicalSnapshotBefore(afterLastMap); snapshot != nil && snapshot.mapIndex >= nextMap {
|
||||
return f.renderMapsFromSnapshot(snapshot)
|
||||
}
|
||||
if nextMap >= afterLastMap {
|
||||
return nil, nil
|
||||
}
|
||||
return f.renderMapsFromMapBoundary(nextMap, afterLastMap, startBlock, startLvPtr)
|
||||
}
|
||||
|
||||
// renderMapsFromSnapshot creates a mapRenderer that starts rendering from a
|
||||
// snapshot made at a block boundary.
|
||||
func (f *FilterMaps) renderMapsFromSnapshot(cp *renderedMap) (*mapRenderer, error) {
|
||||
f.testSnapshotUsed = true
|
||||
iter, err := f.newLogIteratorFromBlockDelimiter(cp.lastBlock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create log iterator from block delimiter %d: %v", cp.lastBlock, err)
|
||||
}
|
||||
return &mapRenderer{
|
||||
f: f,
|
||||
currentMap: &renderedMap{
|
||||
filterMap: cp.filterMap.copy(),
|
||||
mapIndex: cp.mapIndex,
|
||||
lastBlock: cp.lastBlock,
|
||||
blockLvPtrs: cp.blockLvPtrs,
|
||||
},
|
||||
finishedMaps: make(map[uint32]*renderedMap),
|
||||
firstFinished: cp.mapIndex,
|
||||
afterLastFinished: cp.mapIndex,
|
||||
afterLastMap: math.MaxUint32,
|
||||
iterator: iter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// renderMapsFromMapBoundary creates a mapRenderer that starts rendering at a
|
||||
// map boundary.
|
||||
func (f *FilterMaps) renderMapsFromMapBoundary(firstMap, afterLastMap uint32, startBlock, startLvPtr uint64) (*mapRenderer, error) {
|
||||
iter, err := f.newLogIteratorFromMapBoundary(firstMap, startBlock, startLvPtr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create log iterator from map boundary %d: %v", firstMap, err)
|
||||
}
|
||||
return &mapRenderer{
|
||||
f: f,
|
||||
currentMap: &renderedMap{
|
||||
filterMap: f.emptyFilterMap(),
|
||||
mapIndex: firstMap,
|
||||
lastBlock: iter.blockNumber,
|
||||
},
|
||||
finishedMaps: make(map[uint32]*renderedMap),
|
||||
firstFinished: firstMap,
|
||||
afterLastFinished: firstMap,
|
||||
afterLastMap: afterLastMap,
|
||||
iterator: iter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// lastCanonicalSnapshotBefore returns the latest cached snapshot that matches
|
||||
// the current targetView.
|
||||
func (f *FilterMaps) lastCanonicalSnapshotBefore(afterLastMap uint32) *renderedMap {
|
||||
var best *renderedMap
|
||||
for _, blockNumber := range f.renderSnapshots.Keys() {
|
||||
if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.afterLastIndexedBlock &&
|
||||
blockNumber <= f.targetView.headNumber && f.targetView.getBlockId(blockNumber) == cp.lastBlockId &&
|
||||
cp.mapIndex < afterLastMap && (best == nil || blockNumber > best.lastBlock) {
|
||||
best = cp
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
// lastCanonicalMapBoundaryBefore returns the latest map boundary before the
|
||||
// specified map index that matches the current targetView. This can either
|
||||
// be a checkpoint (hardcoded or left from a previously unindexed tail epoch)
|
||||
// or the boundary of a currently rendered map.
|
||||
// Along with the next map index where the rendering can be started, the number
|
||||
// and starting log value pointer of the last block is also returned.
|
||||
func (f *FilterMaps) lastCanonicalMapBoundaryBefore(afterLastMap uint32) (nextMap uint32, startBlock, startLvPtr uint64, err error) {
|
||||
if !f.indexedRange.initialized {
|
||||
return 0, 0, 0, nil
|
||||
}
|
||||
mapIndex := afterLastMap
|
||||
for {
|
||||
var ok bool
|
||||
if mapIndex, ok = f.lastMapBoundaryBefore(mapIndex); !ok {
|
||||
return 0, 0, 0, nil
|
||||
}
|
||||
lastBlock, lastBlockId, err := f.getLastBlockOfMap(mapIndex)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err)
|
||||
}
|
||||
if lastBlock >= f.indexedView.headNumber || lastBlock >= f.targetView.headNumber ||
|
||||
lastBlockId != f.targetView.getBlockId(lastBlock) {
|
||||
// map is not full or inconsistent with targetView; roll back
|
||||
continue
|
||||
}
|
||||
lvPtr, err := f.getBlockLvPointer(lastBlock)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("failed to retrieve log value pointer of last canonical boundary block %d: %v", lastBlock, err)
|
||||
}
|
||||
return mapIndex + 1, lastBlock, lvPtr, nil
|
||||
}
|
||||
}
|
||||
|
||||
// lastMapBoundaryBefore returns the latest map boundary before the specified
|
||||
// map index.
|
||||
func (f *FilterMaps) lastMapBoundaryBefore(mapIndex uint32) (uint32, bool) {
|
||||
if !f.indexedRange.initialized || f.indexedRange.afterLastRenderedMap == 0 {
|
||||
return 0, false
|
||||
}
|
||||
if mapIndex > f.indexedRange.afterLastRenderedMap {
|
||||
mapIndex = f.indexedRange.afterLastRenderedMap
|
||||
}
|
||||
if mapIndex > f.indexedRange.firstRenderedMap {
|
||||
return mapIndex - 1, true
|
||||
}
|
||||
if mapIndex+f.mapsPerEpoch > f.indexedRange.firstRenderedMap {
|
||||
if mapIndex > f.indexedRange.firstRenderedMap-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch {
|
||||
mapIndex = f.indexedRange.firstRenderedMap - f.mapsPerEpoch + f.indexedRange.tailPartialEpoch
|
||||
}
|
||||
} else {
|
||||
mapIndex = (mapIndex >> f.logMapsPerEpoch) << f.logMapsPerEpoch
|
||||
}
|
||||
if mapIndex == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return mapIndex - 1, true
|
||||
}
|
||||
|
||||
// emptyFilterMap returns an empty filter map.
|
||||
func (f *FilterMaps) emptyFilterMap() filterMap {
|
||||
return make(filterMap, f.mapHeight)
|
||||
}
|
||||
|
||||
// loadHeadSnapshot loads the last rendered map from the database and creates
|
||||
// a snapshot.
|
||||
func (f *FilterMaps) loadHeadSnapshot() error {
|
||||
fm, err := f.getFilterMap(f.indexedRange.afterLastRenderedMap - 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load head snapshot map %d: %v", f.indexedRange.afterLastRenderedMap-1, err)
|
||||
}
|
||||
lastBlock, _, err := f.getLastBlockOfMap(f.indexedRange.afterLastRenderedMap - 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve last block of head snapshot map %d: %v", f.indexedRange.afterLastRenderedMap-1, err)
|
||||
}
|
||||
var firstBlock uint64
|
||||
if f.indexedRange.afterLastRenderedMap > 1 {
|
||||
prevLastBlock, _, err := f.getLastBlockOfMap(f.indexedRange.afterLastRenderedMap - 2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve last block of map %d before head snapshot: %v", f.indexedRange.afterLastRenderedMap-2, err)
|
||||
}
|
||||
firstBlock = prevLastBlock + 1
|
||||
}
|
||||
lvPtrs := make([]uint64, lastBlock+1-firstBlock)
|
||||
for i := range lvPtrs {
|
||||
lvPtrs[i], err = f.getBlockLvPointer(firstBlock + uint64(i))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve log value pointer of head snapshot block %d: %v", firstBlock+uint64(i), err)
|
||||
}
|
||||
}
|
||||
f.renderSnapshots.Add(f.indexedRange.afterLastIndexedBlock-1, &renderedMap{
|
||||
filterMap: fm,
|
||||
mapIndex: f.indexedRange.afterLastRenderedMap - 1,
|
||||
lastBlock: f.indexedRange.afterLastIndexedBlock - 1,
|
||||
lastBlockId: f.indexedView.getBlockId(f.indexedRange.afterLastIndexedBlock - 1),
|
||||
blockLvPtrs: lvPtrs,
|
||||
finished: true,
|
||||
headDelimiter: f.indexedRange.headBlockDelimiter,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// makeSnapshot creates a snapshot of the current state of the rendered map.
|
||||
func (r *mapRenderer) makeSnapshot() {
|
||||
r.f.renderSnapshots.Add(r.iterator.blockNumber, &renderedMap{
|
||||
filterMap: r.currentMap.filterMap.copy(),
|
||||
mapIndex: r.currentMap.mapIndex,
|
||||
lastBlock: r.iterator.blockNumber,
|
||||
lastBlockId: r.f.targetView.getBlockId(r.currentMap.lastBlock),
|
||||
blockLvPtrs: r.currentMap.blockLvPtrs,
|
||||
finished: true,
|
||||
headDelimiter: r.iterator.lvIndex,
|
||||
})
|
||||
}
|
||||
|
||||
// run does the actual map rendering. It periodically calls the stopCb callback
|
||||
// and if it returns true the process is interrupted an can be resumed later
|
||||
// by calling run again. The writeCb callback is called after new maps have
|
||||
// been written to disk and the index range has been updated accordingly.
|
||||
func (r *mapRenderer) run(stopCb func() bool, writeCb func()) (bool, error) {
|
||||
for {
|
||||
if done, err := r.renderCurrentMap(stopCb); !done {
|
||||
return done, err // stopped or failed
|
||||
}
|
||||
// map finished
|
||||
r.finishedMaps[r.currentMap.mapIndex] = r.currentMap
|
||||
r.afterLastFinished++
|
||||
if len(r.finishedMaps) >= maxMapsPerBatch || r.afterLastFinished&(r.f.baseRowGroupLength-1) == 0 {
|
||||
if err := r.writeFinishedMaps(stopCb); err != nil {
|
||||
return false, err
|
||||
}
|
||||
writeCb()
|
||||
}
|
||||
if r.afterLastFinished == r.afterLastMap || r.iterator.finished {
|
||||
if err := r.writeFinishedMaps(stopCb); err != nil {
|
||||
return false, err
|
||||
}
|
||||
writeCb()
|
||||
return true, nil
|
||||
}
|
||||
r.currentMap = &renderedMap{
|
||||
filterMap: r.f.emptyFilterMap(),
|
||||
mapIndex: r.afterLastFinished,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// renderCurrentMap renders a single map.
|
||||
func (r *mapRenderer) renderCurrentMap(stopCb func() bool) (bool, error) {
|
||||
if !r.iterator.updateChainView(r.f.targetView) {
|
||||
return false, errChainUpdate
|
||||
}
|
||||
var waitCnt int
|
||||
|
||||
if r.iterator.lvIndex == 0 {
|
||||
r.currentMap.blockLvPtrs = []uint64{0}
|
||||
}
|
||||
type lvPos struct{ rowIndex, layerIndex uint32 }
|
||||
rowMappingCache := lru.NewCache[common.Hash, lvPos](cachedRowMappings)
|
||||
defer rowMappingCache.Purge()
|
||||
|
||||
for r.iterator.lvIndex < uint64(r.currentMap.mapIndex+1)<<r.f.logValuesPerMap && !r.iterator.finished {
|
||||
waitCnt++
|
||||
if waitCnt >= valuesPerCallback {
|
||||
if stopCb() {
|
||||
return false, nil
|
||||
}
|
||||
if !r.iterator.updateChainView(r.f.targetView) {
|
||||
return false, errChainUpdate
|
||||
}
|
||||
waitCnt = 0
|
||||
}
|
||||
r.currentMap.lastBlock = r.iterator.blockNumber
|
||||
if r.iterator.delimiter {
|
||||
r.currentMap.lastBlock++
|
||||
r.currentMap.blockLvPtrs = append(r.currentMap.blockLvPtrs, r.iterator.lvIndex+1)
|
||||
}
|
||||
if logValue := r.iterator.getValueHash(); logValue != (common.Hash{}) {
|
||||
lvp, cached := rowMappingCache.Get(logValue)
|
||||
if !cached {
|
||||
lvp = lvPos{rowIndex: r.f.rowIndex(r.currentMap.mapIndex, 0, logValue)}
|
||||
}
|
||||
for uint32(len(r.currentMap.filterMap[lvp.rowIndex])) >= r.f.maxRowLength(lvp.layerIndex) {
|
||||
lvp.layerIndex++
|
||||
lvp.rowIndex = r.f.rowIndex(r.currentMap.mapIndex, lvp.layerIndex, logValue)
|
||||
cached = false
|
||||
}
|
||||
r.currentMap.filterMap[lvp.rowIndex] = append(r.currentMap.filterMap[lvp.rowIndex], r.f.columnIndex(r.iterator.lvIndex, &logValue))
|
||||
if !cached {
|
||||
rowMappingCache.Add(logValue, lvp)
|
||||
}
|
||||
}
|
||||
if err := r.iterator.next(); err != nil {
|
||||
return false, fmt.Errorf("failed to advance log iterator at %d while rendering map %d: %v", r.iterator.lvIndex, r.currentMap.mapIndex, err)
|
||||
}
|
||||
if !r.f.testDisableSnapshots && r.afterLastMap >= r.f.indexedRange.afterLastRenderedMap &&
|
||||
(r.iterator.delimiter || r.iterator.finished) {
|
||||
r.makeSnapshot()
|
||||
}
|
||||
}
|
||||
if r.iterator.finished {
|
||||
r.currentMap.finished = true
|
||||
r.currentMap.headDelimiter = r.iterator.lvIndex
|
||||
}
|
||||
r.currentMap.lastBlockId = r.f.targetView.getBlockId(r.currentMap.lastBlock)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// writeFinishedMaps writes rendered maps to the database and updates
|
||||
// filterMapsRange and indexedView accordingly.
|
||||
func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error {
|
||||
if len(r.finishedMaps) == 0 {
|
||||
return nil
|
||||
}
|
||||
r.f.indexLock.Lock()
|
||||
defer r.f.indexLock.Unlock()
|
||||
|
||||
oldRange := r.f.indexedRange
|
||||
tempRange, err := r.getTempRange()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get temporary rendered range: %v", err)
|
||||
}
|
||||
newRange, err := r.getUpdatedRange()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get updated rendered range: %v", err)
|
||||
}
|
||||
renderedView := r.f.targetView // stopCb callback might still change targetView while writing finished maps
|
||||
|
||||
batch := r.f.db.NewBatch()
|
||||
var writeCnt int
|
||||
checkWriteCnt := func() {
|
||||
writeCnt++
|
||||
if writeCnt == rowsPerBatch {
|
||||
writeCnt = 0
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Error writing log index update batch", "error", err)
|
||||
}
|
||||
// do not exit while in partially written state but do allow processing
|
||||
// events and pausing while block processing is in progress
|
||||
pauseCb()
|
||||
batch = r.f.db.NewBatch()
|
||||
}
|
||||
}
|
||||
|
||||
r.f.setRange(batch, r.f.indexedView, tempRange)
|
||||
// add or update filter rows
|
||||
for rowIndex := uint32(0); rowIndex < r.f.mapHeight; rowIndex++ {
|
||||
var (
|
||||
mapIndices []uint32
|
||||
rows []FilterRow
|
||||
)
|
||||
for mapIndex := r.firstFinished; mapIndex < r.afterLastFinished; mapIndex++ {
|
||||
row := r.finishedMaps[mapIndex].filterMap[rowIndex]
|
||||
if fm, _ := r.f.filterMapCache.Get(mapIndex); fm != nil && row.Equal(fm[rowIndex]) {
|
||||
continue
|
||||
}
|
||||
mapIndices = append(mapIndices, mapIndex)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
if newRange.afterLastRenderedMap == r.afterLastFinished { // head updated; remove future entries
|
||||
for mapIndex := r.afterLastFinished; mapIndex < oldRange.afterLastRenderedMap; mapIndex++ {
|
||||
if fm, _ := r.f.filterMapCache.Get(mapIndex); fm != nil && len(fm[rowIndex]) == 0 {
|
||||
continue
|
||||
}
|
||||
mapIndices = append(mapIndices, mapIndex)
|
||||
rows = append(rows, nil)
|
||||
}
|
||||
}
|
||||
if err := r.f.storeFilterMapRows(batch, mapIndices, rowIndex, rows); err != nil {
|
||||
return fmt.Errorf("failed to store filter maps %v row %d: %v", mapIndices, rowIndex, err)
|
||||
}
|
||||
checkWriteCnt()
|
||||
}
|
||||
// update filter map cache
|
||||
if newRange.afterLastRenderedMap == r.afterLastFinished {
|
||||
// head updated; cache new head maps and remove future entries
|
||||
for mapIndex := r.firstFinished; mapIndex < r.afterLastFinished; mapIndex++ {
|
||||
r.f.filterMapCache.Add(mapIndex, r.finishedMaps[mapIndex].filterMap)
|
||||
}
|
||||
for mapIndex := r.afterLastFinished; mapIndex < oldRange.afterLastRenderedMap; mapIndex++ {
|
||||
r.f.filterMapCache.Remove(mapIndex)
|
||||
}
|
||||
} else {
|
||||
// head not updated; do not cache maps during tail rendering because we
|
||||
// need head maps to be available in the cache
|
||||
for mapIndex := r.firstFinished; mapIndex < r.afterLastFinished; mapIndex++ {
|
||||
r.f.filterMapCache.Remove(mapIndex)
|
||||
}
|
||||
}
|
||||
// add or update block pointers
|
||||
blockNumber := r.finishedMaps[r.firstFinished].firstBlock()
|
||||
for mapIndex := r.firstFinished; mapIndex < r.afterLastFinished; mapIndex++ {
|
||||
renderedMap := r.finishedMaps[mapIndex]
|
||||
r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId)
|
||||
checkWriteCnt()
|
||||
if blockNumber != renderedMap.firstBlock() {
|
||||
panic("non-continuous block numbers")
|
||||
}
|
||||
for _, lvPtr := range renderedMap.blockLvPtrs {
|
||||
r.f.storeBlockLvPointer(batch, blockNumber, lvPtr)
|
||||
checkWriteCnt()
|
||||
blockNumber++
|
||||
}
|
||||
}
|
||||
if newRange.afterLastRenderedMap == r.afterLastFinished { // head updated; remove future entries
|
||||
for mapIndex := r.afterLastFinished; mapIndex < oldRange.afterLastRenderedMap; mapIndex++ {
|
||||
r.f.deleteLastBlockOfMap(batch, mapIndex)
|
||||
checkWriteCnt()
|
||||
}
|
||||
for ; blockNumber < oldRange.afterLastIndexedBlock; blockNumber++ {
|
||||
r.f.deleteBlockLvPointer(batch, blockNumber)
|
||||
checkWriteCnt()
|
||||
}
|
||||
}
|
||||
r.finishedMaps = make(map[uint32]*renderedMap)
|
||||
r.firstFinished = r.afterLastFinished
|
||||
r.f.setRange(batch, renderedView, newRange)
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Error writing log index update batch", "error", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTempRange returns a temporary filterMapsRange that is committed to the
|
||||
// database while the newly rendered maps are partially written. Writing all
|
||||
// processed maps in a single database batch would be a serious hit on db
|
||||
// performance so instead safety is ensured by first reverting the valid map
|
||||
// range to the unchanged region until all new map data is committed.
|
||||
func (r *mapRenderer) getTempRange() (filterMapsRange, error) {
|
||||
tempRange := r.f.indexedRange
|
||||
if err := tempRange.addRenderedRange(r.firstFinished, r.firstFinished, r.afterLastMap, r.f.mapsPerEpoch); err != nil {
|
||||
return filterMapsRange{}, fmt.Errorf("failed to update temporary rendered range: %v", err)
|
||||
}
|
||||
if tempRange.firstRenderedMap != r.f.indexedRange.firstRenderedMap {
|
||||
// first rendered map changed; update first indexed block
|
||||
if tempRange.firstRenderedMap > 0 {
|
||||
lastBlock, _, err := r.f.getLastBlockOfMap(tempRange.firstRenderedMap - 1)
|
||||
if err != nil {
|
||||
return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d before temporary range: %v", tempRange.firstRenderedMap-1, err)
|
||||
}
|
||||
tempRange.firstIndexedBlock = lastBlock + 1
|
||||
} else {
|
||||
tempRange.firstIndexedBlock = 0
|
||||
}
|
||||
}
|
||||
if tempRange.afterLastRenderedMap != r.f.indexedRange.afterLastRenderedMap {
|
||||
// first rendered map changed; update first indexed block
|
||||
if tempRange.afterLastRenderedMap > 0 {
|
||||
lastBlock, _, err := r.f.getLastBlockOfMap(tempRange.afterLastRenderedMap - 1)
|
||||
if err != nil {
|
||||
return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d at the end of temporary range: %v", tempRange.afterLastRenderedMap-1, err)
|
||||
}
|
||||
tempRange.afterLastIndexedBlock = lastBlock
|
||||
} else {
|
||||
tempRange.afterLastIndexedBlock = 0
|
||||
}
|
||||
tempRange.headBlockDelimiter = 0
|
||||
}
|
||||
return tempRange, nil
|
||||
}
|
||||
|
||||
// getUpdatedRange returns the updated filterMapsRange after writing the newly
|
||||
// rendered maps.
|
||||
func (r *mapRenderer) getUpdatedRange() (filterMapsRange, error) {
|
||||
// update filterMapsRange
|
||||
newRange := r.f.indexedRange
|
||||
if err := newRange.addRenderedRange(r.firstFinished, r.afterLastFinished, r.afterLastMap, r.f.mapsPerEpoch); err != nil {
|
||||
return filterMapsRange{}, fmt.Errorf("failed to update rendered range: %v", err)
|
||||
}
|
||||
if newRange.firstRenderedMap != r.f.indexedRange.firstRenderedMap {
|
||||
// first rendered map changed; update first indexed block
|
||||
if newRange.firstRenderedMap > 0 {
|
||||
lastBlock, _, err := r.f.getLastBlockOfMap(newRange.firstRenderedMap - 1)
|
||||
if err != nil {
|
||||
return filterMapsRange{}, fmt.Errorf("failed to retrieve last block of map %d before rendered range: %v", newRange.firstRenderedMap-1, err)
|
||||
}
|
||||
newRange.firstIndexedBlock = lastBlock + 1
|
||||
} else {
|
||||
newRange.firstIndexedBlock = 0
|
||||
}
|
||||
}
|
||||
if newRange.afterLastRenderedMap == r.afterLastFinished {
|
||||
// last rendered map changed; update last indexed block and head pointers
|
||||
lm := r.finishedMaps[r.afterLastFinished-1]
|
||||
newRange.headBlockIndexed = lm.finished
|
||||
if lm.finished {
|
||||
newRange.afterLastIndexedBlock = r.f.targetView.headNumber + 1
|
||||
if lm.lastBlock != r.f.targetView.headNumber {
|
||||
panic("map rendering finished but last block != head block")
|
||||
}
|
||||
newRange.headBlockDelimiter = lm.headDelimiter
|
||||
} else {
|
||||
newRange.afterLastIndexedBlock = lm.lastBlock
|
||||
newRange.headBlockDelimiter = 0
|
||||
}
|
||||
} else {
|
||||
// last rendered map not replaced; ensure that target chain view matches
|
||||
// indexed chain view on the rendered section
|
||||
if lastBlock := r.finishedMaps[r.afterLastFinished-1].lastBlock; !matchViews(r.f.indexedView, r.f.targetView, lastBlock) {
|
||||
return filterMapsRange{}, errChainUpdate
|
||||
}
|
||||
}
|
||||
return newRange, nil
|
||||
}
|
||||
|
||||
// addRenderedRange adds the range [firstRendered, afterLastRendered) and
|
||||
// removes [afterLastRendered, afterLastRemoved) from the set of rendered maps.
|
||||
func (fmr *filterMapsRange) addRenderedRange(firstRendered, afterLastRendered, afterLastRemoved, mapsPerEpoch uint32) error {
|
||||
if !fmr.initialized {
|
||||
return errors.New("log index not initialized")
|
||||
}
|
||||
|
||||
// Here we create a slice of endpoints for the rendered sections. There are two endpoints
|
||||
// for each section: the index of the first map, and the index after the last map in the
|
||||
// section. We then iterate the endpoints -- adding d values -- to determine whether the
|
||||
// sections are contiguous or whether they have a gap.
|
||||
type endpoint struct {
|
||||
m uint32
|
||||
d int
|
||||
}
|
||||
endpoints := []endpoint{{fmr.firstRenderedMap, 1}, {fmr.afterLastRenderedMap, -1}, {firstRendered, 1}, {afterLastRendered, -101}, {afterLastRemoved, 100}}
|
||||
if fmr.tailPartialEpoch > 0 {
|
||||
endpoints = append(endpoints, []endpoint{{fmr.firstRenderedMap - mapsPerEpoch, 1}, {fmr.firstRenderedMap - mapsPerEpoch + fmr.tailPartialEpoch, -1}}...)
|
||||
}
|
||||
sort.Slice(endpoints, func(i, j int) bool { return endpoints[i].m < endpoints[j].m })
|
||||
var (
|
||||
sum int
|
||||
merged []uint32
|
||||
last bool
|
||||
)
|
||||
for i, e := range endpoints {
|
||||
sum += e.d
|
||||
if i < len(endpoints)-1 && endpoints[i+1].m == e.m {
|
||||
continue
|
||||
}
|
||||
if (sum > 0) != last {
|
||||
merged = append(merged, e.m)
|
||||
last = !last
|
||||
}
|
||||
}
|
||||
|
||||
switch len(merged) {
|
||||
case 0:
|
||||
// Initialized database, but no finished maps yet.
|
||||
fmr.tailPartialEpoch = 0
|
||||
fmr.firstRenderedMap = firstRendered
|
||||
fmr.afterLastRenderedMap = firstRendered
|
||||
|
||||
case 2:
|
||||
// One rendered section (no partial tail epoch).
|
||||
fmr.tailPartialEpoch = 0
|
||||
fmr.firstRenderedMap = merged[0]
|
||||
fmr.afterLastRenderedMap = merged[1]
|
||||
|
||||
case 4:
|
||||
// Two rendered sections (with a gap).
|
||||
// First section (merged[0]-merged[1]) is for the partial tail epoch,
|
||||
// and it has to start exactly one epoch before the main section.
|
||||
if merged[2] != merged[0]+mapsPerEpoch {
|
||||
return fmt.Errorf("invalid tail partial epoch: %v", merged)
|
||||
}
|
||||
fmr.tailPartialEpoch = merged[1] - merged[0]
|
||||
fmr.firstRenderedMap = merged[2]
|
||||
fmr.afterLastRenderedMap = merged[3]
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid number of rendered sections: %v", merged)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// logIterator iterates on the linear log value index range.
|
||||
type logIterator struct {
|
||||
chainView *ChainView
|
||||
blockNumber uint64
|
||||
receipts types.Receipts
|
||||
blockStart, delimiter, finished bool
|
||||
txIndex, logIndex, topicIndex int
|
||||
lvIndex uint64
|
||||
}
|
||||
|
||||
var errUnindexedRange = errors.New("unindexed range")
|
||||
|
||||
// newLogIteratorFromBlockDelimiter creates a logIterator starting at the
|
||||
// given block's first log value entry (the block delimiter), according to the
|
||||
// current targetView.
|
||||
func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber uint64) (*logIterator, error) {
|
||||
if blockNumber > f.targetView.headNumber {
|
||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.headNumber)
|
||||
}
|
||||
if blockNumber < f.indexedRange.firstIndexedBlock || blockNumber >= f.indexedRange.afterLastIndexedBlock {
|
||||
return nil, errUnindexedRange
|
||||
}
|
||||
var lvIndex uint64
|
||||
if f.indexedRange.headBlockIndexed && blockNumber+1 == f.indexedRange.afterLastIndexedBlock {
|
||||
lvIndex = f.indexedRange.headBlockDelimiter
|
||||
} else {
|
||||
var err error
|
||||
lvIndex, err = f.getBlockLvPointer(blockNumber + 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve log value pointer of block %d after delimiter: %v", blockNumber+1, err)
|
||||
}
|
||||
lvIndex--
|
||||
}
|
||||
finished := blockNumber == f.targetView.headNumber
|
||||
return &logIterator{
|
||||
chainView: f.targetView,
|
||||
blockNumber: blockNumber,
|
||||
finished: finished,
|
||||
delimiter: !finished,
|
||||
lvIndex: lvIndex,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newLogIteratorFromMapBoundary creates a logIterator starting at the given
|
||||
// map boundary, according to the current targetView.
|
||||
func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock, startLvPtr uint64) (*logIterator, error) {
|
||||
if startBlock > f.targetView.headNumber {
|
||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.headNumber)
|
||||
}
|
||||
// get block receipts
|
||||
receipts := f.targetView.getReceipts(startBlock)
|
||||
if receipts == nil {
|
||||
return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
|
||||
}
|
||||
// initialize iterator at block start
|
||||
l := &logIterator{
|
||||
chainView: f.targetView,
|
||||
blockNumber: startBlock,
|
||||
receipts: receipts,
|
||||
blockStart: true,
|
||||
lvIndex: startLvPtr,
|
||||
}
|
||||
l.nextValid()
|
||||
targetIndex := uint64(mapIndex) << f.logValuesPerMap
|
||||
if l.lvIndex > targetIndex {
|
||||
return nil, fmt.Errorf("log value pointer %d of last block of map is after map boundary %d", l.lvIndex, targetIndex)
|
||||
}
|
||||
// iterate to map boundary
|
||||
for l.lvIndex < targetIndex {
|
||||
if l.finished {
|
||||
return nil, fmt.Errorf("iterator already finished at %d before map boundary target %d", l.lvIndex, targetIndex)
|
||||
}
|
||||
if err := l.next(); err != nil {
|
||||
return nil, fmt.Errorf("failed to advance log iterator at %d before map boundary target %d: %v", l.lvIndex, targetIndex, err)
|
||||
}
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// updateChainView updates the iterator's chain view if it still matches the
|
||||
// previous view at the current position. Returns true if successful.
|
||||
func (l *logIterator) updateChainView(cv *ChainView) bool {
|
||||
if !matchViews(cv, l.chainView, l.blockNumber) {
|
||||
return false
|
||||
}
|
||||
l.chainView = cv
|
||||
return true
|
||||
}
|
||||
|
||||
// getValueHash returns the log value hash at the current position.
|
||||
func (l *logIterator) getValueHash() common.Hash {
|
||||
if l.delimiter || l.finished {
|
||||
return common.Hash{}
|
||||
}
|
||||
log := l.receipts[l.txIndex].Logs[l.logIndex]
|
||||
if l.topicIndex == 0 {
|
||||
return addressValue(log.Address)
|
||||
}
|
||||
return topicValue(log.Topics[l.topicIndex-1])
|
||||
}
|
||||
|
||||
// next moves the iterator to the next log value index.
|
||||
func (l *logIterator) next() error {
|
||||
if l.finished {
|
||||
return nil
|
||||
}
|
||||
if l.delimiter {
|
||||
l.delimiter = false
|
||||
l.blockNumber++
|
||||
l.receipts = l.chainView.getReceipts(l.blockNumber)
|
||||
if l.receipts == nil {
|
||||
return fmt.Errorf("receipts not found for block %d", l.blockNumber)
|
||||
}
|
||||
l.txIndex, l.logIndex, l.topicIndex, l.blockStart = 0, 0, 0, true
|
||||
} else {
|
||||
l.topicIndex++
|
||||
l.blockStart = false
|
||||
}
|
||||
l.lvIndex++
|
||||
l.nextValid()
|
||||
return nil
|
||||
}
|
||||
|
||||
// nextValid updates the internal transaction, log and topic index pointers
|
||||
// to the next existing log value of the given block if necessary.
|
||||
// Note that nextValid does not advance the log value index pointer.
|
||||
func (l *logIterator) nextValid() {
|
||||
for ; l.txIndex < len(l.receipts); l.txIndex++ {
|
||||
receipt := l.receipts[l.txIndex]
|
||||
for ; l.logIndex < len(receipt.Logs); l.logIndex++ {
|
||||
log := receipt.Logs[l.logIndex]
|
||||
if l.topicIndex <= len(log.Topics) {
|
||||
return
|
||||
}
|
||||
l.topicIndex = 0
|
||||
}
|
||||
l.logIndex = 0
|
||||
}
|
||||
if l.blockNumber == l.chainView.headNumber {
|
||||
l.finished = true
|
||||
} else {
|
||||
l.delimiter = true
|
||||
}
|
||||
}
|
||||
934
core/filtermaps/matcher.go
Normal file
934
core/filtermaps/matcher.go
Normal file
|
|
@ -0,0 +1,934 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
const doRuntimeStats = false
|
||||
|
||||
// ErrMatchAll is returned when the specified filter matches everything.
|
||||
// Handling this case in filtermaps would require an extra special case and
|
||||
// would actually be slower than reverting to legacy filter.
|
||||
var ErrMatchAll = errors.New("match all patterns not supported")
|
||||
|
||||
// MatcherBackend defines the functions required for searching in the log index
|
||||
// data structure. It is currently implemented by FilterMapsMatcherBackend but
|
||||
// once EIP-7745 is implemented and active, these functions can also be trustlessly
|
||||
// served by a remote prover.
|
||||
type MatcherBackend interface {
|
||||
GetParams() *Params
|
||||
GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error)
|
||||
GetFilterMapRow(ctx context.Context, mapIndex, rowIndex uint32, baseLayerOnly bool) (FilterRow, error)
|
||||
GetLogByLvIndex(ctx context.Context, lvIndex uint64) (*types.Log, error)
|
||||
SyncLogIndex(ctx context.Context) (SyncRange, error)
|
||||
Close()
|
||||
}
|
||||
|
||||
// SyncRange is returned by MatcherBackend.SyncLogIndex. It contains the latest
|
||||
// chain head, the indexed range that is currently consistent with the chain
|
||||
// and the valid range that has not been changed and has been consistent with
|
||||
// all states of the chain since the previous SyncLogIndex or the creation of
|
||||
// the matcher backend.
|
||||
type SyncRange struct {
|
||||
HeadNumber uint64
|
||||
// block range where the index has not changed since the last matcher sync
|
||||
// and therefore the set of matches found in this region is guaranteed to
|
||||
// be valid and complete.
|
||||
Valid bool
|
||||
FirstValid, LastValid uint64
|
||||
// block range indexed according to the given chain head.
|
||||
Indexed bool
|
||||
FirstIndexed, LastIndexed uint64
|
||||
}
|
||||
|
||||
// GetPotentialMatches returns a list of logs that are potential matches for the
|
||||
// given filter criteria. If parts of the log index in the searched range are
|
||||
// missing or changed during the search process then the resulting logs belonging
|
||||
// to that block range might be missing or incorrect.
|
||||
// Also note that the returned list may contain false positives.
|
||||
func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock, lastBlock uint64, addresses []common.Address, topics [][]common.Hash) ([]*types.Log, error) {
|
||||
params := backend.GetParams()
|
||||
var getLogStats runtimeStats
|
||||
// find the log value index range to search
|
||||
firstIndex, err := backend.GetBlockLvPointer(ctx, firstBlock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve log value pointer for first block %d: %v", firstBlock, err)
|
||||
}
|
||||
lastIndex, err := backend.GetBlockLvPointer(ctx, lastBlock+1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve log value pointer after last block %d: %v", lastBlock, err)
|
||||
}
|
||||
if lastIndex > 0 {
|
||||
lastIndex--
|
||||
}
|
||||
firstMap, lastMap := uint32(firstIndex>>params.logValuesPerMap), uint32(lastIndex>>params.logValuesPerMap)
|
||||
firstEpoch, lastEpoch := firstMap>>params.logMapsPerEpoch, lastMap>>params.logMapsPerEpoch
|
||||
|
||||
// build matcher according to the given filter criteria
|
||||
matchers := make([]matcher, len(topics)+1)
|
||||
// matchAddress signals a match when there is a match for any of the given
|
||||
// addresses.
|
||||
// If the list of addresses is empty then it creates a "wild card" matcher
|
||||
// that signals every index as a potential match.
|
||||
matchAddress := make(matchAny, len(addresses))
|
||||
for i, address := range addresses {
|
||||
matchAddress[i] = &singleMatcher{backend: backend, value: addressValue(address)}
|
||||
}
|
||||
matchers[0] = matchAddress
|
||||
for i, topicList := range topics {
|
||||
// matchTopic signals a match when there is a match for any of the topics
|
||||
// specified for the given position (topicList).
|
||||
// If topicList is empty then it creates a "wild card" matcher that signals
|
||||
// every index as a potential match.
|
||||
matchTopic := make(matchAny, len(topicList))
|
||||
for j, topic := range topicList {
|
||||
matchTopic[j] = &singleMatcher{backend: backend, value: topicValue(topic)}
|
||||
}
|
||||
matchers[i+1] = matchTopic
|
||||
}
|
||||
// matcher is the final sequence matcher that signals a match when all underlying
|
||||
// matchers signal a match for consecutive log value indices.
|
||||
matcher := newMatchSequence(params, matchers)
|
||||
|
||||
// processEpoch returns the potentially matching logs from the given epoch.
|
||||
processEpoch := func(epochIndex uint32) ([]*types.Log, error) {
|
||||
var logs []*types.Log
|
||||
// create a list of map indices to process
|
||||
fm, lm := epochIndex<<params.logMapsPerEpoch, (epochIndex+1)<<params.logMapsPerEpoch-1
|
||||
if fm < firstMap {
|
||||
fm = firstMap
|
||||
}
|
||||
if lm > lastMap {
|
||||
lm = lastMap
|
||||
}
|
||||
//
|
||||
mapIndices := make([]uint32, lm+1-fm)
|
||||
for i := range mapIndices {
|
||||
mapIndices[i] = fm + uint32(i)
|
||||
}
|
||||
// find potential matches
|
||||
matches, err := getAllMatches(ctx, matcher, mapIndices)
|
||||
if err != nil {
|
||||
return logs, err
|
||||
}
|
||||
// get the actual logs located at the matching log value indices
|
||||
var st int
|
||||
getLogStats.setState(&st, stGetLog)
|
||||
defer getLogStats.setState(&st, stNone)
|
||||
for _, m := range matches {
|
||||
if m == nil {
|
||||
return nil, ErrMatchAll
|
||||
}
|
||||
mlogs, err := getLogsFromMatches(ctx, backend, firstIndex, lastIndex, m)
|
||||
if err != nil {
|
||||
return logs, err
|
||||
}
|
||||
logs = append(logs, mlogs...)
|
||||
}
|
||||
getLogStats.addAmount(st, int64(len(logs)))
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
type task struct {
|
||||
epochIndex uint32
|
||||
logs []*types.Log
|
||||
err error
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
taskCh := make(chan *task)
|
||||
var wg sync.WaitGroup
|
||||
defer func() {
|
||||
close(taskCh)
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
worker := func() {
|
||||
for task := range taskCh {
|
||||
if task == nil {
|
||||
break
|
||||
}
|
||||
task.logs, task.err = processEpoch(task.epochIndex)
|
||||
close(task.done)
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
for i := 0; i < 4; i++ {
|
||||
wg.Add(1)
|
||||
go worker()
|
||||
}
|
||||
|
||||
var logs []*types.Log
|
||||
// startEpoch is the next task to send whenever a worker can accept it.
|
||||
// waitEpoch is the next task we are waiting for to finish in order to append
|
||||
// results in the correct order.
|
||||
startEpoch, waitEpoch := firstEpoch, firstEpoch
|
||||
tasks := make(map[uint32]*task)
|
||||
tasks[startEpoch] = &task{epochIndex: startEpoch, done: make(chan struct{})}
|
||||
for waitEpoch <= lastEpoch {
|
||||
select {
|
||||
case taskCh <- tasks[startEpoch]:
|
||||
startEpoch++
|
||||
if startEpoch <= lastEpoch {
|
||||
if tasks[startEpoch] == nil {
|
||||
tasks[startEpoch] = &task{epochIndex: startEpoch, done: make(chan struct{})}
|
||||
}
|
||||
}
|
||||
case <-tasks[waitEpoch].done:
|
||||
logs = append(logs, tasks[waitEpoch].logs...)
|
||||
if err := tasks[waitEpoch].err; err != nil {
|
||||
if err == ErrMatchAll {
|
||||
return logs, err
|
||||
}
|
||||
return logs, fmt.Errorf("failed to process log index epoch %d: %v", waitEpoch, err)
|
||||
}
|
||||
delete(tasks, waitEpoch)
|
||||
waitEpoch++
|
||||
if waitEpoch <= lastEpoch {
|
||||
if tasks[waitEpoch] == nil {
|
||||
tasks[waitEpoch] = &task{epochIndex: waitEpoch, done: make(chan struct{})}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if doRuntimeStats {
|
||||
log.Info("Log search finished", "elapsed", time.Since(start))
|
||||
for i, ma := range matchers {
|
||||
for j, m := range ma.(matchAny) {
|
||||
log.Info("Single matcher stats", "matchSequence", i, "matchAny", j)
|
||||
m.(*singleMatcher).stats.print()
|
||||
}
|
||||
}
|
||||
log.Info("Get log stats")
|
||||
getLogStats.print()
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// getLogsFromMatches returns the list of potentially matching logs located at
|
||||
// the given list of matching log indices. Matches outside the firstIndex to
|
||||
// lastIndex range are not returned.
|
||||
func getLogsFromMatches(ctx context.Context, backend MatcherBackend, firstIndex, lastIndex uint64, matches potentialMatches) ([]*types.Log, error) {
|
||||
var logs []*types.Log
|
||||
for _, match := range matches {
|
||||
if match < firstIndex || match > lastIndex {
|
||||
continue
|
||||
}
|
||||
log, err := backend.GetLogByLvIndex(ctx, match)
|
||||
if err != nil {
|
||||
return logs, fmt.Errorf("failed to retrieve log at index %d: %v", match, err)
|
||||
}
|
||||
if log != nil {
|
||||
logs = append(logs, log)
|
||||
}
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// matcher defines a general abstraction for any matcher configuration that
|
||||
// can instantiate a matcherInstance.
|
||||
type matcher interface {
|
||||
newInstance(mapIndices []uint32) matcherInstance
|
||||
}
|
||||
|
||||
// matcherInstance defines a general abstraction for a matcher configuration
|
||||
// working on a specific set of map indices and eventually returning a list of
|
||||
// potentially matching log value indices.
|
||||
// Note that processing happens per mapping layer, each call returning a set
|
||||
// of results for the maps where the processing has been finished at the given
|
||||
// layer. Map indices can also be dropped before a result is returned for them
|
||||
// in case the result is no longer interesting. Dropping indices twice or after
|
||||
// a result has been returned has no effect. Exactly one matcherResult is
|
||||
// returned per requested map index unless dropped.
|
||||
type matcherInstance interface {
|
||||
getMatchesForLayer(ctx context.Context, layerIndex uint32) ([]matcherResult, error)
|
||||
dropIndices(mapIndices []uint32)
|
||||
}
|
||||
|
||||
// matcherResult contains the list of potentially matching log value indices
|
||||
// for a given map index.
|
||||
type matcherResult struct {
|
||||
mapIndex uint32
|
||||
matches potentialMatches
|
||||
}
|
||||
|
||||
// getAllMatches creates an instance for a given matcher and set of map indices,
|
||||
// iterates through mapping layers and collects all results, then returns all
|
||||
// results in the same order as the map indices were specified.
|
||||
func getAllMatches(ctx context.Context, matcher matcher, mapIndices []uint32) ([]potentialMatches, error) {
|
||||
instance := matcher.newInstance(mapIndices)
|
||||
resultsMap := make(map[uint32]potentialMatches)
|
||||
for layerIndex := uint32(0); len(resultsMap) < len(mapIndices); layerIndex++ {
|
||||
results, err := instance.getMatchesForLayer(ctx, layerIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, result := range results {
|
||||
resultsMap[result.mapIndex] = result.matches
|
||||
}
|
||||
}
|
||||
matches := make([]potentialMatches, len(mapIndices))
|
||||
for i, mapIndex := range mapIndices {
|
||||
matches[i] = resultsMap[mapIndex]
|
||||
}
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
// singleMatcher implements matcher by returning matches for a single log value hash.
|
||||
type singleMatcher struct {
|
||||
backend MatcherBackend
|
||||
value common.Hash
|
||||
stats runtimeStats
|
||||
}
|
||||
|
||||
// singleMatcherInstance is an instance of singleMatcher.
|
||||
type singleMatcherInstance struct {
|
||||
*singleMatcher
|
||||
mapIndices []uint32
|
||||
filterRows map[uint32][]FilterRow
|
||||
}
|
||||
|
||||
// newInstance creates a new instance of singleMatcher.
|
||||
func (m *singleMatcher) newInstance(mapIndices []uint32) matcherInstance {
|
||||
filterRows := make(map[uint32][]FilterRow)
|
||||
for _, idx := range mapIndices {
|
||||
filterRows[idx] = []FilterRow{}
|
||||
}
|
||||
copiedIndices := make([]uint32, len(mapIndices))
|
||||
copy(copiedIndices, mapIndices)
|
||||
return &singleMatcherInstance{
|
||||
singleMatcher: m,
|
||||
mapIndices: copiedIndices,
|
||||
filterRows: filterRows,
|
||||
}
|
||||
}
|
||||
|
||||
// getMatchesForLayer implements matcherInstance.
|
||||
func (m *singleMatcherInstance) getMatchesForLayer(ctx context.Context, layerIndex uint32) (results []matcherResult, err error) {
|
||||
var st int
|
||||
m.stats.setState(&st, stOther)
|
||||
params := m.backend.GetParams()
|
||||
maskedMapIndex, rowIndex := uint32(math.MaxUint32), uint32(0)
|
||||
for _, mapIndex := range m.mapIndices {
|
||||
filterRows, ok := m.filterRows[mapIndex]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if mm := params.maskedMapIndex(mapIndex, layerIndex); mm != maskedMapIndex {
|
||||
// only recalculate rowIndex when necessary
|
||||
maskedMapIndex = mm
|
||||
rowIndex = params.rowIndex(mapIndex, layerIndex, m.value)
|
||||
}
|
||||
if layerIndex == 0 {
|
||||
m.stats.setState(&st, stFetchFirst)
|
||||
} else {
|
||||
m.stats.setState(&st, stFetchMore)
|
||||
}
|
||||
filterRow, err := m.backend.GetFilterMapRow(ctx, mapIndex, rowIndex, layerIndex == 0)
|
||||
if err != nil {
|
||||
m.stats.setState(&st, stNone)
|
||||
return nil, fmt.Errorf("failed to retrieve filter map %d row %d: %v", mapIndex, rowIndex, err)
|
||||
}
|
||||
m.stats.addAmount(st, int64(len(filterRow)))
|
||||
m.stats.setState(&st, stOther)
|
||||
filterRows = append(filterRows, filterRow)
|
||||
if uint32(len(filterRow)) < params.maxRowLength(layerIndex) {
|
||||
m.stats.setState(&st, stProcess)
|
||||
matches := params.potentialMatches(filterRows, mapIndex, m.value)
|
||||
m.stats.addAmount(st, int64(len(matches)))
|
||||
results = append(results, matcherResult{
|
||||
mapIndex: mapIndex,
|
||||
matches: matches,
|
||||
})
|
||||
m.stats.setState(&st, stOther)
|
||||
delete(m.filterRows, mapIndex)
|
||||
} else {
|
||||
m.filterRows[mapIndex] = filterRows
|
||||
}
|
||||
}
|
||||
m.cleanMapIndices()
|
||||
m.stats.setState(&st, stNone)
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// dropIndices implements matcherInstance.
|
||||
func (m *singleMatcherInstance) dropIndices(dropIndices []uint32) {
|
||||
for _, mapIndex := range dropIndices {
|
||||
delete(m.filterRows, mapIndex)
|
||||
}
|
||||
m.cleanMapIndices()
|
||||
}
|
||||
|
||||
// cleanMapIndices removes map indices from the list if there is no matching
|
||||
// filterRows entry because a result has been returned or the index has been
|
||||
// dropped.
|
||||
func (m *singleMatcherInstance) cleanMapIndices() {
|
||||
var j int
|
||||
for i, mapIndex := range m.mapIndices {
|
||||
if _, ok := m.filterRows[mapIndex]; ok {
|
||||
if i != j {
|
||||
m.mapIndices[j] = mapIndex
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
m.mapIndices = m.mapIndices[:j]
|
||||
}
|
||||
|
||||
// matchAny combinines a set of matchers and returns a match for every position
|
||||
// where any of the underlying matchers signaled a match. A zero-length matchAny
|
||||
// acts as a "wild card" that signals a potential match at every position.
|
||||
type matchAny []matcher
|
||||
|
||||
// matchAnyInstance is an instance of matchAny.
|
||||
type matchAnyInstance struct {
|
||||
matchAny
|
||||
childInstances []matcherInstance
|
||||
childResults map[uint32]matchAnyResults
|
||||
}
|
||||
|
||||
// matchAnyResults is used by matchAnyInstance to collect results from all
|
||||
// child matchers for a specific map index. Once all results has been received
|
||||
// a merged result is returned for the given map and this structure is discarded.
|
||||
type matchAnyResults struct {
|
||||
matches []potentialMatches
|
||||
done []bool
|
||||
needMore int
|
||||
}
|
||||
|
||||
// newInstance creates a new instance of matchAny.
|
||||
func (m matchAny) newInstance(mapIndices []uint32) matcherInstance {
|
||||
if len(m) == 1 {
|
||||
return m[0].newInstance(mapIndices)
|
||||
}
|
||||
childResults := make(map[uint32]matchAnyResults)
|
||||
for _, idx := range mapIndices {
|
||||
childResults[idx] = matchAnyResults{
|
||||
matches: make([]potentialMatches, len(m)),
|
||||
done: make([]bool, len(m)),
|
||||
needMore: len(m),
|
||||
}
|
||||
}
|
||||
childInstances := make([]matcherInstance, len(m))
|
||||
for i, matcher := range m {
|
||||
childInstances[i] = matcher.newInstance(mapIndices)
|
||||
}
|
||||
return &matchAnyInstance{
|
||||
matchAny: m,
|
||||
childInstances: childInstances,
|
||||
childResults: childResults,
|
||||
}
|
||||
}
|
||||
|
||||
// getMatchesForLayer implements matcherInstance.
|
||||
func (m *matchAnyInstance) getMatchesForLayer(ctx context.Context, layerIndex uint32) (mergedResults []matcherResult, err error) {
|
||||
if len(m.matchAny) == 0 {
|
||||
// return "wild card" results (potentialMatches(nil) is interpreted as a
|
||||
// potential match at every log value index of the map).
|
||||
mergedResults = make([]matcherResult, len(m.childResults))
|
||||
var i int
|
||||
for mapIndex := range m.childResults {
|
||||
mergedResults[i] = matcherResult{mapIndex: mapIndex, matches: nil}
|
||||
i++
|
||||
}
|
||||
return mergedResults, nil
|
||||
}
|
||||
for i, childInstance := range m.childInstances {
|
||||
results, err := childInstance.getMatchesForLayer(ctx, layerIndex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to evaluate child matcher on layer %d: %v", layerIndex, err)
|
||||
}
|
||||
for _, result := range results {
|
||||
mr, ok := m.childResults[result.mapIndex]
|
||||
if !ok || mr.done[i] {
|
||||
continue
|
||||
}
|
||||
mr.done[i] = true
|
||||
mr.matches[i] = result.matches
|
||||
mr.needMore--
|
||||
if mr.needMore == 0 || result.matches == nil {
|
||||
mergedResults = append(mergedResults, matcherResult{
|
||||
mapIndex: result.mapIndex,
|
||||
matches: mergeResults(mr.matches),
|
||||
})
|
||||
delete(m.childResults, result.mapIndex)
|
||||
} else {
|
||||
m.childResults[result.mapIndex] = mr
|
||||
}
|
||||
}
|
||||
}
|
||||
return mergedResults, nil
|
||||
}
|
||||
|
||||
// dropIndices implements matcherInstance.
|
||||
func (m *matchAnyInstance) dropIndices(dropIndices []uint32) {
|
||||
for _, childInstance := range m.childInstances {
|
||||
childInstance.dropIndices(dropIndices)
|
||||
}
|
||||
for _, mapIndex := range dropIndices {
|
||||
delete(m.childResults, mapIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// mergeResults merges multiple lists of matches into a single one, preserving
|
||||
// ascending order and filtering out any duplicates.
|
||||
func mergeResults(results []potentialMatches) potentialMatches {
|
||||
if len(results) == 0 {
|
||||
return nil
|
||||
}
|
||||
var sumLen int
|
||||
for _, res := range results {
|
||||
if res == nil {
|
||||
// nil is a wild card; all indices in map range are potential matches
|
||||
return nil
|
||||
}
|
||||
sumLen += len(res)
|
||||
}
|
||||
merged := make(potentialMatches, 0, sumLen)
|
||||
for {
|
||||
best := -1
|
||||
for i, res := range results {
|
||||
if len(res) == 0 {
|
||||
continue
|
||||
}
|
||||
if best < 0 || res[0] < results[best][0] {
|
||||
best = i
|
||||
}
|
||||
}
|
||||
if best < 0 {
|
||||
return merged
|
||||
}
|
||||
if len(merged) == 0 || results[best][0] > merged[len(merged)-1] {
|
||||
merged = append(merged, results[best][0])
|
||||
}
|
||||
results[best] = results[best][1:]
|
||||
}
|
||||
}
|
||||
|
||||
// matchSequence combines two matchers, a "base" and a "next" matcher with a
|
||||
// positive integer offset so that the resulting matcher signals a match at log
|
||||
// value index X when the base matcher returns a match at X and the next matcher
|
||||
// gives a match at X+offset. Note that matchSequence can be used recursively to
|
||||
// detect any log value sequence.
|
||||
type matchSequence struct {
|
||||
params *Params
|
||||
base, next matcher
|
||||
offset uint64
|
||||
statsLock sync.Mutex
|
||||
baseStats, nextStats matchOrderStats
|
||||
}
|
||||
|
||||
// newInstance creates a new instance of matchSequence.
|
||||
func (m *matchSequence) newInstance(mapIndices []uint32) matcherInstance {
|
||||
// determine set of indices to request from next matcher
|
||||
nextIndices := make([]uint32, 0, len(mapIndices)*3/2)
|
||||
needMatched := make(map[uint32]struct{})
|
||||
baseRequested := make(map[uint32]struct{})
|
||||
nextRequested := make(map[uint32]struct{})
|
||||
for _, mapIndex := range mapIndices {
|
||||
needMatched[mapIndex] = struct{}{}
|
||||
baseRequested[mapIndex] = struct{}{}
|
||||
if _, ok := nextRequested[mapIndex]; !ok {
|
||||
nextIndices = append(nextIndices, mapIndex)
|
||||
nextRequested[mapIndex] = struct{}{}
|
||||
}
|
||||
nextIndices = append(nextIndices, mapIndex+1)
|
||||
nextRequested[mapIndex+1] = struct{}{}
|
||||
}
|
||||
return &matchSequenceInstance{
|
||||
matchSequence: m,
|
||||
baseInstance: m.base.newInstance(mapIndices),
|
||||
nextInstance: m.next.newInstance(nextIndices),
|
||||
needMatched: needMatched,
|
||||
baseRequested: baseRequested,
|
||||
nextRequested: nextRequested,
|
||||
baseResults: make(map[uint32]potentialMatches),
|
||||
nextResults: make(map[uint32]potentialMatches),
|
||||
}
|
||||
}
|
||||
|
||||
// matchOrderStats collects statistics about the evaluating cost and the
|
||||
// occurrence of empty result sets from both base and next child matchers.
|
||||
// This allows the optimization of the evaluation order by evaluating the
|
||||
// child first that is cheaper and/or gives empty results more often and not
|
||||
// evaluating the other child in most cases.
|
||||
// Note that matchOrderStats is specific to matchSequence and the results are
|
||||
// carried over to future instances as the results are mostly useful when
|
||||
// evaluating layer zero of each instance. For this reason it should be used
|
||||
// in a thread safe way as is may be accessed from multiple worker goroutines.
|
||||
type matchOrderStats struct {
|
||||
totalCount, nonEmptyCount, totalCost uint64
|
||||
}
|
||||
|
||||
// add collects statistics after a child has been evaluated for a certain layer.
|
||||
func (ms *matchOrderStats) add(empty bool, layerIndex uint32) {
|
||||
if empty && layerIndex != 0 {
|
||||
// matchers may be evaluated for higher layers after all results have
|
||||
// been returned. Also, empty results are not relevant when previous
|
||||
// layers yielded matches already, so these cases can be ignored.
|
||||
return
|
||||
}
|
||||
ms.totalCount++
|
||||
if !empty {
|
||||
ms.nonEmptyCount++
|
||||
}
|
||||
ms.totalCost += uint64(layerIndex + 1)
|
||||
}
|
||||
|
||||
// mergeStats merges two sets of matchOrderStats.
|
||||
func (ms *matchOrderStats) mergeStats(add matchOrderStats) {
|
||||
ms.totalCount += add.totalCount
|
||||
ms.nonEmptyCount += add.nonEmptyCount
|
||||
ms.totalCost += add.totalCost
|
||||
}
|
||||
|
||||
// baseFirst returns true if the base child matcher should be evaluated first.
|
||||
func (m *matchSequence) baseFirst() bool {
|
||||
m.statsLock.Lock()
|
||||
bf := float64(m.baseStats.totalCost)*float64(m.nextStats.totalCount)+
|
||||
float64(m.baseStats.nonEmptyCount)*float64(m.nextStats.totalCost) <
|
||||
float64(m.baseStats.totalCost)*float64(m.nextStats.nonEmptyCount)+
|
||||
float64(m.nextStats.totalCost)*float64(m.baseStats.totalCount)
|
||||
m.statsLock.Unlock()
|
||||
return bf
|
||||
}
|
||||
|
||||
// mergeBaseStats merges a set of matchOrderStats into the base matcher stats.
|
||||
func (m *matchSequence) mergeBaseStats(stats matchOrderStats) {
|
||||
m.statsLock.Lock()
|
||||
m.baseStats.mergeStats(stats)
|
||||
m.statsLock.Unlock()
|
||||
}
|
||||
|
||||
// mergeNextStats merges a set of matchOrderStats into the next matcher stats.
|
||||
func (m *matchSequence) mergeNextStats(stats matchOrderStats) {
|
||||
m.statsLock.Lock()
|
||||
m.nextStats.mergeStats(stats)
|
||||
m.statsLock.Unlock()
|
||||
}
|
||||
|
||||
// newMatchSequence creates a recursive sequence matcher from a list of underlying
|
||||
// matchers. The resulting matcher signals a match at log value index X when each
|
||||
// underlying matcher matchers[i] returns a match at X+i.
|
||||
func newMatchSequence(params *Params, matchers []matcher) matcher {
|
||||
if len(matchers) == 0 {
|
||||
panic("zero length sequence matchers are not allowed")
|
||||
}
|
||||
if len(matchers) == 1 {
|
||||
return matchers[0]
|
||||
}
|
||||
return &matchSequence{
|
||||
params: params,
|
||||
base: newMatchSequence(params, matchers[:len(matchers)-1]),
|
||||
next: matchers[len(matchers)-1],
|
||||
offset: uint64(len(matchers) - 1),
|
||||
}
|
||||
}
|
||||
|
||||
// matchSequenceInstance is an instance of matchSequence.
|
||||
type matchSequenceInstance struct {
|
||||
*matchSequence
|
||||
baseInstance, nextInstance matcherInstance
|
||||
baseRequested, nextRequested, needMatched map[uint32]struct{}
|
||||
baseResults, nextResults map[uint32]potentialMatches
|
||||
}
|
||||
|
||||
// getMatchesForLayer implements matcherInstance.
|
||||
func (m *matchSequenceInstance) getMatchesForLayer(ctx context.Context, layerIndex uint32) (matchedResults []matcherResult, err error) {
|
||||
// decide whether to evaluate base or next matcher first
|
||||
baseFirst := m.baseFirst()
|
||||
if baseFirst {
|
||||
if err := m.evalBase(ctx, layerIndex); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := m.evalNext(ctx, layerIndex); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !baseFirst {
|
||||
if err := m.evalBase(ctx, layerIndex); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// evaluate and return matched results where possible
|
||||
for mapIndex := range m.needMatched {
|
||||
if _, ok := m.baseRequested[mapIndex]; ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := m.nextRequested[mapIndex]; ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := m.nextRequested[mapIndex+1]; ok {
|
||||
continue
|
||||
}
|
||||
matchedResults = append(matchedResults, matcherResult{
|
||||
mapIndex: mapIndex,
|
||||
matches: m.params.matchResults(mapIndex, m.offset, m.baseResults[mapIndex], m.nextResults[mapIndex], m.nextResults[mapIndex+1]),
|
||||
})
|
||||
delete(m.needMatched, mapIndex)
|
||||
}
|
||||
return matchedResults, nil
|
||||
}
|
||||
|
||||
// dropIndices implements matcherInstance.
|
||||
func (m *matchSequenceInstance) dropIndices(dropIndices []uint32) {
|
||||
for _, mapIndex := range dropIndices {
|
||||
delete(m.needMatched, mapIndex)
|
||||
}
|
||||
var dropBase, dropNext []uint32
|
||||
for _, mapIndex := range dropIndices {
|
||||
if m.dropBase(mapIndex) {
|
||||
dropBase = append(dropBase, mapIndex)
|
||||
}
|
||||
}
|
||||
m.baseInstance.dropIndices(dropBase)
|
||||
for _, mapIndex := range dropIndices {
|
||||
if m.dropNext(mapIndex) {
|
||||
dropNext = append(dropNext, mapIndex)
|
||||
}
|
||||
if m.dropNext(mapIndex + 1) {
|
||||
dropNext = append(dropNext, mapIndex+1)
|
||||
}
|
||||
}
|
||||
m.nextInstance.dropIndices(dropNext)
|
||||
}
|
||||
|
||||
// evalBase evaluates the base child matcher and drops map indices from the
|
||||
// next matcher if possible.
|
||||
func (m *matchSequenceInstance) evalBase(ctx context.Context, layerIndex uint32) error {
|
||||
results, err := m.baseInstance.getMatchesForLayer(ctx, layerIndex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to evaluate base matcher on layer %d: %v", layerIndex, err)
|
||||
}
|
||||
var (
|
||||
dropIndices []uint32
|
||||
stats matchOrderStats
|
||||
)
|
||||
for _, r := range results {
|
||||
m.baseResults[r.mapIndex] = r.matches
|
||||
delete(m.baseRequested, r.mapIndex)
|
||||
stats.add(r.matches != nil && len(r.matches) == 0, layerIndex)
|
||||
}
|
||||
m.mergeBaseStats(stats)
|
||||
for _, r := range results {
|
||||
if m.dropNext(r.mapIndex) {
|
||||
dropIndices = append(dropIndices, r.mapIndex)
|
||||
}
|
||||
if m.dropNext(r.mapIndex + 1) {
|
||||
dropIndices = append(dropIndices, r.mapIndex+1)
|
||||
}
|
||||
}
|
||||
if len(dropIndices) > 0 {
|
||||
m.nextInstance.dropIndices(dropIndices)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// evalNext evaluates the next child matcher and drops map indices from the
|
||||
// base matcher if possible.
|
||||
func (m *matchSequenceInstance) evalNext(ctx context.Context, layerIndex uint32) error {
|
||||
results, err := m.nextInstance.getMatchesForLayer(ctx, layerIndex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to evaluate next matcher on layer %d: %v", layerIndex, err)
|
||||
}
|
||||
var (
|
||||
dropIndices []uint32
|
||||
stats matchOrderStats
|
||||
)
|
||||
for _, r := range results {
|
||||
m.nextResults[r.mapIndex] = r.matches
|
||||
delete(m.nextRequested, r.mapIndex)
|
||||
stats.add(r.matches != nil && len(r.matches) == 0, layerIndex)
|
||||
}
|
||||
m.mergeNextStats(stats)
|
||||
for _, r := range results {
|
||||
if r.mapIndex > 0 && m.dropBase(r.mapIndex-1) {
|
||||
dropIndices = append(dropIndices, r.mapIndex-1)
|
||||
}
|
||||
if m.dropBase(r.mapIndex) {
|
||||
dropIndices = append(dropIndices, r.mapIndex)
|
||||
}
|
||||
}
|
||||
if len(dropIndices) > 0 {
|
||||
m.baseInstance.dropIndices(dropIndices)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dropBase checks whether the given map index can be dropped from the base
|
||||
// matcher based on the known results from the next matcher and removes it
|
||||
// from the internal requested set and returns true if possible.
|
||||
func (m *matchSequenceInstance) dropBase(mapIndex uint32) bool {
|
||||
if _, ok := m.baseRequested[mapIndex]; !ok {
|
||||
return false
|
||||
}
|
||||
if _, ok := m.needMatched[mapIndex]; ok {
|
||||
if next := m.nextResults[mapIndex]; next == nil ||
|
||||
(len(next) > 0 && next[len(next)-1] >= (uint64(mapIndex)<<m.params.logValuesPerMap)+m.offset) {
|
||||
return false
|
||||
}
|
||||
if nextNext := m.nextResults[mapIndex+1]; nextNext == nil ||
|
||||
(len(nextNext) > 0 && nextNext[0] < (uint64(mapIndex+1)<<m.params.logValuesPerMap)+m.offset) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
delete(m.baseRequested, mapIndex)
|
||||
return true
|
||||
}
|
||||
|
||||
// dropNext checks whether the given map index can be dropped from the next
|
||||
// matcher based on the known results from the base matcher and removes it
|
||||
// from the internal requested set and returns true if possible.
|
||||
func (m *matchSequenceInstance) dropNext(mapIndex uint32) bool {
|
||||
if _, ok := m.nextRequested[mapIndex]; !ok {
|
||||
return false
|
||||
}
|
||||
if _, ok := m.needMatched[mapIndex-1]; ok {
|
||||
if prevBase := m.baseResults[mapIndex-1]; prevBase == nil ||
|
||||
(len(prevBase) > 0 && prevBase[len(prevBase)-1]+m.offset >= (uint64(mapIndex)<<m.params.logValuesPerMap)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if _, ok := m.needMatched[mapIndex]; ok {
|
||||
if base := m.baseResults[mapIndex]; base == nil ||
|
||||
(len(base) > 0 && base[0]+m.offset < (uint64(mapIndex+1)<<m.params.logValuesPerMap)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
delete(m.nextRequested, mapIndex)
|
||||
return true
|
||||
}
|
||||
|
||||
// matchResults returns a list of sequence matches for the given mapIndex and
|
||||
// offset based on the base matcher's results at mapIndex and the next matcher's
|
||||
// results at mapIndex and mapIndex+1. Note that acquiring nextNextRes may be
|
||||
// skipped and it can be substituted with an empty list if baseRes has no potential
|
||||
// matches that could be sequence matched with anything that could be in nextNextRes.
|
||||
func (params *Params) matchResults(mapIndex uint32, offset uint64, baseRes, nextRes, nextNextRes potentialMatches) potentialMatches {
|
||||
if nextRes == nil || (baseRes != nil && len(baseRes) == 0) {
|
||||
// if nextRes is a wild card or baseRes is empty then the sequence matcher
|
||||
// result equals baseRes.
|
||||
return baseRes
|
||||
}
|
||||
if len(nextRes) > 0 {
|
||||
// discard items from nextRes whose corresponding base matcher results
|
||||
// with the negative offset applied would be located at mapIndex-1.
|
||||
start := 0
|
||||
for start < len(nextRes) && nextRes[start] < uint64(mapIndex)<<params.logValuesPerMap+offset {
|
||||
start++
|
||||
}
|
||||
nextRes = nextRes[start:]
|
||||
}
|
||||
if len(nextNextRes) > 0 {
|
||||
// discard items from nextNextRes whose corresponding base matcher results
|
||||
// with the negative offset applied would still be located at mapIndex+1.
|
||||
stop := 0
|
||||
for stop < len(nextNextRes) && nextNextRes[stop] < uint64(mapIndex+1)<<params.logValuesPerMap+offset {
|
||||
stop++
|
||||
}
|
||||
nextNextRes = nextNextRes[:stop]
|
||||
}
|
||||
maxLen := len(nextRes) + len(nextNextRes)
|
||||
if maxLen == 0 {
|
||||
return nextRes
|
||||
}
|
||||
if len(baseRes) < maxLen {
|
||||
maxLen = len(baseRes)
|
||||
}
|
||||
// iterate through baseRes, nextRes and nextNextRes and collect matching results.
|
||||
matchedRes := make(potentialMatches, 0, maxLen)
|
||||
for _, nextRes := range []potentialMatches{nextRes, nextNextRes} {
|
||||
if baseRes != nil {
|
||||
for len(nextRes) > 0 && len(baseRes) > 0 {
|
||||
if nextRes[0] > baseRes[0]+offset {
|
||||
baseRes = baseRes[1:]
|
||||
} else if nextRes[0] < baseRes[0]+offset {
|
||||
nextRes = nextRes[1:]
|
||||
} else {
|
||||
matchedRes = append(matchedRes, baseRes[0])
|
||||
baseRes = baseRes[1:]
|
||||
nextRes = nextRes[1:]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// baseRes is a wild card so just return next matcher results with
|
||||
// negative offset.
|
||||
for len(nextRes) > 0 {
|
||||
matchedRes = append(matchedRes, nextRes[0]-offset)
|
||||
nextRes = nextRes[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchedRes
|
||||
}
|
||||
|
||||
// runtimeStats collects processing time statistics while searching in the log
|
||||
// index. Used only when the doRuntimeStats global flag is true.
|
||||
type runtimeStats struct {
|
||||
dt, cnt, amount [stCount]int64
|
||||
}
|
||||
|
||||
const (
|
||||
stNone = iota
|
||||
stFetchFirst
|
||||
stFetchMore
|
||||
stProcess
|
||||
stGetLog
|
||||
stOther
|
||||
stCount
|
||||
)
|
||||
|
||||
var stNames = []string{"", "fetchFirst", "fetchMore", "process", "getLog", "other"}
|
||||
|
||||
// set sets the processing state to one of the pre-defined constants.
|
||||
// Processing time spent in each state is measured separately.
|
||||
func (ts *runtimeStats) setState(state *int, newState int) {
|
||||
if !doRuntimeStats || newState == *state {
|
||||
return
|
||||
}
|
||||
now := int64(mclock.Now())
|
||||
atomic.AddInt64(&ts.dt[*state], now)
|
||||
atomic.AddInt64(&ts.dt[newState], -now)
|
||||
atomic.AddInt64(&ts.cnt[newState], 1)
|
||||
*state = newState
|
||||
}
|
||||
|
||||
func (ts *runtimeStats) addAmount(state int, amount int64) {
|
||||
atomic.AddInt64(&ts.amount[state], amount)
|
||||
}
|
||||
|
||||
// print prints the collected statistics.
|
||||
func (ts *runtimeStats) print() {
|
||||
for i := 1; i < stCount; i++ {
|
||||
log.Info("Matcher stats", "name", stNames[i], "dt", time.Duration(ts.dt[i]), "count", ts.cnt[i], "amount", ts.amount[i])
|
||||
}
|
||||
}
|
||||
206
core/filtermaps/matcher_backend.go
Normal file
206
core/filtermaps/matcher_backend.go
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// FilterMapsMatcherBackend implements MatcherBackend.
|
||||
type FilterMapsMatcherBackend struct {
|
||||
f *FilterMaps
|
||||
|
||||
// these fields should be accessed under f.matchersLock mutex.
|
||||
valid bool
|
||||
firstValid, lastValid uint64
|
||||
syncCh chan SyncRange
|
||||
}
|
||||
|
||||
// NewMatcherBackend returns a FilterMapsMatcherBackend after registering it in
|
||||
// the active matcher set.
|
||||
// Note that Close should always be called when the matcher is no longer used.
|
||||
func (f *FilterMaps) NewMatcherBackend() *FilterMapsMatcherBackend {
|
||||
f.indexLock.RLock()
|
||||
f.matchersLock.Lock()
|
||||
defer func() {
|
||||
f.matchersLock.Unlock()
|
||||
f.indexLock.RUnlock()
|
||||
}()
|
||||
|
||||
fm := &FilterMapsMatcherBackend{
|
||||
f: f,
|
||||
valid: f.indexedRange.initialized && f.indexedRange.afterLastIndexedBlock > f.indexedRange.firstIndexedBlock,
|
||||
firstValid: f.indexedRange.firstIndexedBlock,
|
||||
lastValid: f.indexedRange.afterLastIndexedBlock - 1,
|
||||
}
|
||||
f.matchers[fm] = struct{}{}
|
||||
return fm
|
||||
}
|
||||
|
||||
// GetParams returns the filtermaps parameters.
|
||||
// GetParams implements MatcherBackend.
|
||||
func (fm *FilterMapsMatcherBackend) GetParams() *Params {
|
||||
return &fm.f.Params
|
||||
}
|
||||
|
||||
// Close removes the matcher from the set of active matchers and ensures that
|
||||
// any SyncLogIndex calls are cancelled.
|
||||
// Close implements MatcherBackend.
|
||||
func (fm *FilterMapsMatcherBackend) Close() {
|
||||
fm.f.matchersLock.Lock()
|
||||
defer fm.f.matchersLock.Unlock()
|
||||
|
||||
delete(fm.f.matchers, fm)
|
||||
}
|
||||
|
||||
// GetFilterMapRow returns the given row of the given map. If the row is empty
|
||||
// then a non-nil zero length row is returned. If baseLayerOnly is true then
|
||||
// only the first baseRowLength entries of the row are guaranteed to be
|
||||
// returned.
|
||||
// Note that the returned slices should not be modified, they should be copied
|
||||
// on write.
|
||||
// GetFilterMapRow implements MatcherBackend.
|
||||
func (fm *FilterMapsMatcherBackend) GetFilterMapRow(ctx context.Context, mapIndex, rowIndex uint32, baseLayerOnly bool) (FilterRow, error) {
|
||||
return fm.f.getFilterMapRow(mapIndex, rowIndex, baseLayerOnly)
|
||||
}
|
||||
|
||||
// GetBlockLvPointer returns the starting log value index where the log values
|
||||
// generated by the given block are located. If blockNumber is beyond the current
|
||||
// head then the first unoccupied log value index is returned.
|
||||
// GetBlockLvPointer implements MatcherBackend.
|
||||
func (fm *FilterMapsMatcherBackend) GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error) {
|
||||
fm.f.indexLock.RLock()
|
||||
defer fm.f.indexLock.RUnlock()
|
||||
|
||||
return fm.f.getBlockLvPointer(blockNumber)
|
||||
}
|
||||
|
||||
// GetLogByLvIndex returns the log at the given log value index.
|
||||
// Note that this function assumes that the log index structure is consistent
|
||||
// with the canonical chain at the point where the given log value index points.
|
||||
// If this is not the case then an invalid result may be returned or certain
|
||||
// logs might not be returned at all.
|
||||
// No error is returned though because of an inconsistency between the chain and
|
||||
// the log index. It is the caller's responsibility to verify this consistency
|
||||
// using SyncLogIndex and re-process certain blocks if necessary.
|
||||
// GetLogByLvIndex implements MatcherBackend.
|
||||
func (fm *FilterMapsMatcherBackend) GetLogByLvIndex(ctx context.Context, lvIndex uint64) (*types.Log, error) {
|
||||
fm.f.indexLock.RLock()
|
||||
defer fm.f.indexLock.RUnlock()
|
||||
|
||||
return fm.f.getLogByLvIndex(lvIndex)
|
||||
}
|
||||
|
||||
// synced signals to the matcher that has triggered a synchronisation that it
|
||||
// has been finished and the log index is consistent with the chain head passed
|
||||
// as a parameter.
|
||||
// Note that if the log index head was far behind the chain head then it might not
|
||||
// be synced up to the given head in a single step. Still, the latest chain head
|
||||
// should be passed as a parameter and the existing log index should be consistent
|
||||
// with that chain.
|
||||
func (fm *FilterMapsMatcherBackend) synced() {
|
||||
fm.f.indexLock.RLock()
|
||||
fm.f.matchersLock.Lock()
|
||||
defer func() {
|
||||
fm.f.matchersLock.Unlock()
|
||||
fm.f.indexLock.RUnlock()
|
||||
}()
|
||||
|
||||
var (
|
||||
indexed bool
|
||||
lastIndexed, subLastIndexed uint64
|
||||
)
|
||||
if !fm.f.indexedRange.headBlockIndexed {
|
||||
subLastIndexed = 1
|
||||
}
|
||||
if fm.f.indexedRange.afterLastIndexedBlock-subLastIndexed > fm.f.indexedRange.firstIndexedBlock {
|
||||
indexed, lastIndexed = true, fm.f.indexedRange.afterLastIndexedBlock-subLastIndexed-1
|
||||
}
|
||||
fm.syncCh <- SyncRange{
|
||||
HeadNumber: fm.f.indexedView.headNumber,
|
||||
Valid: fm.valid,
|
||||
FirstValid: fm.firstValid,
|
||||
LastValid: fm.lastValid,
|
||||
Indexed: indexed,
|
||||
FirstIndexed: fm.f.indexedRange.firstIndexedBlock,
|
||||
LastIndexed: lastIndexed,
|
||||
}
|
||||
fm.valid = indexed
|
||||
fm.firstValid = fm.f.indexedRange.firstIndexedBlock
|
||||
fm.lastValid = lastIndexed
|
||||
fm.syncCh = nil
|
||||
}
|
||||
|
||||
// SyncLogIndex ensures that the log index is consistent with the current state
|
||||
// of the chain and is synced up to the current head. It blocks until this state
|
||||
// is achieved or the context is cancelled.
|
||||
// If successful, it returns a SyncRange that contains the latest chain head,
|
||||
// the indexed range that is currently consistent with the chain and the valid
|
||||
// range that has not been changed and has been consistent with all states of the
|
||||
// chain since the previous SyncLogIndex or the creation of the matcher backend.
|
||||
func (fm *FilterMapsMatcherBackend) SyncLogIndex(ctx context.Context) (SyncRange, error) {
|
||||
if fm.f.disabled {
|
||||
return SyncRange{HeadNumber: fm.f.targetView.headNumber}, nil
|
||||
}
|
||||
|
||||
syncCh := make(chan SyncRange, 1)
|
||||
fm.f.matchersLock.Lock()
|
||||
fm.syncCh = syncCh
|
||||
fm.f.matchersLock.Unlock()
|
||||
|
||||
select {
|
||||
case fm.f.matcherSyncCh <- fm:
|
||||
case <-ctx.Done():
|
||||
return SyncRange{}, ctx.Err()
|
||||
}
|
||||
select {
|
||||
case vr := <-syncCh:
|
||||
return vr, nil
|
||||
case <-ctx.Done():
|
||||
return SyncRange{}, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// updateMatchersValidRange iterates through active matchers and limits their
|
||||
// valid range with the current indexed range. This function should be called
|
||||
// whenever a part of the log index has been removed, before adding new blocks
|
||||
// to it.
|
||||
// Note that this function assumes that the index read lock is being held.
|
||||
func (f *FilterMaps) updateMatchersValidRange() {
|
||||
f.matchersLock.Lock()
|
||||
defer f.matchersLock.Unlock()
|
||||
|
||||
for fm := range f.matchers {
|
||||
if !f.indexedRange.hasIndexedBlocks() {
|
||||
fm.valid = false
|
||||
}
|
||||
if !fm.valid {
|
||||
continue
|
||||
}
|
||||
if fm.firstValid < f.indexedRange.firstIndexedBlock {
|
||||
fm.firstValid = f.indexedRange.firstIndexedBlock
|
||||
}
|
||||
if fm.lastValid >= f.indexedRange.afterLastIndexedBlock {
|
||||
fm.lastValid = f.indexedRange.afterLastIndexedBlock - 1
|
||||
}
|
||||
if fm.firstValid > fm.lastValid {
|
||||
fm.valid = false
|
||||
}
|
||||
}
|
||||
}
|
||||
87
core/filtermaps/matcher_test.go
Normal file
87
core/filtermaps/matcher_test.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
"context"
|
||||
crand "crypto/rand"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestMatcher(t *testing.T) {
|
||||
ts := newTestSetup(t)
|
||||
defer ts.close()
|
||||
|
||||
ts.chain.addBlocks(100, 10, 10, 4, true)
|
||||
ts.setHistory(0, false)
|
||||
ts.fm.WaitIdle()
|
||||
|
||||
for i := 0; i < 2000; i++ {
|
||||
bhash := ts.chain.canonical[rand.Intn(len(ts.chain.canonical))]
|
||||
receipts := ts.chain.receipts[bhash]
|
||||
if len(receipts) == 0 {
|
||||
continue
|
||||
}
|
||||
receipt := receipts[rand.Intn(len(receipts))]
|
||||
if len(receipt.Logs) == 0 {
|
||||
continue
|
||||
}
|
||||
log := receipt.Logs[rand.Intn(len(receipt.Logs))]
|
||||
var ok bool
|
||||
addresses := make([]common.Address, rand.Intn(3))
|
||||
for i := range addresses {
|
||||
crand.Read(addresses[i][:])
|
||||
}
|
||||
if len(addresses) > 0 {
|
||||
addresses[rand.Intn(len(addresses))] = log.Address
|
||||
ok = true
|
||||
}
|
||||
topics := make([][]common.Hash, rand.Intn(len(log.Topics)+1))
|
||||
for j := range topics {
|
||||
topics[j] = make([]common.Hash, rand.Intn(3))
|
||||
for i := range topics[j] {
|
||||
crand.Read(topics[j][i][:])
|
||||
}
|
||||
if len(topics[j]) > 0 {
|
||||
topics[j][rand.Intn(len(topics[j]))] = log.Topics[j]
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
continue // cannot search for match-all pattern
|
||||
}
|
||||
mb := ts.fm.NewMatcherBackend()
|
||||
logs, err := GetPotentialMatches(context.Background(), mb, 0, 1000, addresses, topics)
|
||||
mb.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Log search error: %v", err)
|
||||
}
|
||||
var found bool
|
||||
for _, l := range logs {
|
||||
if l == log {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Log search did not return expected log (addresses: %v, topics: %v, expected log: %v)", addresses, topics, *log)
|
||||
}
|
||||
}
|
||||
}
|
||||
212
core/filtermaps/math.go
Normal file
212
core/filtermaps/math.go
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"hash/fnv"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// Params defines the basic parameters of the log index structure.
|
||||
type Params struct {
|
||||
logMapHeight uint // log2(mapHeight)
|
||||
logMapWidth uint // log2(mapWidth)
|
||||
logMapsPerEpoch uint // log2(mapsPerEpoch)
|
||||
logValuesPerMap uint // log2(logValuesPerMap)
|
||||
baseRowLengthRatio uint // baseRowLength / average row length
|
||||
logLayerDiff uint // maxRowLength log2 growth per layer
|
||||
// derived fields
|
||||
mapHeight uint32 // filter map height (number of rows)
|
||||
mapsPerEpoch uint32 // number of maps in an epoch
|
||||
baseRowLength uint32 // maximum number of log values per row on layer 0
|
||||
valuesPerMap uint64 // number of log values marked on each filter map
|
||||
// not affecting consensus
|
||||
baseRowGroupLength uint32 // length of base row groups in local database
|
||||
}
|
||||
|
||||
// DefaultParams is the set of parameters used on mainnet.
|
||||
var DefaultParams = Params{
|
||||
logMapHeight: 16,
|
||||
logMapWidth: 24,
|
||||
logMapsPerEpoch: 10,
|
||||
logValuesPerMap: 16,
|
||||
baseRowGroupLength: 32,
|
||||
baseRowLengthRatio: 8,
|
||||
logLayerDiff: 4,
|
||||
}
|
||||
|
||||
// RangeTestParams puts one log value per epoch, ensuring block exact tail unindexing for testing
|
||||
var RangeTestParams = Params{
|
||||
logMapHeight: 4,
|
||||
logMapWidth: 24,
|
||||
logMapsPerEpoch: 0,
|
||||
logValuesPerMap: 0,
|
||||
baseRowGroupLength: 32,
|
||||
baseRowLengthRatio: 16, // baseRowLength >= 1
|
||||
logLayerDiff: 4,
|
||||
}
|
||||
|
||||
// deriveFields calculates the derived fields of the parameter set.
|
||||
func (p *Params) deriveFields() {
|
||||
p.mapHeight = uint32(1) << p.logMapHeight
|
||||
p.mapsPerEpoch = uint32(1) << p.logMapsPerEpoch
|
||||
p.valuesPerMap = uint64(1) << p.logValuesPerMap
|
||||
p.baseRowLength = uint32(p.valuesPerMap * uint64(p.baseRowLengthRatio) / uint64(p.mapHeight))
|
||||
}
|
||||
|
||||
// addressValue returns the log value hash of a log emitting address.
|
||||
func addressValue(address common.Address) common.Hash {
|
||||
var result common.Hash
|
||||
hasher := sha256.New()
|
||||
hasher.Write(address[:])
|
||||
hasher.Sum(result[:0])
|
||||
return result
|
||||
}
|
||||
|
||||
// topicValue returns the log value hash of a log topic.
|
||||
func topicValue(topic common.Hash) common.Hash {
|
||||
var result common.Hash
|
||||
hasher := sha256.New()
|
||||
hasher.Write(topic[:])
|
||||
hasher.Sum(result[:0])
|
||||
return result
|
||||
}
|
||||
|
||||
// rowIndex returns the row index in which the given log value should be marked
|
||||
// on the given map and mapping layer. Note that row assignments are re-shuffled
|
||||
// with a different frequency on each mapping layer, allowing efficient disk
|
||||
// access and Merkle proofs for long sections of short rows on lower order
|
||||
// layers while avoiding putting too many heavy rows next to each other on
|
||||
// higher order layers.
|
||||
func (p *Params) rowIndex(mapIndex, layerIndex uint32, logValue common.Hash) uint32 {
|
||||
hasher := sha256.New()
|
||||
hasher.Write(logValue[:])
|
||||
var indexEnc [8]byte
|
||||
binary.LittleEndian.PutUint32(indexEnc[0:4], p.maskedMapIndex(mapIndex, layerIndex))
|
||||
binary.LittleEndian.PutUint32(indexEnc[4:8], layerIndex)
|
||||
hasher.Write(indexEnc[:])
|
||||
var hash common.Hash
|
||||
hasher.Sum(hash[:0])
|
||||
return binary.LittleEndian.Uint32(hash[:4]) % p.mapHeight
|
||||
}
|
||||
|
||||
// columnIndex returns the column index where the given log value at the given
|
||||
// position should be marked.
|
||||
func (p *Params) columnIndex(lvIndex uint64, logValue *common.Hash) uint32 {
|
||||
var indexEnc [8]byte
|
||||
binary.LittleEndian.PutUint64(indexEnc[:], lvIndex)
|
||||
// Note: reusing the hasher brings practically no performance gain and would
|
||||
// require passing it through the entire matcher logic because of multi-thread
|
||||
// matching
|
||||
hasher := fnv.New64a()
|
||||
hasher.Write(indexEnc[:])
|
||||
hasher.Write(logValue[:])
|
||||
hash := hasher.Sum64()
|
||||
hashBits := p.logMapWidth - p.logValuesPerMap
|
||||
return uint32(lvIndex%p.valuesPerMap)<<hashBits + (uint32(hash>>(64-hashBits)) ^ uint32(hash)>>(32-hashBits))
|
||||
}
|
||||
|
||||
// maxRowLength returns the maximum length filter rows are populated up to
|
||||
// when using the given mapping layer. A log value can be marked on the map
|
||||
// according to a given mapping layer if the row mapping on that layer points
|
||||
// to a row that has not yet reached the maxRowLength belonging to that layer.
|
||||
// This means that a row that is considered full on a given layer may still be
|
||||
// extended further on a higher order layer.
|
||||
// Each value is marked on the lowest order layer possible, assuming that marks
|
||||
// are added in ascending log value index order.
|
||||
// When searching for a log value one should consider all layers and process
|
||||
// corresponding rows up until the first one where the row mapped to the given
|
||||
// layer is not full.
|
||||
func (p *Params) maxRowLength(layerIndex uint32) uint32 {
|
||||
logLayerDiff := uint(layerIndex) * p.logLayerDiff
|
||||
if logLayerDiff > p.logMapsPerEpoch {
|
||||
logLayerDiff = p.logMapsPerEpoch
|
||||
}
|
||||
return p.baseRowLength << logLayerDiff
|
||||
}
|
||||
|
||||
// maskedMapIndex returns the index used for row mapping calculation on the
|
||||
// given layer. On layer zero the mapping changes once per epoch, then the
|
||||
// frequency of re-mapping increases with every new layer until it reaches
|
||||
// the frequency where it is different for every mapIndex.
|
||||
func (p *Params) maskedMapIndex(mapIndex, layerIndex uint32) uint32 {
|
||||
logLayerDiff := uint(layerIndex) * p.logLayerDiff
|
||||
if logLayerDiff > p.logMapsPerEpoch {
|
||||
logLayerDiff = p.logMapsPerEpoch
|
||||
}
|
||||
return mapIndex & (uint32(math.MaxUint32) << (p.logMapsPerEpoch - logLayerDiff))
|
||||
}
|
||||
|
||||
// potentialMatches returns the list of log value indices potentially matching
|
||||
// the given log value hash in the range of the filter map the row belongs to.
|
||||
// Note that the list of indices is always sorted and potential duplicates are
|
||||
// removed. Though the column indices are stored in the same order they were
|
||||
// added and therefore the true matches are automatically reverse transformed
|
||||
// in the right order, false positives can ruin this property. Since these can
|
||||
// only be separated from true matches after the combined pattern matching of the
|
||||
// outputs of individual log value matchers and this pattern matcher assumes a
|
||||
// sorted and duplicate-free list of indices, we should ensure these properties
|
||||
// here.
|
||||
func (p *Params) potentialMatches(rows []FilterRow, mapIndex uint32, logValue common.Hash) potentialMatches {
|
||||
results := make(potentialMatches, 0, 8)
|
||||
mapFirst := uint64(mapIndex) << p.logValuesPerMap
|
||||
for i, row := range rows {
|
||||
rowLen, maxLen := len(row), int(p.maxRowLength(uint32(i)))
|
||||
if rowLen > maxLen {
|
||||
rowLen = maxLen // any additional entries are generated by another log value on a higher mapping layer
|
||||
}
|
||||
for i := 0; i < rowLen; i++ {
|
||||
if potentialMatch := mapFirst + uint64(row[i]>>(p.logMapWidth-p.logValuesPerMap)); row[i] == p.columnIndex(potentialMatch, &logValue) {
|
||||
results = append(results, potentialMatch)
|
||||
}
|
||||
}
|
||||
if rowLen < maxLen {
|
||||
break
|
||||
}
|
||||
if i == len(rows)-1 {
|
||||
panic("potentialMatches: insufficient list of row alternatives")
|
||||
}
|
||||
}
|
||||
sort.Sort(results)
|
||||
// remove duplicates
|
||||
j := 0
|
||||
for i, match := range results {
|
||||
if i == 0 || match != results[i-1] {
|
||||
results[j] = results[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return results[:j]
|
||||
}
|
||||
|
||||
// potentialMatches is a strictly monotonically increasing list of log value
|
||||
// indices in the range of a filter map that are potential matches for certain
|
||||
// filter criteria.
|
||||
// potentialMatches implements sort.Interface.
|
||||
// Note that nil is used as a wildcard and therefore means that all log value
|
||||
// indices in the filter map range are potential matches. If there are no
|
||||
// potential matches in the given map's range then an empty slice should be used.
|
||||
type potentialMatches []uint64
|
||||
|
||||
func (p potentialMatches) Len() int { return len(p) }
|
||||
func (p potentialMatches) Less(i, j int) bool { return p[i] < p[j] }
|
||||
func (p potentialMatches) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
149
core/filtermaps/math_test.go
Normal file
149
core/filtermaps/math_test.go
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2024 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package filtermaps
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestSingleMatch(t *testing.T) {
|
||||
params := DefaultParams
|
||||
params.deriveFields()
|
||||
|
||||
for count := 0; count < 100000; count++ {
|
||||
// generate a row with a single random entry
|
||||
mapIndex := rand.Uint32()
|
||||
lvIndex := uint64(mapIndex)<<params.logValuesPerMap + uint64(rand.Intn(int(params.valuesPerMap)))
|
||||
var lvHash common.Hash
|
||||
crand.Read(lvHash[:])
|
||||
row := FilterRow{params.columnIndex(lvIndex, &lvHash)}
|
||||
matches := params.potentialMatches([]FilterRow{row}, mapIndex, lvHash)
|
||||
// check if it has been reverse transformed correctly
|
||||
if len(matches) != 1 {
|
||||
t.Fatalf("Invalid length of matches (got %d, expected 1)", len(matches))
|
||||
}
|
||||
if matches[0] != lvIndex {
|
||||
if len(matches) != 1 {
|
||||
t.Fatalf("Incorrect match returned (got %d, expected %d)", matches[0], lvIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
testPmCount = 50
|
||||
testPmLen = 1000
|
||||
)
|
||||
|
||||
func TestPotentialMatches(t *testing.T) {
|
||||
params := DefaultParams
|
||||
params.deriveFields()
|
||||
|
||||
var falsePositives int
|
||||
for count := 0; count < testPmCount; count++ {
|
||||
mapIndex := rand.Uint32()
|
||||
lvStart := uint64(mapIndex) << params.logValuesPerMap
|
||||
var row FilterRow
|
||||
lvIndices := make([]uint64, testPmLen)
|
||||
lvHashes := make([]common.Hash, testPmLen+1)
|
||||
for i := range lvIndices {
|
||||
// add testPmLen single entries with different log value hashes at different indices
|
||||
lvIndices[i] = lvStart + uint64(rand.Intn(int(params.valuesPerMap)))
|
||||
crand.Read(lvHashes[i][:])
|
||||
row = append(row, params.columnIndex(lvIndices[i], &lvHashes[i]))
|
||||
}
|
||||
// add the same log value hash at the first testPmLen log value indices of the map's range
|
||||
crand.Read(lvHashes[testPmLen][:])
|
||||
for lvIndex := lvStart; lvIndex < lvStart+testPmLen; lvIndex++ {
|
||||
row = append(row, params.columnIndex(lvIndex, &lvHashes[testPmLen]))
|
||||
}
|
||||
// randomly duplicate some entries
|
||||
for i := 0; i < testPmLen; i++ {
|
||||
row = append(row, row[rand.Intn(len(row))])
|
||||
}
|
||||
// randomly mix up order of elements
|
||||
for i := len(row) - 1; i > 0; i-- {
|
||||
j := rand.Intn(i)
|
||||
row[i], row[j] = row[j], row[i]
|
||||
}
|
||||
// split up into a list of rows if longer than allowed
|
||||
var rows []FilterRow
|
||||
for layerIndex := uint32(0); row != nil; layerIndex++ {
|
||||
maxLen := int(params.maxRowLength(layerIndex))
|
||||
if len(row) > maxLen {
|
||||
rows = append(rows, row[:maxLen])
|
||||
row = row[maxLen:]
|
||||
} else {
|
||||
rows = append(rows, row)
|
||||
row = nil
|
||||
}
|
||||
}
|
||||
// check retrieved matches while also counting false positives
|
||||
for i, lvHash := range lvHashes {
|
||||
matches := params.potentialMatches(rows, mapIndex, lvHash)
|
||||
if i < testPmLen {
|
||||
// check single entry match
|
||||
if len(matches) < 1 {
|
||||
t.Fatalf("Invalid length of matches (got %d, expected >=1)", len(matches))
|
||||
}
|
||||
var found bool
|
||||
for _, lvi := range matches {
|
||||
if lvi == lvIndices[i] {
|
||||
found = true
|
||||
} else {
|
||||
falsePositives++
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Expected match not found (got %v, expected %d)", matches, lvIndices[i])
|
||||
}
|
||||
} else {
|
||||
// check "long series" match
|
||||
if len(matches) < testPmLen {
|
||||
t.Fatalf("Invalid length of matches (got %d, expected >=%d)", len(matches), testPmLen)
|
||||
}
|
||||
// since results are ordered, first testPmLen entries should always match exactly
|
||||
for j := 0; j < testPmLen; j++ {
|
||||
if matches[j] != lvStart+uint64(j) {
|
||||
t.Fatalf("Incorrect match at index %d (got %d, expected %d)", j, matches[j], lvStart+uint64(j))
|
||||
}
|
||||
}
|
||||
// the rest are false positives
|
||||
falsePositives += len(matches) - testPmLen
|
||||
}
|
||||
}
|
||||
}
|
||||
// Whenever looking for a certain log value hash, each entry in the row that
|
||||
// was generated by another log value hash (a "foreign entry") has a
|
||||
// valuesPerMap // 2^32 chance of yielding a false positive if the reverse
|
||||
// transformed 32 bit integer is by random chance less than valuesPerMap and
|
||||
// is therefore considered a potentially valid match.
|
||||
// We have testPmLen unique hash entries and a testPmLen long series of entries
|
||||
// for the same hash. For each of the testPmLen unique hash entries there are
|
||||
// testPmLen*2-1 foreign entries while for the long series there are testPmLen
|
||||
// foreign entries. This means that after performing all these filtering runs,
|
||||
// we have processed 2*testPmLen^2 foreign entries, which given us an estimate
|
||||
// of how many false positives to expect.
|
||||
expFalse := int(uint64(testPmCount*testPmLen*testPmLen*2) * params.valuesPerMap >> params.logMapWidth)
|
||||
if falsePositives < expFalse/2 || falsePositives > expFalse*3/2 {
|
||||
t.Fatalf("False positive rate out of expected range (got %d, expected %d +-50%%)", falsePositives, expFalse)
|
||||
}
|
||||
}
|
||||
|
|
@ -256,7 +256,7 @@ func (e *GenesisMismatchError) Error() string {
|
|||
|
||||
// ChainOverrides contains the changes to chain config.
|
||||
type ChainOverrides struct {
|
||||
OverrideCancun *uint64
|
||||
OverridePrague *uint64
|
||||
OverrideVerkle *uint64
|
||||
}
|
||||
|
||||
|
|
@ -265,8 +265,8 @@ func (o *ChainOverrides) apply(cfg *params.ChainConfig) error {
|
|||
if o == nil || cfg == nil {
|
||||
return nil
|
||||
}
|
||||
if o.OverrideCancun != nil {
|
||||
cfg.CancunTime = o.OverrideCancun
|
||||
if o.OverridePrague != nil {
|
||||
cfg.PragueTime = o.OverridePrague
|
||||
}
|
||||
if o.OverrideVerkle != nil {
|
||||
cfg.VerkleTime = o.OverrideVerkle
|
||||
|
|
@ -386,7 +386,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
|||
|
||||
// LoadChainConfig loads the stored chain config if it is already present in
|
||||
// database, otherwise, return the config in the provided genesis specification.
|
||||
func LoadChainConfig(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, error) {
|
||||
func LoadChainConfig(db ethdb.Database, genesis *Genesis) (cfg *params.ChainConfig, ghash common.Hash, err error) {
|
||||
// Load the stored chain config from the database. It can be nil
|
||||
// in case the database is empty. Notably, we only care about the
|
||||
// chain config corresponds to the canonical chain.
|
||||
|
|
@ -394,27 +394,28 @@ func LoadChainConfig(db ethdb.Database, genesis *Genesis) (*params.ChainConfig,
|
|||
if stored != (common.Hash{}) {
|
||||
storedcfg := rawdb.ReadChainConfig(db, stored)
|
||||
if storedcfg != nil {
|
||||
return storedcfg, nil
|
||||
return storedcfg, stored, nil
|
||||
}
|
||||
}
|
||||
// Load the config from the provided genesis specification
|
||||
if genesis != nil {
|
||||
// Reject invalid genesis spec without valid chain config
|
||||
if genesis.Config == nil {
|
||||
return nil, errGenesisNoConfig
|
||||
return nil, common.Hash{}, errGenesisNoConfig
|
||||
}
|
||||
// If the canonical genesis header is present, but the chain
|
||||
// config is missing(initialize the empty leveldb with an
|
||||
// external ancient chain segment), ensure the provided genesis
|
||||
// is matched.
|
||||
if stored != (common.Hash{}) && genesis.ToBlock().Hash() != stored {
|
||||
return nil, &GenesisMismatchError{stored, genesis.ToBlock().Hash()}
|
||||
ghash := genesis.ToBlock().Hash()
|
||||
if stored != (common.Hash{}) && ghash != stored {
|
||||
return nil, ghash, &GenesisMismatchError{stored, ghash}
|
||||
}
|
||||
return genesis.Config, nil
|
||||
return genesis.Config, ghash, nil
|
||||
}
|
||||
// There is no stored chain config and no new config provided,
|
||||
// In this case the default chain config(mainnet) will be used
|
||||
return params.MainnetChainConfig, nil
|
||||
return params.MainnetChainConfig, params.MainnetGenesisHash, nil
|
||||
}
|
||||
|
||||
// chainConfigOrDefault retrieves the attached chain configuration. If the genesis
|
||||
|
|
@ -636,15 +637,23 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
|||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Difficulty: big.NewInt(0),
|
||||
Alloc: map[common.Address]types.Account{
|
||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
||||
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
||||
common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
|
||||
common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity
|
||||
common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp
|
||||
common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
|
||||
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
|
||||
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
|
||||
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
|
||||
common.BytesToAddress([]byte{0x01}): {Balance: big.NewInt(1)}, // ECRecover
|
||||
common.BytesToAddress([]byte{0x02}): {Balance: big.NewInt(1)}, // SHA256
|
||||
common.BytesToAddress([]byte{0x03}): {Balance: big.NewInt(1)}, // RIPEMD
|
||||
common.BytesToAddress([]byte{0x04}): {Balance: big.NewInt(1)}, // Identity
|
||||
common.BytesToAddress([]byte{0x05}): {Balance: big.NewInt(1)}, // ModExp
|
||||
common.BytesToAddress([]byte{0x06}): {Balance: big.NewInt(1)}, // ECAdd
|
||||
common.BytesToAddress([]byte{0x07}): {Balance: big.NewInt(1)}, // ECScalarMul
|
||||
common.BytesToAddress([]byte{0x08}): {Balance: big.NewInt(1)}, // ECPairing
|
||||
common.BytesToAddress([]byte{0x09}): {Balance: big.NewInt(1)}, // BLAKE2b
|
||||
common.BytesToAddress([]byte{0x0a}): {Balance: big.NewInt(1)}, // KZGPointEval
|
||||
common.BytesToAddress([]byte{0x0b}): {Balance: big.NewInt(1)}, // BLSG1Add
|
||||
common.BytesToAddress([]byte{0x0c}): {Balance: big.NewInt(1)}, // BLSG1MultiExp
|
||||
common.BytesToAddress([]byte{0x0d}): {Balance: big.NewInt(1)}, // BLSG2Add
|
||||
common.BytesToAddress([]byte{0x0e}): {Balance: big.NewInt(1)}, // BLSG2MultiExp
|
||||
common.BytesToAddress([]byte{0x0f}): {Balance: big.NewInt(1)}, // BLSG1Pairing
|
||||
common.BytesToAddress([]byte{0x10}): {Balance: big.NewInt(1)}, // BLSG1MapG1
|
||||
common.BytesToAddress([]byte{0x11}): {Balance: big.NewInt(1)}, // BLSG2MapG2
|
||||
// Pre-deploy system contracts
|
||||
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0},
|
||||
params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0},
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package rawdb
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -179,3 +181,306 @@ func DeleteBloombits(db ethdb.Database, bit uint, from uint64, to uint64) {
|
|||
log.Crit("Failed to delete bloom bits", "err", it.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFilterMapRow retrieves a filter map row at the given mapRowIndex
|
||||
// (see filtermaps.mapRowIndex for the storage index encoding).
|
||||
// Note that zero length rows are not stored in the database and therefore all
|
||||
// non-existent entries are interpreted as empty rows and return no error.
|
||||
// Also note that the mapRowIndex indexing scheme is the same as the one
|
||||
// proposed in EIP-7745 for tree-hashing the filter map structure and for the
|
||||
// same data proximity reasons it is also suitable for database representation.
|
||||
// See also:
|
||||
// https://eips.ethereum.org/EIPS/eip-7745#hash-tree-structure
|
||||
func ReadFilterMapExtRow(db ethdb.KeyValueReader, mapRowIndex uint64, bitLength uint) ([]uint32, error) {
|
||||
byteLength := int(bitLength) / 8
|
||||
if int(bitLength) != byteLength*8 {
|
||||
panic("invalid bit length")
|
||||
}
|
||||
key := filterMapRowKey(mapRowIndex, false)
|
||||
has, err := db.Has(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, nil
|
||||
}
|
||||
encRow, err := db.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(encRow)%byteLength != 0 {
|
||||
return nil, errors.New("Invalid encoded extended filter row length")
|
||||
}
|
||||
row := make([]uint32, len(encRow)/byteLength)
|
||||
var b [4]byte
|
||||
for i := range row {
|
||||
copy(b[:byteLength], encRow[i*byteLength:(i+1)*byteLength])
|
||||
row[i] = binary.LittleEndian.Uint32(b[:])
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func ReadFilterMapBaseRows(db ethdb.KeyValueReader, mapRowIndex uint64, rowCount uint32, bitLength uint) ([][]uint32, error) {
|
||||
byteLength := int(bitLength) / 8
|
||||
if int(bitLength) != byteLength*8 {
|
||||
panic("invalid bit length")
|
||||
}
|
||||
key := filterMapRowKey(mapRowIndex, true)
|
||||
has, err := db.Has(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows := make([][]uint32, rowCount)
|
||||
if !has {
|
||||
return rows, nil
|
||||
}
|
||||
encRows, err := db.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encLen := len(encRows)
|
||||
var (
|
||||
entryCount, entriesInRow, rowIndex, headerLen, headerBits int
|
||||
headerByte byte
|
||||
)
|
||||
for headerLen+byteLength*entryCount < encLen {
|
||||
if headerBits == 0 {
|
||||
headerByte = encRows[headerLen]
|
||||
headerLen++
|
||||
headerBits = 8
|
||||
}
|
||||
if headerByte&1 > 0 {
|
||||
entriesInRow++
|
||||
entryCount++
|
||||
} else {
|
||||
if entriesInRow > 0 {
|
||||
rows[rowIndex] = make([]uint32, entriesInRow)
|
||||
entriesInRow = 0
|
||||
}
|
||||
rowIndex++
|
||||
}
|
||||
headerByte >>= 1
|
||||
headerBits--
|
||||
}
|
||||
if headerLen+byteLength*entryCount > encLen {
|
||||
return nil, errors.New("Invalid encoded base filter rows length")
|
||||
}
|
||||
if entriesInRow > 0 {
|
||||
rows[rowIndex] = make([]uint32, entriesInRow)
|
||||
}
|
||||
nextEntry := headerLen
|
||||
for _, row := range rows {
|
||||
for i := range row {
|
||||
var b [4]byte
|
||||
copy(b[:byteLength], encRows[nextEntry:nextEntry+byteLength])
|
||||
row[i] = binary.LittleEndian.Uint32(b[:])
|
||||
nextEntry += byteLength
|
||||
}
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// WriteFilterMapRow stores a filter map row at the given mapRowIndex or deletes
|
||||
// any existing entry if the row is empty.
|
||||
func WriteFilterMapExtRow(db ethdb.KeyValueWriter, mapRowIndex uint64, row []uint32, bitLength uint) {
|
||||
byteLength := int(bitLength) / 8
|
||||
if int(bitLength) != byteLength*8 {
|
||||
panic("invalid bit length")
|
||||
}
|
||||
var err error
|
||||
if len(row) > 0 {
|
||||
encRow := make([]byte, len(row)*byteLength)
|
||||
for i, c := range row {
|
||||
var b [4]byte
|
||||
binary.LittleEndian.PutUint32(b[:], c)
|
||||
copy(encRow[i*byteLength:(i+1)*byteLength], b[:byteLength])
|
||||
}
|
||||
err = db.Put(filterMapRowKey(mapRowIndex, false), encRow)
|
||||
} else {
|
||||
err = db.Delete(filterMapRowKey(mapRowIndex, false))
|
||||
}
|
||||
if err != nil {
|
||||
log.Crit("Failed to store extended filter map row", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func WriteFilterMapBaseRows(db ethdb.KeyValueWriter, mapRowIndex uint64, rows [][]uint32, bitLength uint) {
|
||||
byteLength := int(bitLength) / 8
|
||||
if int(bitLength) != byteLength*8 {
|
||||
panic("invalid bit length")
|
||||
}
|
||||
var entryCount, zeroBits int
|
||||
for i, row := range rows {
|
||||
if len(row) > 0 {
|
||||
entryCount += len(row)
|
||||
zeroBits = i
|
||||
}
|
||||
}
|
||||
var err error
|
||||
if entryCount > 0 {
|
||||
headerLen := (zeroBits + entryCount + 7) / 8
|
||||
encRows := make([]byte, headerLen+entryCount*byteLength)
|
||||
nextEntry := headerLen
|
||||
|
||||
headerPtr, headerByte := 0, byte(1)
|
||||
addHeaderBit := func(bit bool) {
|
||||
if bit {
|
||||
encRows[headerPtr] += headerByte
|
||||
}
|
||||
if headerByte += headerByte; headerByte == 0 {
|
||||
headerPtr++
|
||||
headerByte = 1
|
||||
}
|
||||
}
|
||||
|
||||
for _, row := range rows {
|
||||
for _, entry := range row {
|
||||
var b [4]byte
|
||||
binary.LittleEndian.PutUint32(b[:], entry)
|
||||
copy(encRows[nextEntry:nextEntry+byteLength], b[:byteLength])
|
||||
nextEntry += byteLength
|
||||
addHeaderBit(true)
|
||||
}
|
||||
if zeroBits == 0 {
|
||||
break
|
||||
}
|
||||
addHeaderBit(false)
|
||||
zeroBits--
|
||||
}
|
||||
err = db.Put(filterMapRowKey(mapRowIndex, true), encRows)
|
||||
} else {
|
||||
err = db.Delete(filterMapRowKey(mapRowIndex, true))
|
||||
}
|
||||
if err != nil {
|
||||
log.Crit("Failed to store base filter map rows", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteFilterMapRows(db ethdb.KeyValueRangeDeleter, firstMapRowIndex, afterLastMapRowIndex uint64) {
|
||||
if err := db.DeleteRange(filterMapRowKey(firstMapRowIndex, false), filterMapRowKey(afterLastMapRowIndex, false)); err != nil {
|
||||
log.Crit("Failed to delete range of filter map rows", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFilterMapLastBlock retrieves the number of the block that generated the
|
||||
// last log value entry of the given map.
|
||||
func ReadFilterMapLastBlock(db ethdb.KeyValueReader, mapIndex uint32) (uint64, common.Hash, error) {
|
||||
enc, err := db.Get(filterMapLastBlockKey(mapIndex))
|
||||
if err != nil {
|
||||
return 0, common.Hash{}, err
|
||||
}
|
||||
if len(enc) != 40 {
|
||||
return 0, common.Hash{}, errors.New("Invalid block number and id encoding")
|
||||
}
|
||||
var id common.Hash
|
||||
copy(id[:], enc[8:])
|
||||
return binary.BigEndian.Uint64(enc[:8]), id, nil
|
||||
}
|
||||
|
||||
// WriteFilterMapLastBlock stores the number of the block that generated the
|
||||
// last log value entry of the given map.
|
||||
func WriteFilterMapLastBlock(db ethdb.KeyValueWriter, mapIndex uint32, blockNumber uint64, id common.Hash) {
|
||||
var enc [40]byte
|
||||
binary.BigEndian.PutUint64(enc[:8], blockNumber)
|
||||
copy(enc[8:], id[:])
|
||||
if err := db.Put(filterMapLastBlockKey(mapIndex), enc[:]); err != nil {
|
||||
log.Crit("Failed to store filter map last block pointer", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteFilterMapLastBlock deletes the number of the block that generated the
|
||||
// last log value entry of the given map.
|
||||
func DeleteFilterMapLastBlock(db ethdb.KeyValueWriter, mapIndex uint32) {
|
||||
if err := db.Delete(filterMapLastBlockKey(mapIndex)); err != nil {
|
||||
log.Crit("Failed to delete filter map last block pointer", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteFilterMapLastBlocks(db ethdb.KeyValueRangeDeleter, firstMapIndex, afterLastMapIndex uint32) {
|
||||
if err := db.DeleteRange(filterMapLastBlockKey(firstMapIndex), filterMapLastBlockKey(afterLastMapIndex)); err != nil {
|
||||
log.Crit("Failed to delete range of filter map last block pointers", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadBlockLvPointer retrieves the starting log value index where the log values
|
||||
// generated by the given block are located.
|
||||
func ReadBlockLvPointer(db ethdb.KeyValueReader, blockNumber uint64) (uint64, error) {
|
||||
encPtr, err := db.Get(filterMapBlockLVKey(blockNumber))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(encPtr) != 8 {
|
||||
return 0, errors.New("Invalid log value pointer encoding")
|
||||
}
|
||||
return binary.BigEndian.Uint64(encPtr), nil
|
||||
}
|
||||
|
||||
// WriteBlockLvPointer stores the starting log value index where the log values
|
||||
// generated by the given block are located.
|
||||
func WriteBlockLvPointer(db ethdb.KeyValueWriter, blockNumber, lvPointer uint64) {
|
||||
var encPtr [8]byte
|
||||
binary.BigEndian.PutUint64(encPtr[:], lvPointer)
|
||||
if err := db.Put(filterMapBlockLVKey(blockNumber), encPtr[:]); err != nil {
|
||||
log.Crit("Failed to store block log value pointer", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteBlockLvPointer deletes the starting log value index where the log values
|
||||
// generated by the given block are located.
|
||||
func DeleteBlockLvPointer(db ethdb.KeyValueWriter, blockNumber uint64) {
|
||||
if err := db.Delete(filterMapBlockLVKey(blockNumber)); err != nil {
|
||||
log.Crit("Failed to delete block log value pointer", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteBlockLvPointers(db ethdb.KeyValueRangeDeleter, firstBlockNumber, afterLastBlockNumber uint64) {
|
||||
if err := db.DeleteRange(filterMapBlockLVKey(firstBlockNumber), filterMapBlockLVKey(afterLastBlockNumber)); err != nil {
|
||||
log.Crit("Failed to delete range of block log value pointers", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// FilterMapsRange is a storage representation of the block range covered by the
|
||||
// filter maps structure and the corresponting log value index range.
|
||||
type FilterMapsRange struct {
|
||||
HeadBlockIndexed bool
|
||||
HeadBlockDelimiter uint64
|
||||
FirstIndexedBlock, AfterLastIndexedBlock uint64
|
||||
FirstRenderedMap, AfterLastRenderedMap, TailPartialEpoch uint32
|
||||
}
|
||||
|
||||
// ReadFilterMapsRange retrieves the filter maps range data. Note that if the
|
||||
// database entry is not present, that is interpreted as a valid non-initialized
|
||||
// state and returns a blank range structure and no error.
|
||||
func ReadFilterMapsRange(db ethdb.KeyValueReader) (FilterMapsRange, bool, error) {
|
||||
if has, err := db.Has(filterMapsRangeKey); !has || err != nil {
|
||||
return FilterMapsRange{}, false, err
|
||||
}
|
||||
encRange, err := db.Get(filterMapsRangeKey)
|
||||
if err != nil {
|
||||
return FilterMapsRange{}, false, err
|
||||
}
|
||||
var fmRange FilterMapsRange
|
||||
if err := rlp.DecodeBytes(encRange, &fmRange); err != nil {
|
||||
return FilterMapsRange{}, false, err
|
||||
}
|
||||
return fmRange, true, err
|
||||
}
|
||||
|
||||
// WriteFilterMapsRange stores the filter maps range data.
|
||||
func WriteFilterMapsRange(db ethdb.KeyValueWriter, fmRange FilterMapsRange) {
|
||||
encRange, err := rlp.EncodeToBytes(&fmRange)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode filter maps range", "err", err)
|
||||
}
|
||||
if err := db.Put(filterMapsRangeKey, encRange); err != nil {
|
||||
log.Crit("Failed to store filter maps range", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteFilterMapsRange deletes the filter maps range data which is interpreted
|
||||
// as reverting to the un-initialized state.
|
||||
func DeleteFilterMapsRange(db ethdb.KeyValueWriter) {
|
||||
if err := db.Delete(filterMapsRangeKey); err != nil {
|
||||
log.Crit("Failed to delete filter maps range", "err", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ import (
|
|||
// ReadPreimage retrieves a single preimage of the provided hash.
|
||||
func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
|
||||
data, _ := db.Get(preimageKey(hash))
|
||||
if len(data) == 0 {
|
||||
preimageMissCounter.Inc(1)
|
||||
} else {
|
||||
preimageHitsCounter.Inc(1)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +43,6 @@ func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
|
|||
}
|
||||
}
|
||||
preimageCounter.Inc(int64(len(preimages)))
|
||||
preimageHitCounter.Inc(int64(len(preimages)))
|
||||
}
|
||||
|
||||
// ReadCode retrieves the contract code of the provided code hash.
|
||||
|
|
|
|||
|
|
@ -376,6 +376,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
storageSnaps stat
|
||||
preimages stat
|
||||
bloomBits stat
|
||||
filterMaps stat
|
||||
beaconHeaders stat
|
||||
cliqueSnaps stat
|
||||
|
||||
|
|
@ -440,6 +441,8 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
bloomBits.Add(size)
|
||||
case bytes.HasPrefix(key, BloomBitsIndexPrefix):
|
||||
bloomBits.Add(size)
|
||||
case bytes.HasPrefix(key, []byte(FilterMapsPrefix)):
|
||||
filterMaps.Add(size)
|
||||
case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8):
|
||||
beaconHeaders.Add(size)
|
||||
case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength:
|
||||
|
|
@ -505,6 +508,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
|
||||
{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
|
||||
{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
|
||||
{"Key-Value store", "Log search index", filterMaps.Size(), filterMaps.Count()},
|
||||
{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
|
||||
{"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()},
|
||||
{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
|
||||
|
|
|
|||
|
|
@ -145,8 +145,15 @@ var (
|
|||
FixedCommitteeRootKey = []byte("fixedRoot-") // bigEndian64(syncPeriod) -> committee root hash
|
||||
SyncCommitteeKey = []byte("committee-") // bigEndian64(syncPeriod) -> serialized committee
|
||||
|
||||
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
|
||||
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
|
||||
FilterMapsPrefix = "fm-"
|
||||
filterMapsRangeKey = []byte(FilterMapsPrefix + "R")
|
||||
filterMapRowPrefix = []byte(FilterMapsPrefix + "r") // filterMapRowPrefix + mapRowIndex (uint64 big endian) -> filter row
|
||||
filterMapLastBlockPrefix = []byte(FilterMapsPrefix + "b") // filterMapLastBlockPrefix + mapIndex (uint32 big endian) -> block number (uint64 big endian)
|
||||
filterMapBlockLVPrefix = []byte(FilterMapsPrefix + "p") // filterMapBlockLVPrefix + num (uint64 big endian) -> log value pointer (uint64 big endian)
|
||||
|
||||
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
|
||||
preimageHitsCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
|
||||
preimageMissCounter = metrics.NewRegisteredCounter("db/preimage/miss", nil)
|
||||
)
|
||||
|
||||
// LegacyTxLookupEntry is the legacy TxLookupEntry definition with some unnecessary
|
||||
|
|
@ -341,3 +348,34 @@ func IsStorageTrieNode(key []byte) bool {
|
|||
ok, _, _ := ResolveStorageTrieNode(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
// filterMapRowKey = filterMapRowPrefix + mapRowIndex (uint64 big endian)
|
||||
func filterMapRowKey(mapRowIndex uint64, base bool) []byte {
|
||||
extLen := 8
|
||||
if base {
|
||||
extLen = 9
|
||||
}
|
||||
l := len(filterMapRowPrefix)
|
||||
key := make([]byte, l+extLen)
|
||||
copy(key[:l], filterMapRowPrefix)
|
||||
binary.BigEndian.PutUint64(key[l:l+8], mapRowIndex)
|
||||
return key
|
||||
}
|
||||
|
||||
// filterMapLastBlockKey = filterMapLastBlockPrefix + mapIndex (uint32 big endian)
|
||||
func filterMapLastBlockKey(mapIndex uint32) []byte {
|
||||
l := len(filterMapLastBlockPrefix)
|
||||
key := make([]byte, l+4)
|
||||
copy(key[:l], filterMapLastBlockPrefix)
|
||||
binary.BigEndian.PutUint32(key[l:], mapIndex)
|
||||
return key
|
||||
}
|
||||
|
||||
// filterMapBlockLVKey = filterMapBlockLVPrefix + num (uint64 big endian)
|
||||
func filterMapBlockLVKey(number uint64) []byte {
|
||||
l := len(filterMapBlockLVPrefix)
|
||||
key := make([]byte, l+8)
|
||||
copy(key[:l], filterMapBlockLVPrefix)
|
||||
binary.BigEndian.PutUint64(key[l:], number)
|
||||
return key
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package state
|
|||
import "github.com/ethereum/go-ethereum/metrics"
|
||||
|
||||
var (
|
||||
accountReadMeters = metrics.NewRegisteredMeter("state/read/accounts", nil)
|
||||
accountReadMeters = metrics.NewRegisteredMeter("state/read/account", nil)
|
||||
storageReadMeters = metrics.NewRegisteredMeter("state/read/storage", nil)
|
||||
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
|
||||
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
|
||||
|
|
|
|||
|
|
@ -1604,7 +1604,7 @@ func (pool *LegacyPool) demoteUnexecutables() {
|
|||
gapped := list.Cap(0)
|
||||
for _, tx := range gapped {
|
||||
hash := tx.Hash()
|
||||
log.Error("Demoting invalidated transaction", "hash", hash)
|
||||
log.Warn("Demoting invalidated transaction", "hash", hash)
|
||||
|
||||
// Internal shuffle shouldn't touch the lookup set.
|
||||
pool.enqueueTx(hash, tx, false)
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, unsigned char *point,
|
|||
ARG_CHECK(scalar != NULL);
|
||||
(void)ctx;
|
||||
|
||||
secp256k1_fe_set_b32(&feX, point);
|
||||
secp256k1_fe_set_b32(&feY, point+32);
|
||||
secp256k1_fe_set_b32_limit(&feX, point);
|
||||
secp256k1_fe_set_b32_limit(&feY, point+32);
|
||||
secp256k1_ge_set_xy(&ge, &feX, &feY);
|
||||
secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&s)) {
|
||||
|
|
|
|||
101
crypto/secp256k1/libsecp256k1/.cirrus.yml
Normal file
101
crypto/secp256k1/libsecp256k1/.cirrus.yml
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
env:
|
||||
### cirrus config
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
### compiler options
|
||||
HOST:
|
||||
WRAPPER_CMD:
|
||||
# Specific warnings can be disabled with -Wno-error=foo.
|
||||
# -pedantic-errors is not equivalent to -Werror=pedantic and thus not implied by -Werror according to the GCC manual.
|
||||
WERROR_CFLAGS: -Werror -pedantic-errors
|
||||
MAKEFLAGS: -j4
|
||||
BUILD: check
|
||||
### secp256k1 config
|
||||
ECMULTWINDOW: 15
|
||||
ECMULTGENKB: 22
|
||||
ASM: no
|
||||
WIDEMUL: auto
|
||||
WITH_VALGRIND: yes
|
||||
EXTRAFLAGS:
|
||||
### secp256k1 modules
|
||||
EXPERIMENTAL: no
|
||||
ECDH: no
|
||||
RECOVERY: no
|
||||
EXTRAKEYS: no
|
||||
SCHNORRSIG: no
|
||||
MUSIG: no
|
||||
ELLSWIFT: no
|
||||
### test options
|
||||
SECP256K1_TEST_ITERS: 64
|
||||
BENCH: yes
|
||||
SECP256K1_BENCH_ITERS: 2
|
||||
CTIMETESTS: yes
|
||||
# Compile and run the tests
|
||||
EXAMPLES: yes
|
||||
|
||||
cat_logs_snippet: &CAT_LOGS
|
||||
always:
|
||||
cat_tests_log_script:
|
||||
- cat tests.log || true
|
||||
cat_noverify_tests_log_script:
|
||||
- cat noverify_tests.log || true
|
||||
cat_exhaustive_tests_log_script:
|
||||
- cat exhaustive_tests.log || true
|
||||
cat_ctime_tests_log_script:
|
||||
- cat ctime_tests.log || true
|
||||
cat_bench_log_script:
|
||||
- cat bench.log || true
|
||||
cat_config_log_script:
|
||||
- cat config.log || true
|
||||
cat_test_env_script:
|
||||
- cat test_env.log || true
|
||||
cat_ci_env_script:
|
||||
- env
|
||||
|
||||
linux_arm64_container_snippet: &LINUX_ARM64_CONTAINER
|
||||
env_script:
|
||||
- env | tee /tmp/env
|
||||
build_script:
|
||||
- DOCKER_BUILDKIT=1 docker build --file "ci/linux-debian.Dockerfile" --tag="ci_secp256k1_arm"
|
||||
- docker image prune --force # Cleanup stale layers
|
||||
test_script:
|
||||
- docker run --rm --mount "type=bind,src=./,dst=/ci_secp256k1" --env-file /tmp/env --replace --name "ci_secp256k1_arm" "ci_secp256k1_arm" bash -c "cd /ci_secp256k1/ && ./ci/ci.sh"
|
||||
|
||||
task:
|
||||
name: "ARM64: Linux (Debian stable)"
|
||||
persistent_worker:
|
||||
labels:
|
||||
type: arm64
|
||||
env:
|
||||
ECDH: yes
|
||||
RECOVERY: yes
|
||||
EXTRAKEYS: yes
|
||||
SCHNORRSIG: yes
|
||||
MUSIG: yes
|
||||
ELLSWIFT: yes
|
||||
matrix:
|
||||
# Currently only gcc-snapshot, the other compilers are tested on GHA with QEMU
|
||||
- env: { CC: 'gcc-snapshot' }
|
||||
<< : *LINUX_ARM64_CONTAINER
|
||||
<< : *CAT_LOGS
|
||||
|
||||
task:
|
||||
name: "ARM64: Linux (Debian stable), Valgrind"
|
||||
persistent_worker:
|
||||
labels:
|
||||
type: arm64
|
||||
env:
|
||||
ECDH: yes
|
||||
RECOVERY: yes
|
||||
EXTRAKEYS: yes
|
||||
SCHNORRSIG: yes
|
||||
MUSIG: yes
|
||||
ELLSWIFT: yes
|
||||
WRAPPER_CMD: 'valgrind --error-exitcode=42'
|
||||
SECP256K1_TEST_ITERS: 2
|
||||
matrix:
|
||||
- env: { CC: 'gcc' }
|
||||
- env: { CC: 'clang' }
|
||||
- env: { CC: 'gcc-snapshot' }
|
||||
- env: { CC: 'clang-snapshot' }
|
||||
<< : *LINUX_ARM64_CONTAINER
|
||||
<< : *CAT_LOGS
|
||||
2
crypto/secp256k1/libsecp256k1/.gitattributes
vendored
Normal file
2
crypto/secp256k1/libsecp256k1/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
src/precomputed_ecmult.c linguist-generated
|
||||
src/precomputed_ecmult_gen.c linguist-generated
|
||||
33
crypto/secp256k1/libsecp256k1/.github/actions/install-homebrew-valgrind/action.yml
vendored
Normal file
33
crypto/secp256k1/libsecp256k1/.github/actions/install-homebrew-valgrind/action.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
name: "Install Valgrind"
|
||||
description: "Install Homebrew's Valgrind package and cache it."
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: |
|
||||
brew tap LouisBrunner/valgrind
|
||||
brew fetch --HEAD LouisBrunner/valgrind/valgrind
|
||||
echo "CI_HOMEBREW_CELLAR_VALGRIND=$(brew --cellar valgrind)" >> "$GITHUB_ENV"
|
||||
shell: bash
|
||||
|
||||
- run: |
|
||||
sw_vers > valgrind_fingerprint
|
||||
brew --version >> valgrind_fingerprint
|
||||
git -C "$(brew --cache)/valgrind--git" rev-parse HEAD >> valgrind_fingerprint
|
||||
cat valgrind_fingerprint
|
||||
shell: bash
|
||||
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
path: ${{ env.CI_HOMEBREW_CELLAR_VALGRIND }}
|
||||
key: ${{ github.job }}-valgrind-${{ hashFiles('valgrind_fingerprint') }}
|
||||
|
||||
- if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
brew install --HEAD LouisBrunner/valgrind/valgrind
|
||||
shell: bash
|
||||
|
||||
- if: steps.cache.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
brew link valgrind
|
||||
shell: bash
|
||||
54
crypto/secp256k1/libsecp256k1/.github/actions/run-in-docker-action/action.yml
vendored
Normal file
54
crypto/secp256k1/libsecp256k1/.github/actions/run-in-docker-action/action.yml
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
name: 'Run in Docker with environment'
|
||||
description: 'Run a command in a Docker container, while passing explicitly set environment variables into the container.'
|
||||
inputs:
|
||||
dockerfile:
|
||||
description: 'A Dockerfile that defines an image'
|
||||
required: true
|
||||
tag:
|
||||
description: 'A tag of an image'
|
||||
required: true
|
||||
command:
|
||||
description: 'A command to run in a container'
|
||||
required: false
|
||||
default: ./ci/ci.sh
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- uses: docker/build-push-action@v5
|
||||
id: main_builder
|
||||
continue-on-error: true
|
||||
with:
|
||||
context: .
|
||||
file: ${{ inputs.dockerfile }}
|
||||
tags: ${{ inputs.tag }}
|
||||
load: true
|
||||
cache-from: type=gha
|
||||
|
||||
- uses: docker/build-push-action@v5
|
||||
id: retry_builder
|
||||
if: steps.main_builder.outcome == 'failure'
|
||||
with:
|
||||
context: .
|
||||
file: ${{ inputs.dockerfile }}
|
||||
tags: ${{ inputs.tag }}
|
||||
load: true
|
||||
cache-from: type=gha
|
||||
|
||||
- # Workaround for https://github.com/google/sanitizers/issues/1614 .
|
||||
# The underlying issue has been fixed in clang 18.1.3.
|
||||
run: sudo sysctl -w vm.mmap_rnd_bits=28
|
||||
shell: bash
|
||||
|
||||
- # Tell Docker to pass environment variables in `env` into the container.
|
||||
run: >
|
||||
docker run \
|
||||
$(echo '${{ toJSON(env) }}' | jq -r 'keys[] | "--env \(.) "') \
|
||||
--volume ${{ github.workspace }}:${{ github.workspace }} \
|
||||
--workdir ${{ github.workspace }} \
|
||||
${{ inputs.tag }} bash -c "
|
||||
git config --global --add safe.directory ${{ github.workspace }}
|
||||
${{ inputs.command }}
|
||||
"
|
||||
shell: bash
|
||||
890
crypto/secp256k1/libsecp256k1/.github/workflows/ci.yml
vendored
Normal file
890
crypto/secp256k1/libsecp256k1/.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,890 @@
|
|||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.event_name != 'pull_request' && github.run_id || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
### compiler options
|
||||
HOST:
|
||||
WRAPPER_CMD:
|
||||
# Specific warnings can be disabled with -Wno-error=foo.
|
||||
# -pedantic-errors is not equivalent to -Werror=pedantic and thus not implied by -Werror according to the GCC manual.
|
||||
WERROR_CFLAGS: '-Werror -pedantic-errors'
|
||||
MAKEFLAGS: '-j4'
|
||||
BUILD: 'check'
|
||||
### secp256k1 config
|
||||
ECMULTWINDOW: 15
|
||||
ECMULTGENKB: 86
|
||||
ASM: 'no'
|
||||
WIDEMUL: 'auto'
|
||||
WITH_VALGRIND: 'yes'
|
||||
EXTRAFLAGS:
|
||||
### secp256k1 modules
|
||||
EXPERIMENTAL: 'no'
|
||||
ECDH: 'no'
|
||||
RECOVERY: 'no'
|
||||
EXTRAKEYS: 'no'
|
||||
SCHNORRSIG: 'no'
|
||||
MUSIG: 'no'
|
||||
ELLSWIFT: 'no'
|
||||
### test options
|
||||
SECP256K1_TEST_ITERS: 64
|
||||
BENCH: 'yes'
|
||||
SECP256K1_BENCH_ITERS: 2
|
||||
CTIMETESTS: 'yes'
|
||||
# Compile and run the examples.
|
||||
EXAMPLES: 'yes'
|
||||
|
||||
jobs:
|
||||
docker_cache:
|
||||
name: "Build Docker image"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
# See: https://github.com/moby/buildkit/issues/3969.
|
||||
driver-opts: |
|
||||
network=host
|
||||
|
||||
- name: Build container
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
file: ./ci/linux-debian.Dockerfile
|
||||
tags: linux-debian-image
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=min
|
||||
|
||||
linux_debian:
|
||||
name: "x86_64: Linux (Debian stable)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int128' }
|
||||
- env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes' }
|
||||
- env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' }
|
||||
- env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes' }
|
||||
- env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', CPPFLAGS: '-DVERIFY' }
|
||||
- env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' }
|
||||
- env_vars: { CPPFLAGS: '-DDETERMINISTIC' }
|
||||
- env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' }
|
||||
- env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- env_vars: { ECMULTGENKB: 2, ECMULTWINDOW: 2 }
|
||||
- env_vars: { ECMULTGENKB: 86, ECMULTWINDOW: 4 }
|
||||
cc:
|
||||
- 'gcc'
|
||||
- 'clang'
|
||||
- 'gcc-snapshot'
|
||||
- 'clang-snapshot'
|
||||
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.configuration.env_vars }}
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
i686_debian:
|
||||
name: "i686: Linux (Debian stable)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cc:
|
||||
- 'i686-linux-gnu-gcc'
|
||||
- 'clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include'
|
||||
|
||||
env:
|
||||
HOST: 'i686-linux-gnu'
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CC: ${{ matrix.cc }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
s390x_debian:
|
||||
name: "s390x (big-endian): Linux (Debian stable, QEMU)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
env:
|
||||
WRAPPER_CMD: 'qemu-s390x'
|
||||
SECP256K1_TEST_ITERS: 16
|
||||
HOST: 's390x-linux-gnu'
|
||||
WITH_VALGRIND: 'no'
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CTIMETESTS: 'no'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
arm32_debian:
|
||||
name: "ARM32: Linux (Debian stable, QEMU)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- env_vars: {}
|
||||
- env_vars: { EXPERIMENTAL: 'yes', ASM: 'arm32' }
|
||||
|
||||
env:
|
||||
WRAPPER_CMD: 'qemu-arm'
|
||||
SECP256K1_TEST_ITERS: 16
|
||||
HOST: 'arm-linux-gnueabihf'
|
||||
WITH_VALGRIND: 'no'
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CTIMETESTS: 'no'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.configuration.env_vars }}
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
arm64_debian:
|
||||
name: "ARM64: Linux (Debian stable, QEMU)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
env:
|
||||
WRAPPER_CMD: 'qemu-aarch64'
|
||||
SECP256K1_TEST_ITERS: 16
|
||||
HOST: 'aarch64-linux-gnu'
|
||||
WITH_VALGRIND: 'no'
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CTIMETESTS: 'no'
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- env_vars: { } # gcc
|
||||
- env_vars: # clang
|
||||
CC: 'clang --target=aarch64-linux-gnu'
|
||||
- env_vars: # clang-snapshot
|
||||
CC: 'clang-snapshot --target=aarch64-linux-gnu'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.configuration.env_vars }}
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
ppc64le_debian:
|
||||
name: "ppc64le: Linux (Debian stable, QEMU)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
env:
|
||||
WRAPPER_CMD: 'qemu-ppc64le'
|
||||
SECP256K1_TEST_ITERS: 16
|
||||
HOST: 'powerpc64le-linux-gnu'
|
||||
WITH_VALGRIND: 'no'
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CTIMETESTS: 'no'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
valgrind_debian:
|
||||
name: "Valgrind (memcheck)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- env_vars: { CC: 'clang', ASM: 'auto' }
|
||||
- env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'auto' }
|
||||
- env_vars: { CC: 'clang', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 }
|
||||
- env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 }
|
||||
|
||||
env:
|
||||
# The `--error-exitcode` is required to make the test fail if valgrind found errors,
|
||||
# otherwise it will return 0 (https://www.valgrind.org/docs/manual/manual-core.html).
|
||||
WRAPPER_CMD: 'valgrind --error-exitcode=42'
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CTIMETESTS: 'no'
|
||||
SECP256K1_TEST_ITERS: 2
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.configuration.env_vars }}
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
sanitizers_debian:
|
||||
name: "UBSan, ASan, LSan"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- env_vars: { CC: 'clang', ASM: 'auto' }
|
||||
- env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'auto' }
|
||||
- env_vars: { CC: 'clang', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 }
|
||||
- env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 }
|
||||
|
||||
env:
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CTIMETESTS: 'no'
|
||||
CFLAGS: '-fsanitize=undefined,address -g'
|
||||
UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1'
|
||||
ASAN_OPTIONS: 'strict_string_checks=1:detect_stack_use_after_return=1:detect_leaks=1'
|
||||
LSAN_OPTIONS: 'use_unaligned=1'
|
||||
SECP256K1_TEST_ITERS: 32
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.configuration.env_vars }}
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
msan_debian:
|
||||
name: "MSan"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- env_vars:
|
||||
CTIMETESTS: 'yes'
|
||||
CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -g'
|
||||
- env_vars:
|
||||
ECMULTGENKB: 2
|
||||
ECMULTWINDOW: 2
|
||||
CTIMETESTS: 'yes'
|
||||
CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -g -O3'
|
||||
- env_vars:
|
||||
# -fsanitize-memory-param-retval is clang's default, but our build system disables it
|
||||
# when ctime_tests when enabled.
|
||||
CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -fsanitize-memory-param-retval -g'
|
||||
CTIMETESTS: 'no'
|
||||
|
||||
env:
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CC: 'clang'
|
||||
SECP256K1_TEST_ITERS: 32
|
||||
ASM: 'no'
|
||||
WITH_VALGRIND: 'no'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.configuration.env_vars }}
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
mingw_debian:
|
||||
name: ${{ matrix.configuration.job_name }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
env:
|
||||
WRAPPER_CMD: 'wine'
|
||||
WITH_VALGRIND: 'no'
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
CTIMETESTS: 'no'
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- job_name: 'x86_64 (mingw32-w64): Windows (Debian stable, Wine)'
|
||||
env_vars:
|
||||
HOST: 'x86_64-w64-mingw32'
|
||||
- job_name: 'i686 (mingw32-w64): Windows (Debian stable, Wine)'
|
||||
env_vars:
|
||||
HOST: 'i686-w64-mingw32'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.configuration.env_vars }}
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
x86_64-macos-native:
|
||||
name: "x86_64: macOS Ventura, Valgrind"
|
||||
# See: https://github.com/actions/runner-images#available-images.
|
||||
runs-on: macos-13
|
||||
|
||||
env:
|
||||
CC: 'clang'
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
env_vars:
|
||||
- { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 }
|
||||
- { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', MUSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' }
|
||||
- BUILD: 'distcheck'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Homebrew packages
|
||||
run: |
|
||||
brew install --quiet automake libtool gcc
|
||||
ln -s $(brew --prefix gcc)/bin/gcc-?? /usr/local/bin/gcc
|
||||
|
||||
- name: Install and cache Valgrind
|
||||
uses: ./.github/actions/install-homebrew-valgrind
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.env_vars }}
|
||||
run: ./ci/ci.sh
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
arm64-macos-native:
|
||||
name: "ARM64: macOS Sonoma"
|
||||
# See: https://github.com/actions/runner-images#available-images.
|
||||
runs-on: macos-14
|
||||
|
||||
env:
|
||||
CC: 'clang'
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||
WITH_VALGRIND: 'no'
|
||||
CTIMETESTS: 'no'
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
env_vars:
|
||||
- { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- { WIDEMUL: 'int128_struct', ECMULTGENPRECISION: 2, ECMULTWINDOW: 4 }
|
||||
- { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' }
|
||||
- { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY' }
|
||||
- BUILD: 'distcheck'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Homebrew packages
|
||||
run: |
|
||||
brew install --quiet automake libtool gcc
|
||||
ln -s $(brew --prefix gcc)/bin/gcc-?? /usr/local/bin/gcc
|
||||
|
||||
- name: CI script
|
||||
env: ${{ matrix.env_vars }}
|
||||
run: ./ci/ci.sh
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
win64-native:
|
||||
name: ${{ matrix.configuration.job_name }}
|
||||
# See: https://github.com/actions/runner-images#available-images.
|
||||
runs-on: windows-2022
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
configuration:
|
||||
- job_name: 'x64 (MSVC): Windows (VS 2022, shared)'
|
||||
cmake_options: '-A x64 -DBUILD_SHARED_LIBS=ON'
|
||||
- job_name: 'x64 (MSVC): Windows (VS 2022, static)'
|
||||
cmake_options: '-A x64 -DBUILD_SHARED_LIBS=OFF'
|
||||
- job_name: 'x64 (MSVC): Windows (VS 2022, int128_struct)'
|
||||
cmake_options: '-A x64 -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct'
|
||||
- job_name: 'x64 (MSVC): Windows (VS 2022, int128_struct with __(u)mulh)'
|
||||
cmake_options: '-A x64 -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct'
|
||||
cpp_flags: '/DSECP256K1_MSVC_MULH_TEST_OVERRIDE'
|
||||
- job_name: 'x86 (MSVC): Windows (VS 2022)'
|
||||
cmake_options: '-A Win32'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Generate buildsystem
|
||||
run: cmake -E env CFLAGS="/WX ${{ matrix.configuration.cpp_flags }}" cmake -B build -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DSECP256K1_BUILD_EXAMPLES=ON ${{ matrix.configuration.cmake_options }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build --config RelWithDebInfo -- /p:UseMultiToolTask=true /maxCpuCount
|
||||
|
||||
- name: Binaries info
|
||||
# Use the bash shell included with Git for Windows.
|
||||
shell: bash
|
||||
run: |
|
||||
cd build/bin/RelWithDebInfo && file *tests.exe bench*.exe libsecp256k1-*.dll || true
|
||||
|
||||
- name: Check
|
||||
run: |
|
||||
ctest -C RelWithDebInfo --test-dir build -j ([int]$env:NUMBER_OF_PROCESSORS + 1)
|
||||
build\bin\RelWithDebInfo\bench_ecmult.exe
|
||||
build\bin\RelWithDebInfo\bench_internal.exe
|
||||
build\bin\RelWithDebInfo\bench.exe
|
||||
|
||||
win64-native-headers:
|
||||
name: "x64 (MSVC): C++ (public headers)"
|
||||
# See: https://github.com/actions/runner-images#available-images.
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Add cl.exe to PATH
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: C++ (public headers)
|
||||
run: |
|
||||
cl.exe -c -WX -TP include/*.h
|
||||
|
||||
cxx_fpermissive_debian:
|
||||
name: "C++ -fpermissive (entire project)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
env:
|
||||
CC: 'g++'
|
||||
CFLAGS: '-fpermissive -g'
|
||||
CPPFLAGS: '-DSECP256K1_CPLUSPLUS_TEST_OVERRIDE'
|
||||
WERROR_CFLAGS:
|
||||
ECDH: 'yes'
|
||||
RECOVERY: 'yes'
|
||||
EXTRAKEYS: 'yes'
|
||||
SCHNORRSIG: 'yes'
|
||||
MUSIG: 'yes'
|
||||
ELLSWIFT: 'yes'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
|
||||
- run: cat tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat noverify_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat exhaustive_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat ctime_tests.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat bench.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat config.log || true
|
||||
if: ${{ always() }}
|
||||
- run: cat test_env.log || true
|
||||
if: ${{ always() }}
|
||||
- name: CI env
|
||||
run: env
|
||||
if: ${{ always() }}
|
||||
|
||||
cxx_headers_debian:
|
||||
name: "C++ (public headers)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_cache
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
uses: ./.github/actions/run-in-docker-action
|
||||
with:
|
||||
dockerfile: ./ci/linux-debian.Dockerfile
|
||||
tag: linux-debian-image
|
||||
command: |
|
||||
g++ -Werror include/*.h
|
||||
clang -Werror -x c++-header include/*.h
|
||||
|
||||
sage:
|
||||
name: "SageMath prover"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: sagemath/sagemath:latest
|
||||
options: --user root
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CI script
|
||||
run: |
|
||||
cd sage
|
||||
sage prove_group_implementations.sage
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- run: ./autogen.sh && ./configure --enable-dev-mode && make distcheck
|
||||
|
||||
- name: Check installation with Autotools
|
||||
env:
|
||||
CI_INSTALL: ${{ runner.temp }}/${{ github.run_id }}${{ github.action }}/install
|
||||
run: |
|
||||
./autogen.sh && ./configure --prefix=${{ env.CI_INSTALL }} && make clean && make install && ls -RlAh ${{ env.CI_INSTALL }}
|
||||
gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=${{ env.CI_INSTALL }}/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"${{ env.CI_INSTALL }}/lib" && ./ecdsa
|
||||
|
||||
- name: Check installation with CMake
|
||||
env:
|
||||
CI_BUILD: ${{ runner.temp }}/${{ github.run_id }}${{ github.action }}/build
|
||||
CI_INSTALL: ${{ runner.temp }}/${{ github.run_id }}${{ github.action }}/install
|
||||
run: |
|
||||
cmake -B ${{ env.CI_BUILD }} -DCMAKE_INSTALL_PREFIX=${{ env.CI_INSTALL }} && cmake --build ${{ env.CI_BUILD }} && cmake --install ${{ env.CI_BUILD }} && ls -RlAh ${{ env.CI_INSTALL }}
|
||||
gcc -o ecdsa examples/ecdsa.c -I ${{ env.CI_INSTALL }}/include -L ${{ env.CI_INSTALL }}/lib*/ -l secp256k1 -Wl,-rpath,"${{ env.CI_INSTALL }}/lib",-rpath,"${{ env.CI_INSTALL }}/lib64" && ./ecdsa
|
||||
42
crypto/secp256k1/libsecp256k1/.gitignore
vendored
42
crypto/secp256k1/libsecp256k1/.gitignore
vendored
|
|
@ -1,17 +1,24 @@
|
|||
bench_inv
|
||||
bench_ecdh
|
||||
bench_sign
|
||||
bench_verify
|
||||
bench_schnorr_verify
|
||||
bench_recover
|
||||
bench
|
||||
bench_ecmult
|
||||
bench_internal
|
||||
noverify_tests
|
||||
tests
|
||||
exhaustive_tests
|
||||
gen_context
|
||||
precompute_ecmult_gen
|
||||
precompute_ecmult
|
||||
ctime_tests
|
||||
ecdh_example
|
||||
ecdsa_example
|
||||
schnorr_example
|
||||
ellswift_example
|
||||
musig_example
|
||||
*.exe
|
||||
*.so
|
||||
*.a
|
||||
!.gitignore
|
||||
*.csv
|
||||
*.log
|
||||
*.trs
|
||||
*.sage.py
|
||||
|
||||
Makefile
|
||||
configure
|
||||
|
|
@ -21,6 +28,7 @@ aclocal.m4
|
|||
autom4te.cache/
|
||||
config.log
|
||||
config.status
|
||||
conftest*
|
||||
*.tar.gz
|
||||
*.la
|
||||
libtool
|
||||
|
|
@ -29,9 +37,15 @@ libtool
|
|||
*.lo
|
||||
*.o
|
||||
*~
|
||||
src/libsecp256k1-config.h
|
||||
src/libsecp256k1-config.h.in
|
||||
src/ecmult_static_context.h
|
||||
|
||||
coverage/
|
||||
coverage.html
|
||||
coverage.*.html
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
|
||||
build-aux/ar-lib
|
||||
build-aux/config.guess
|
||||
build-aux/config.sub
|
||||
build-aux/depcomp
|
||||
|
|
@ -45,5 +59,9 @@ build-aux/m4/ltversion.m4
|
|||
build-aux/missing
|
||||
build-aux/compile
|
||||
build-aux/test-driver
|
||||
src/stamp-h1
|
||||
libsecp256k1.pc
|
||||
|
||||
### CMake
|
||||
/CMakeUserPresets.json
|
||||
# Default CMake build directory.
|
||||
/build
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
language: c
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages: libgmp-dev
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
cache:
|
||||
directories:
|
||||
- src/java/guava/
|
||||
env:
|
||||
global:
|
||||
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no
|
||||
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
|
||||
matrix:
|
||||
- SCALAR=32bit RECOVERY=yes
|
||||
- SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
|
||||
- SCALAR=64bit
|
||||
- FIELD=64bit RECOVERY=yes
|
||||
- FIELD=64bit ENDOMORPHISM=yes
|
||||
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
|
||||
- FIELD=64bit ASM=x86_64
|
||||
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
|
||||
- FIELD=32bit ENDOMORPHISM=yes
|
||||
- BIGNUM=no
|
||||
- BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
|
||||
- BIGNUM=no STATICPRECOMPUTATION=no
|
||||
- BUILD=distcheck
|
||||
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
|
||||
- EXTRAFLAGS=CFLAGS=-O0
|
||||
- BUILD=check-java ECDH=yes EXPERIMENTAL=yes
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- compiler: clang
|
||||
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- libgmp-dev:i386
|
||||
- compiler: clang
|
||||
env: HOST=i686-linux-gnu
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- compiler: gcc
|
||||
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- compiler: gcc
|
||||
env: HOST=i686-linux-gnu
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- libgmp-dev:i386
|
||||
before_install: mkdir -p `dirname $GUAVA_JAR`
|
||||
install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi
|
||||
before_script: ./autogen.sh
|
||||
script:
|
||||
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
|
||||
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
|
||||
- ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
|
||||
os: linux
|
||||
177
crypto/secp256k1/libsecp256k1/CHANGELOG.md
Normal file
177
crypto/secp256k1/libsecp256k1/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.6.0] - 2024-11-04
|
||||
|
||||
#### Added
|
||||
- New module `musig` implements the MuSig2 multisignature scheme according to the [BIP 327 specification](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). See:
|
||||
- Header file `include/secp256k1_musig.h` which defines the new API.
|
||||
- Document `doc/musig.md` for further notes on API usage.
|
||||
- Usage example `examples/musig.c`.
|
||||
- New CMake variable `SECP256K1_APPEND_LDFLAGS` for appending linker flags to the build command.
|
||||
|
||||
#### Changed
|
||||
- API functions now use a significantly more robust method to clear secrets from the stack before returning. However, secret clearing remains a best-effort security measure and cannot guarantee complete removal.
|
||||
- Any type `secp256k1_foo` can now be forward-declared using `typedef struct secp256k1_foo secp256k1_foo;` (or also `struct secp256k1_foo;` in C++).
|
||||
- Organized CMake build artifacts into dedicated directories (`bin/` for executables, `lib/` for libraries) to improve build output structure and Windows shared library compatibility.
|
||||
|
||||
#### Removed
|
||||
- Removed the `secp256k1_scratch_space` struct and its associated functions `secp256k1_scratch_space_create` and `secp256k1_scratch_space_destroy` because the scratch space was unused in the API.
|
||||
|
||||
#### ABI Compatibility
|
||||
The symbols `secp256k1_scratch_space_create` and `secp256k1_scratch_space_destroy` were removed.
|
||||
Otherwise, the library maintains backward compatibility with versions 0.3.x through 0.5.x.
|
||||
|
||||
## [0.5.1] - 2024-08-01
|
||||
|
||||
#### Added
|
||||
- Added usage example for an ElligatorSwift key exchange.
|
||||
|
||||
#### Changed
|
||||
- The default size of the precomputed table for signing was changed from 22 KiB to 86 KiB. The size can be changed with the configure option `--ecmult-gen-kb` (`SECP256K1_ECMULT_GEN_KB` for CMake).
|
||||
- "auto" is no longer an accepted value for the `--with-ecmult-window` and `--with-ecmult-gen-kb` configure options (this also applies to `SECP256K1_ECMULT_WINDOW_SIZE` and `SECP256K1_ECMULT_GEN_KB` in CMake). To achieve the same configuration as previously provided by the "auto" value, omit setting the configure option explicitly.
|
||||
|
||||
#### Fixed
|
||||
- Fixed compilation when the extrakeys module is disabled.
|
||||
|
||||
#### ABI Compatibility
|
||||
The ABI is backward compatible with versions 0.5.0, 0.4.x and 0.3.x.
|
||||
|
||||
## [0.5.0] - 2024-05-06
|
||||
|
||||
#### Added
|
||||
- New function `secp256k1_ec_pubkey_sort` that sorts public keys using lexicographic (of compressed serialization) order.
|
||||
|
||||
#### Changed
|
||||
- The implementation of the point multiplication algorithm used for signing and public key generation was changed, resulting in improved performance for those operations.
|
||||
- The related configure option `--ecmult-gen-precision` was replaced with `--ecmult-gen-kb` (`SECP256K1_ECMULT_GEN_KB` for CMake).
|
||||
- This changes the supported precomputed table sizes for these operations. The new supported sizes are 2 KiB, 22 KiB, or 86 KiB (while the old supported sizes were 32 KiB, 64 KiB, or 512 KiB).
|
||||
|
||||
#### ABI Compatibility
|
||||
The ABI is backward compatible with versions 0.4.x and 0.3.x.
|
||||
|
||||
## [0.4.1] - 2023-12-21
|
||||
|
||||
#### Changed
|
||||
- The point multiplication algorithm used for ECDH operations (module `ecdh`) was replaced with a slightly faster one.
|
||||
- Optional handwritten x86_64 assembly for field operations was removed because modern C compilers are able to output more efficient assembly. This change results in a significant speedup of some library functions when handwritten x86_64 assembly is enabled (`--with-asm=x86_64` in GNU Autotools, `-DSECP256K1_ASM=x86_64` in CMake), which is the default on x86_64. Benchmarks with GCC 10.5.0 show a 10% speedup for `secp256k1_ecdsa_verify` and `secp256k1_schnorrsig_verify`.
|
||||
|
||||
#### ABI Compatibility
|
||||
The ABI is backward compatible with versions 0.4.0 and 0.3.x.
|
||||
|
||||
## [0.4.0] - 2023-09-04
|
||||
|
||||
#### Added
|
||||
- New module `ellswift` implements ElligatorSwift encoding for public keys and x-only Diffie-Hellman key exchange for them.
|
||||
ElligatorSwift permits representing secp256k1 public keys as 64-byte arrays which cannot be distinguished from uniformly random. See:
|
||||
- Header file `include/secp256k1_ellswift.h` which defines the new API.
|
||||
- Document `doc/ellswift.md` which explains the mathematical background of the scheme.
|
||||
- The [paper](https://eprint.iacr.org/2022/759) on which the scheme is based.
|
||||
- We now test the library with unreleased development snapshots of GCC and Clang. This gives us an early chance to catch miscompilations and constant-time issues introduced by the compiler (such as those that led to the previous two releases).
|
||||
|
||||
#### Fixed
|
||||
- Fixed symbol visibility in Windows DLL builds, where three internal library symbols were wrongly exported.
|
||||
|
||||
#### Changed
|
||||
- When consuming libsecp256k1 as a static library on Windows, the user must now define the `SECP256K1_STATIC` macro before including `secp256k1.h`.
|
||||
|
||||
#### ABI Compatibility
|
||||
This release is backward compatible with the ABI of 0.3.0, 0.3.1, and 0.3.2. Symbol visibility is now believed to be handled properly on supported platforms and is now considered to be part of the ABI. Please report any improperly exported symbols as a bug.
|
||||
|
||||
## [0.3.2] - 2023-05-13
|
||||
We strongly recommend updating to 0.3.2 if you use or plan to use GCC >=13 to compile libsecp256k1. When in doubt, check the GCC version using `gcc -v`.
|
||||
|
||||
#### Security
|
||||
- Module `ecdh`: Fix "constant-timeness" issue with GCC 13.1 (and potentially future versions of GCC) that could leave applications using libsecp256k1's ECDH module vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow during ECDH computations when libsecp256k1 is compiled with GCC 13.1.
|
||||
|
||||
#### Fixed
|
||||
- Fixed an old bug that permitted compilers to potentially output bad assembly code on x86_64. In theory, it could lead to a crash or a read of unrelated memory, but this has never been observed on any compilers so far.
|
||||
|
||||
#### Changed
|
||||
- Various improvements and changes to CMake builds. CMake builds remain experimental.
|
||||
- Made API versioning consistent with GNU Autotools builds.
|
||||
- Switched to `BUILD_SHARED_LIBS` variable for controlling whether to build a static or a shared library.
|
||||
- Added `SECP256K1_INSTALL` variable for the controlling whether to install the build artefacts.
|
||||
- Renamed asm build option `arm` to `arm32`. Use `--with-asm=arm32` instead of `--with-asm=arm` (GNU Autotools), and `-DSECP256K1_ASM=arm32` instead of `-DSECP256K1_ASM=arm` (CMake).
|
||||
|
||||
#### ABI Compatibility
|
||||
The ABI is compatible with versions 0.3.0 and 0.3.1.
|
||||
|
||||
## [0.3.1] - 2023-04-10
|
||||
We strongly recommend updating to 0.3.1 if you use or plan to use Clang >=14 to compile libsecp256k1, e.g., Xcode >=14 on macOS has Clang >=14. When in doubt, check the Clang version using `clang -v`.
|
||||
|
||||
#### Security
|
||||
- Fix "constant-timeness" issue with Clang >=14 that could leave applications using libsecp256k1 vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow and secret-dependent memory accesses in conditional moves of memory objects when libsecp256k1 is compiled with Clang >=14.
|
||||
|
||||
#### Added
|
||||
- Added tests against [Project Wycheproof's](https://github.com/google/wycheproof/) set of ECDSA test vectors (Bitcoin "low-S" variant), a fixed set of test cases designed to trigger various edge cases.
|
||||
|
||||
#### Changed
|
||||
- Increased minimum required CMake version to 3.13. CMake builds remain experimental.
|
||||
|
||||
#### ABI Compatibility
|
||||
The ABI is compatible with version 0.3.0.
|
||||
|
||||
## [0.3.0] - 2023-03-08
|
||||
|
||||
#### Added
|
||||
- Added experimental support for CMake builds. Traditional GNU Autotools builds (`./configure` and `make`) remain fully supported.
|
||||
- Usage examples: Added a recommended method for securely clearing sensitive data, e.g., secret keys, from memory.
|
||||
- Tests: Added a new test binary `noverify_tests`. This binary runs the tests without some additional checks present in the ordinary `tests` binary and is thereby closer to production binaries. The `noverify_tests` binary is automatically run as part of the `make check` target.
|
||||
|
||||
#### Fixed
|
||||
- Fixed declarations of API variables for MSVC (`__declspec(dllimport)`). This fixes MSVC builds of programs which link against a libsecp256k1 DLL dynamically and use API variables (and not only API functions). Unfortunately, the MSVC linker now will emit warning `LNK4217` when trying to link against libsecp256k1 statically. Pass `/ignore:4217` to the linker to suppress this warning.
|
||||
|
||||
#### Changed
|
||||
- Forbade cloning or destroying `secp256k1_context_static`. Create a new context instead of cloning the static context. (If this change breaks your code, your code is probably wrong.)
|
||||
- Forbade randomizing (copies of) `secp256k1_context_static`. Randomizing a copy of `secp256k1_context_static` did not have any effect and did not provide defense-in-depth protection against side-channel attacks. Create a new context if you want to benefit from randomization.
|
||||
|
||||
#### Removed
|
||||
- Removed the configuration header `src/libsecp256k1-config.h`. We recommend passing flags to `./configure` or `cmake` to set configuration options (see `./configure --help` or `cmake -LH`). If you cannot or do not want to use one of the supported build systems, pass configuration flags such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG` manually to the compiler (see the file `configure.ac` for supported flags).
|
||||
|
||||
#### ABI Compatibility
|
||||
Due to changes in the API regarding `secp256k1_context_static` described above, the ABI is *not* compatible with previous versions.
|
||||
|
||||
## [0.2.0] - 2022-12-12
|
||||
|
||||
#### Added
|
||||
- Added usage examples for common use cases in a new `examples/` directory.
|
||||
- Added `secp256k1_selftest`, to be used in conjunction with `secp256k1_context_static`.
|
||||
- Added support for 128-bit wide multiplication on MSVC for x86_64 and arm64, giving roughly a 20% speedup on those platforms.
|
||||
|
||||
#### Changed
|
||||
- Enabled modules `schnorrsig`, `extrakeys` and `ecdh` by default in `./configure`.
|
||||
- The `secp256k1_nonce_function_rfc6979` nonce function, used by default by `secp256k1_ecdsa_sign`, now reduces the message hash modulo the group order to match the specification. This only affects improper use of ECDSA signing API.
|
||||
|
||||
#### Deprecated
|
||||
- Deprecated context flags `SECP256K1_CONTEXT_VERIFY` and `SECP256K1_CONTEXT_SIGN`. Use `SECP256K1_CONTEXT_NONE` instead.
|
||||
- Renamed `secp256k1_context_no_precomp` to `secp256k1_context_static`.
|
||||
- Module `schnorrsig`: renamed `secp256k1_schnorrsig_sign` to `secp256k1_schnorrsig_sign32`.
|
||||
|
||||
#### ABI Compatibility
|
||||
Since this is the first release, we do not compare application binary interfaces.
|
||||
However, there are earlier unreleased versions of libsecp256k1 that are *not* ABI compatible with this version.
|
||||
|
||||
## [0.1.0] - 2013-03-05 to 2021-12-25
|
||||
|
||||
This version was in fact never released.
|
||||
The number was given by the build system since the introduction of autotools in Jan 2014 (ea0fe5a5bf0c04f9cc955b2966b614f5f378c6f6).
|
||||
Therefore, this version number does not uniquely identify a set of source files.
|
||||
|
||||
[unreleased]: https://github.com/bitcoin-core/secp256k1/compare/v0.6.0...HEAD
|
||||
[0.6.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.5.1...v0.6.0
|
||||
[0.5.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.5.0...v0.5.1
|
||||
[0.5.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.4.1...v0.5.0
|
||||
[0.4.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.4.0...v0.4.1
|
||||
[0.4.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.2...v0.4.0
|
||||
[0.3.2]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.1...v0.3.2
|
||||
[0.3.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.2.0...v0.3.0
|
||||
[0.2.0]: https://github.com/bitcoin-core/secp256k1/compare/423b6d19d373f1224fd671a982584d7e7900bc93..v0.2.0
|
||||
[0.1.0]: https://github.com/bitcoin-core/secp256k1/commit/423b6d19d373f1224fd671a982584d7e7900bc93
|
||||
405
crypto/secp256k1/libsecp256k1/CMakeLists.txt
Normal file
405
crypto/secp256k1/libsecp256k1/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
#=============================
|
||||
# Project / Package metadata
|
||||
#=============================
|
||||
project(libsecp256k1
|
||||
# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of
|
||||
# the API. All changes in experimental modules are treated as
|
||||
# backwards-compatible and therefore at most increase the minor version.
|
||||
VERSION 0.6.1
|
||||
DESCRIPTION "Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1."
|
||||
HOMEPAGE_URL "https://github.com/bitcoin-core/secp256k1"
|
||||
LANGUAGES C
|
||||
)
|
||||
enable_testing()
|
||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.21)
|
||||
# Emulates CMake 3.21+ behavior.
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
set(PROJECT_IS_TOP_LEVEL ON)
|
||||
set(${PROJECT_NAME}_IS_TOP_LEVEL ON)
|
||||
else()
|
||||
set(PROJECT_IS_TOP_LEVEL OFF)
|
||||
set(${PROJECT_NAME}_IS_TOP_LEVEL OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# The library version is based on libtool versioning of the ABI. The set of
|
||||
# rules for updating the version can be found here:
|
||||
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
# All changes in experimental modules are treated as if they don't affect the
|
||||
# interface and therefore only increase the revision.
|
||||
set(${PROJECT_NAME}_LIB_VERSION_CURRENT 5)
|
||||
set(${PROJECT_NAME}_LIB_VERSION_REVISION 1)
|
||||
set(${PROJECT_NAME}_LIB_VERSION_AGE 0)
|
||||
|
||||
#=============================
|
||||
# Language setup
|
||||
#=============================
|
||||
set(CMAKE_C_STANDARD 90)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
#=============================
|
||||
# Configurable options
|
||||
#=============================
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries." ON)
|
||||
option(SECP256K1_DISABLE_SHARED "Disable shared library. Overrides BUILD_SHARED_LIBS." OFF)
|
||||
if(SECP256K1_DISABLE_SHARED)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
endif()
|
||||
|
||||
option(SECP256K1_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL})
|
||||
|
||||
## Modules
|
||||
|
||||
# We declare all options before processing them, to make sure we can express
|
||||
# dependencies while processing.
|
||||
option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON)
|
||||
option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF)
|
||||
option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON)
|
||||
option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON)
|
||||
option(SECP256K1_ENABLE_MODULE_MUSIG "Enable musig module." ON)
|
||||
option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON)
|
||||
|
||||
# Processing must be done in a topological sorting of the dependency graph
|
||||
# (dependent module first).
|
||||
if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
|
||||
add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_MUSIG)
|
||||
if(DEFINED SECP256K1_ENABLE_MODULE_SCHNORRSIG AND NOT SECP256K1_ENABLE_MODULE_SCHNORRSIG)
|
||||
message(FATAL_ERROR "Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.")
|
||||
endif()
|
||||
set(SECP256K1_ENABLE_MODULE_SCHNORRSIG ON)
|
||||
add_compile_definitions(ENABLE_MODULE_MUSIG=1)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
|
||||
if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS)
|
||||
message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.")
|
||||
endif()
|
||||
set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON)
|
||||
add_compile_definitions(ENABLE_MODULE_SCHNORRSIG=1)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_EXTRAKEYS)
|
||||
add_compile_definitions(ENABLE_MODULE_EXTRAKEYS=1)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_RECOVERY)
|
||||
add_compile_definitions(ENABLE_MODULE_RECOVERY=1)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_ECDH)
|
||||
add_compile_definitions(ENABLE_MODULE_ECDH=1)
|
||||
endif()
|
||||
|
||||
option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF)
|
||||
if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS)
|
||||
add_compile_definitions(USE_EXTERNAL_DEFAULT_CALLBACKS=1)
|
||||
endif()
|
||||
|
||||
set(SECP256K1_ECMULT_WINDOW_SIZE 15 CACHE STRING "Window size for ecmult precomputation for verification, specified as integer in range [2..24]. The default value is a reasonable setting for desktop machines (currently 15). [default=15]")
|
||||
set_property(CACHE SECP256K1_ECMULT_WINDOW_SIZE PROPERTY STRINGS 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24)
|
||||
include(CheckStringOptionValue)
|
||||
check_string_option_value(SECP256K1_ECMULT_WINDOW_SIZE)
|
||||
add_compile_definitions(ECMULT_WINDOW_SIZE=${SECP256K1_ECMULT_WINDOW_SIZE})
|
||||
|
||||
set(SECP256K1_ECMULT_GEN_KB 86 CACHE STRING "The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms). Larger values result in possibly better signing or key generation performance at the cost of a larger table. Valid choices are 2, 22, 86. The default value is a reasonable setting for desktop machines (currently 86). [default=86]")
|
||||
set_property(CACHE SECP256K1_ECMULT_GEN_KB PROPERTY STRINGS 2 22 86)
|
||||
check_string_option_value(SECP256K1_ECMULT_GEN_KB)
|
||||
if(SECP256K1_ECMULT_GEN_KB EQUAL 2)
|
||||
add_compile_definitions(COMB_BLOCKS=2)
|
||||
add_compile_definitions(COMB_TEETH=5)
|
||||
elseif(SECP256K1_ECMULT_GEN_KB EQUAL 22)
|
||||
add_compile_definitions(COMB_BLOCKS=11)
|
||||
add_compile_definitions(COMB_TEETH=6)
|
||||
elseif(SECP256K1_ECMULT_GEN_KB EQUAL 86)
|
||||
add_compile_definitions(COMB_BLOCKS=43)
|
||||
add_compile_definitions(COMB_TEETH=6)
|
||||
endif()
|
||||
|
||||
set(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY "OFF" CACHE STRING "Test-only override of the (autodetected by the C code) \"widemul\" setting. Legal values are: \"OFF\", \"int128_struct\", \"int128\" or \"int64\". [default=OFF]")
|
||||
set_property(CACHE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY PROPERTY STRINGS "OFF" "int128_struct" "int128" "int64")
|
||||
check_string_option_value(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY)
|
||||
if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY)
|
||||
string(TOUPPER "${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}" widemul_upper_value)
|
||||
add_compile_definitions(USE_FORCE_WIDEMUL_${widemul_upper_value}=1)
|
||||
endif()
|
||||
mark_as_advanced(FORCE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY)
|
||||
|
||||
set(SECP256K1_ASM "AUTO" CACHE STRING "Assembly to use: \"AUTO\", \"OFF\", \"x86_64\" or \"arm32\" (experimental). [default=AUTO]")
|
||||
set_property(CACHE SECP256K1_ASM PROPERTY STRINGS "AUTO" "OFF" "x86_64" "arm32")
|
||||
check_string_option_value(SECP256K1_ASM)
|
||||
if(SECP256K1_ASM STREQUAL "arm32")
|
||||
enable_language(ASM)
|
||||
include(CheckArm32Assembly)
|
||||
check_arm32_assembly()
|
||||
if(HAVE_ARM32_ASM)
|
||||
add_compile_definitions(USE_EXTERNAL_ASM=1)
|
||||
else()
|
||||
message(FATAL_ERROR "ARM32 assembly requested but not available.")
|
||||
endif()
|
||||
elseif(SECP256K1_ASM)
|
||||
include(CheckX86_64Assembly)
|
||||
check_x86_64_assembly()
|
||||
if(HAVE_X86_64_ASM)
|
||||
set(SECP256K1_ASM "x86_64")
|
||||
add_compile_definitions(USE_ASM_X86_64=1)
|
||||
elseif(SECP256K1_ASM STREQUAL "AUTO")
|
||||
set(SECP256K1_ASM "OFF")
|
||||
else()
|
||||
message(FATAL_ERROR "x86_64 assembly requested but not available.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF)
|
||||
if(NOT SECP256K1_EXPERIMENTAL)
|
||||
if(SECP256K1_ASM STREQUAL "arm32")
|
||||
message(FATAL_ERROR "ARM32 assembly is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(SECP256K1_VALGRIND "AUTO" CACHE STRING "Build with extra checks for running inside Valgrind. [default=AUTO]")
|
||||
set_property(CACHE SECP256K1_VALGRIND PROPERTY STRINGS "AUTO" "OFF" "ON")
|
||||
check_string_option_value(SECP256K1_VALGRIND)
|
||||
if(SECP256K1_VALGRIND)
|
||||
find_package(Valgrind MODULE)
|
||||
if(Valgrind_FOUND)
|
||||
set(SECP256K1_VALGRIND ON)
|
||||
include_directories(${Valgrind_INCLUDE_DIR})
|
||||
add_compile_definitions(VALGRIND)
|
||||
elseif(SECP256K1_VALGRIND STREQUAL "AUTO")
|
||||
set(SECP256K1_VALGRIND OFF)
|
||||
else()
|
||||
message(FATAL_ERROR "Valgrind support requested but valgrind/memcheck.h header not available.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." ON)
|
||||
option(SECP256K1_BUILD_TESTS "Build tests." ON)
|
||||
option(SECP256K1_BUILD_EXHAUSTIVE_TESTS "Build exhaustive tests." ON)
|
||||
option(SECP256K1_BUILD_CTIME_TESTS "Build constant-time tests." ${SECP256K1_VALGRIND})
|
||||
option(SECP256K1_BUILD_EXAMPLES "Build examples." OFF)
|
||||
|
||||
# Redefine configuration flags.
|
||||
# We leave assertions on, because they are only used in the examples, and we want them always on there.
|
||||
if(MSVC)
|
||||
string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
||||
string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
|
||||
else()
|
||||
string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
||||
string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
|
||||
# Prefer -O2 optimization level. (-O3 is CMake's default for Release for many compilers.)
|
||||
string(REGEX REPLACE "-O3( |$)" "-O2\\1" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
endif()
|
||||
|
||||
# Define custom "Coverage" build type.
|
||||
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O0 -DCOVERAGE=1 --coverage" CACHE STRING
|
||||
"Flags used by the C compiler during \"Coverage\" builds."
|
||||
FORCE
|
||||
)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING
|
||||
"Flags used for linking binaries during \"Coverage\" builds."
|
||||
FORCE
|
||||
)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING
|
||||
"Flags used by the shared libraries linker during \"Coverage\" builds."
|
||||
FORCE
|
||||
)
|
||||
mark_as_advanced(
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
)
|
||||
|
||||
if(PROJECT_IS_TOP_LEVEL)
|
||||
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
set(default_build_type "RelWithDebInfo")
|
||||
if(is_multi_config)
|
||||
set(CMAKE_CONFIGURATION_TYPES "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" CACHE STRING
|
||||
"Supported configuration types."
|
||||
FORCE
|
||||
)
|
||||
else()
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
|
||||
STRINGS "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage"
|
||||
)
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "Setting build type to \"${default_build_type}\" as none was specified")
|
||||
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING
|
||||
"Choose the type of build."
|
||||
FORCE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(TryAppendCFlags)
|
||||
if(MSVC)
|
||||
# Keep the following commands ordered lexicographically.
|
||||
try_append_c_flags(/W3) # Production quality warning level.
|
||||
try_append_c_flags(/wd4146) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned".
|
||||
try_append_c_flags(/wd4244) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data".
|
||||
try_append_c_flags(/wd4267) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data".
|
||||
# Eliminate deprecation warnings for the older, less secure functions.
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
else()
|
||||
# Keep the following commands ordered lexicographically.
|
||||
try_append_c_flags(-pedantic)
|
||||
try_append_c_flags(-Wall) # GCC >= 2.95 and probably many other compilers.
|
||||
try_append_c_flags(-Wcast-align) # GCC >= 2.95.
|
||||
try_append_c_flags(-Wcast-align=strict) # GCC >= 8.0.
|
||||
try_append_c_flags(-Wconditional-uninitialized) # Clang >= 3.0 only.
|
||||
try_append_c_flags(-Wextra) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions.
|
||||
try_append_c_flags(-Wnested-externs)
|
||||
try_append_c_flags(-Wno-long-long) # GCC >= 3.0, -Wlong-long is implied by -pedantic.
|
||||
try_append_c_flags(-Wno-overlength-strings) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic.
|
||||
try_append_c_flags(-Wno-unused-function) # GCC >= 3.0, -Wunused-function is implied by -Wall.
|
||||
try_append_c_flags(-Wreserved-identifier) # Clang >= 13.0 only.
|
||||
try_append_c_flags(-Wshadow)
|
||||
try_append_c_flags(-Wstrict-prototypes)
|
||||
try_append_c_flags(-Wundef)
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
|
||||
set(print_msan_notice)
|
||||
if(SECP256K1_BUILD_CTIME_TESTS)
|
||||
include(CheckMemorySanitizer)
|
||||
check_memory_sanitizer(msan_enabled)
|
||||
if(msan_enabled)
|
||||
try_append_c_flags(-fno-sanitize-memory-param-retval)
|
||||
set(print_msan_notice YES)
|
||||
endif()
|
||||
unset(msan_enabled)
|
||||
endif()
|
||||
|
||||
set(SECP256K1_APPEND_CFLAGS "" CACHE STRING "Compiler flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
|
||||
if(SECP256K1_APPEND_CFLAGS)
|
||||
# Appending to this low-level rule variable is the only way to
|
||||
# guarantee that the flags appear at the end of the command line.
|
||||
string(APPEND CMAKE_C_COMPILE_OBJECT " ${SECP256K1_APPEND_CFLAGS}")
|
||||
endif()
|
||||
|
||||
set(SECP256K1_APPEND_LDFLAGS "" CACHE STRING "Linker flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
|
||||
if(SECP256K1_APPEND_LDFLAGS)
|
||||
# Appending to this low-level rule variable is the only way to
|
||||
# guarantee that the flags appear at the end of the command line.
|
||||
string(APPEND CMAKE_C_CREATE_SHARED_LIBRARY " ${SECP256K1_APPEND_LDFLAGS}")
|
||||
string(APPEND CMAKE_C_LINK_EXECUTABLE " ${SECP256K1_APPEND_LDFLAGS}")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
endif()
|
||||
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
endif()
|
||||
if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
|
||||
endif()
|
||||
add_subdirectory(src)
|
||||
if(SECP256K1_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
message("\n")
|
||||
message("secp256k1 configure summary")
|
||||
message("===========================")
|
||||
message("Build artifacts:")
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(library_type "Shared")
|
||||
else()
|
||||
set(library_type "Static")
|
||||
endif()
|
||||
|
||||
message(" library type ........................ ${library_type}")
|
||||
message("Optional modules:")
|
||||
message(" ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH}")
|
||||
message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}")
|
||||
message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}")
|
||||
message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}")
|
||||
message(" musig ............................... ${SECP256K1_ENABLE_MODULE_MUSIG}")
|
||||
message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}")
|
||||
message("Parameters:")
|
||||
message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}")
|
||||
message(" ecmult gen table size ............... ${SECP256K1_ECMULT_GEN_KB} KiB")
|
||||
message("Optional features:")
|
||||
message(" assembly ............................ ${SECP256K1_ASM}")
|
||||
message(" external callbacks .................. ${SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS}")
|
||||
if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY)
|
||||
message(" wide multiplication (test-only) ..... ${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}")
|
||||
endif()
|
||||
message("Optional binaries:")
|
||||
message(" benchmark ........................... ${SECP256K1_BUILD_BENCHMARK}")
|
||||
message(" noverify_tests ...................... ${SECP256K1_BUILD_TESTS}")
|
||||
set(tests_status "${SECP256K1_BUILD_TESTS}")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
|
||||
set(tests_status OFF)
|
||||
endif()
|
||||
message(" tests ............................... ${tests_status}")
|
||||
message(" exhaustive tests .................... ${SECP256K1_BUILD_EXHAUSTIVE_TESTS}")
|
||||
message(" ctime_tests ......................... ${SECP256K1_BUILD_CTIME_TESTS}")
|
||||
message(" examples ............................ ${SECP256K1_BUILD_EXAMPLES}")
|
||||
message("")
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
set(cross_status "TRUE, for ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
else()
|
||||
set(cross_status "FALSE")
|
||||
endif()
|
||||
message("Cross compiling ....................... ${cross_status}")
|
||||
message("Valgrind .............................. ${SECP256K1_VALGRIND}")
|
||||
get_directory_property(definitions COMPILE_DEFINITIONS)
|
||||
string(REPLACE ";" " " definitions "${definitions}")
|
||||
message("Preprocessor defined macros ........... ${definitions}")
|
||||
message("C compiler ............................ ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}, ${CMAKE_C_COMPILER}")
|
||||
message("CFLAGS ................................ ${CMAKE_C_FLAGS}")
|
||||
get_directory_property(compile_options COMPILE_OPTIONS)
|
||||
string(REPLACE ";" " " compile_options "${compile_options}")
|
||||
message("Compile options ....................... " ${compile_options})
|
||||
if(NOT is_multi_config)
|
||||
message("Build type:")
|
||||
message(" - CMAKE_BUILD_TYPE ................... ${CMAKE_BUILD_TYPE}")
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
|
||||
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_${build_type}}")
|
||||
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_${build_type}}")
|
||||
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_${build_type}}")
|
||||
else()
|
||||
message("Supported configurations .............. ${CMAKE_CONFIGURATION_TYPES}")
|
||||
message("RelWithDebInfo configuration:")
|
||||
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
||||
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
message("Debug configuration:")
|
||||
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_DEBUG}")
|
||||
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
|
||||
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}")
|
||||
endif()
|
||||
if(SECP256K1_APPEND_CFLAGS)
|
||||
message("SECP256K1_APPEND_CFLAGS ............... ${SECP256K1_APPEND_CFLAGS}")
|
||||
endif()
|
||||
if(SECP256K1_APPEND_LDFLAGS)
|
||||
message("SECP256K1_APPEND_LDFLAGS .............. ${SECP256K1_APPEND_LDFLAGS}")
|
||||
endif()
|
||||
message("")
|
||||
if(print_msan_notice)
|
||||
message(
|
||||
"Note:\n"
|
||||
" MemorySanitizer detected, tried to add -fno-sanitize-memory-param-retval to compile options\n"
|
||||
" to avoid false positives in ctime_tests. Pass -DSECP256K1_BUILD_CTIME_TESTS=OFF to avoid this.\n"
|
||||
)
|
||||
endif()
|
||||
if(SECP256K1_EXPERIMENTAL)
|
||||
message(
|
||||
" ******\n"
|
||||
" WARNING: experimental build\n"
|
||||
" Experimental features do not have stable APIs or properties, and may not be safe for production use.\n"
|
||||
" ******\n"
|
||||
)
|
||||
endif()
|
||||
19
crypto/secp256k1/libsecp256k1/CMakePresets.json
Normal file
19
crypto/secp256k1/libsecp256k1/CMakePresets.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0},
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "dev-mode",
|
||||
"displayName": "Development mode (intended only for developers of the library)",
|
||||
"cacheVariables": {
|
||||
"SECP256K1_EXPERIMENTAL": "ON",
|
||||
"SECP256K1_ENABLE_MODULE_RECOVERY": "ON",
|
||||
"SECP256K1_BUILD_EXAMPLES": "ON"
|
||||
},
|
||||
"warnings": {
|
||||
"dev": true,
|
||||
"uninitialized": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
109
crypto/secp256k1/libsecp256k1/CONTRIBUTING.md
Normal file
109
crypto/secp256k1/libsecp256k1/CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# Contributing to libsecp256k1
|
||||
|
||||
## Scope
|
||||
|
||||
libsecp256k1 is a library for elliptic curve cryptography on the curve secp256k1, not a general-purpose cryptography library.
|
||||
The library primarily serves the needs of the Bitcoin Core project but provides additional functionality for the benefit of the wider Bitcoin ecosystem.
|
||||
|
||||
## Adding new functionality or modules
|
||||
|
||||
The libsecp256k1 project welcomes contributions in the form of new functionality or modules, provided they are within the project's scope.
|
||||
|
||||
It is the responsibility of the contributors to convince the maintainers that the proposed functionality is within the project's scope, high-quality and maintainable.
|
||||
Contributors are recommended to provide the following in addition to the new code:
|
||||
|
||||
* **Specification:**
|
||||
A specification can help significantly in reviewing the new code as it provides documentation and context.
|
||||
It may justify various design decisions, give a motivation and outline security goals.
|
||||
If the specification contains pseudocode, a reference implementation or test vectors, these can be used to compare with the proposed libsecp256k1 code.
|
||||
* **Security Arguments:**
|
||||
In addition to a defining the security goals, it should be argued that the new functionality meets these goals.
|
||||
Depending on the nature of the new functionality, a wide range of security arguments are acceptable, ranging from being "obviously secure" to rigorous proofs of security.
|
||||
* **Relevance Arguments:**
|
||||
The relevance of the new functionality for the Bitcoin ecosystem should be argued by outlining clear use cases.
|
||||
|
||||
These are not the only factors taken into account when considering to add new functionality.
|
||||
The proposed new libsecp256k1 code must be of high quality, including API documentation and tests, as well as featuring a misuse-resistant API design.
|
||||
|
||||
We recommend reaching out to other contributors (see [Communication Channels](#communication-channels)) and get feedback before implementing new functionality.
|
||||
|
||||
## Communication channels
|
||||
|
||||
Most communication about libsecp256k1 occurs on the GitHub repository: in issues, pull request or on the discussion board.
|
||||
|
||||
Additionally, there is an IRC channel dedicated to libsecp256k1, with biweekly meetings (see channel topic).
|
||||
The channel is `#secp256k1` on Libera Chat.
|
||||
The easiest way to participate on IRC is with the web client, [web.libera.chat](https://web.libera.chat/#secp256k1).
|
||||
Chat history logs can be found at https://gnusha.org/secp256k1/.
|
||||
|
||||
## Contributor workflow & peer review
|
||||
|
||||
The Contributor Workflow & Peer Review in libsecp256k1 are similar to Bitcoin Core's workflow and review processes described in its [CONTRIBUTING.md](https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md).
|
||||
|
||||
### Coding conventions
|
||||
|
||||
In addition, libsecp256k1 tries to maintain the following coding conventions:
|
||||
|
||||
* No runtime heap allocation (e.g., no `malloc`) unless explicitly requested by the caller (via `secp256k1_context_create` or `secp256k1_scratch_space_create`, for example). Moreover, it should be possible to use the library without any heap allocations.
|
||||
* The tests should cover all lines and branches of the library (see [Test coverage](#coverage)).
|
||||
* Operations involving secret data should be tested for being constant time with respect to the secrets (see [src/ctime_tests.c](src/ctime_tests.c)).
|
||||
* Local variables containing secret data should be cleared explicitly to try to delete secrets from memory.
|
||||
* Use `secp256k1_memcmp_var` instead of `memcmp` (see [#823](https://github.com/bitcoin-core/secp256k1/issues/823)).
|
||||
* As a rule of thumb, the default values for configuration options should target standard desktop machines and align with Bitcoin Core's defaults, and the tests should mostly exercise the default configuration (see [#1549](https://github.com/bitcoin-core/secp256k1/issues/1549#issuecomment-2200559257)).
|
||||
|
||||
#### Style conventions
|
||||
|
||||
* Commits should be atomic and diffs should be easy to read. For this reason, do not mix any formatting fixes or code moves with actual code changes. Make sure each individual commit is hygienic: that it builds successfully on its own without warnings, errors, regressions, or test failures.
|
||||
* New code should adhere to the style of existing, in particular surrounding, code. Other than that, we do not enforce strict rules for code formatting.
|
||||
* The code conforms to C89. Most notably, that means that only `/* ... */` comments are allowed (no `//` line comments). Moreover, any declarations in a `{ ... }` block (e.g., a function) must appear at the beginning of the block before any statements. When you would like to declare a variable in the middle of a block, you can open a new block:
|
||||
```C
|
||||
void secp256k_foo(void) {
|
||||
unsigned int x; /* declaration */
|
||||
int y = 2*x; /* declaration */
|
||||
x = 17; /* statement */
|
||||
{
|
||||
int a, b; /* declaration */
|
||||
a = x + y; /* statement */
|
||||
secp256k_bar(x, &b); /* statement */
|
||||
}
|
||||
}
|
||||
```
|
||||
* Use `unsigned int` instead of just `unsigned`.
|
||||
* Use `void *ptr` instead of `void* ptr`.
|
||||
* Arguments of the publicly-facing API must have a specific order defined in [include/secp256k1.h](include/secp256k1.h).
|
||||
* User-facing comment lines in headers should be limited to 80 chars if possible.
|
||||
* All identifiers in file scope should start with `secp256k1_`.
|
||||
* Avoid trailing whitespace.
|
||||
* Use the constants `EXIT_SUCCESS`/`EXIT_FAILURE` (defined in `stdlib.h`) to indicate program execution status for examples and other binaries.
|
||||
|
||||
### Tests
|
||||
|
||||
#### Coverage
|
||||
|
||||
This library aims to have full coverage of reachable lines and branches.
|
||||
|
||||
To create a test coverage report, configure with `--enable-coverage` (use of GCC is necessary):
|
||||
|
||||
$ ./configure --enable-coverage
|
||||
|
||||
Run the tests:
|
||||
|
||||
$ make check
|
||||
|
||||
To create a report, `gcovr` is recommended, as it includes branch coverage reporting:
|
||||
|
||||
$ gcovr --exclude 'src/bench*' --print-summary
|
||||
|
||||
To create a HTML report with coloured and annotated source code:
|
||||
|
||||
$ mkdir -p coverage
|
||||
$ gcovr --exclude 'src/bench*' --html --html-details -o coverage/coverage.html
|
||||
|
||||
#### Exhaustive tests
|
||||
|
||||
There are tests of several functions in which a small group replaces secp256k1.
|
||||
These tests are *exhaustive* since they provide all elements and scalars of the small group as input arguments (see [src/tests_exhaustive.c](src/tests_exhaustive.c)).
|
||||
|
||||
### Benchmarks
|
||||
|
||||
See `src/bench*.c` for examples of benchmarks.
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
ACLOCAL_AMFLAGS = -I build-aux/m4
|
||||
|
||||
# AM_CFLAGS will be automatically prepended to CFLAGS by Automake when compiling some foo
|
||||
# which does not have an explicit foo_CFLAGS variable set.
|
||||
AM_CFLAGS = $(SECP_CFLAGS)
|
||||
|
||||
lib_LTLIBRARIES = libsecp256k1.la
|
||||
if USE_JNI
|
||||
JNI_LIB = libsecp256k1_jni.la
|
||||
noinst_LTLIBRARIES = $(JNI_LIB)
|
||||
else
|
||||
JNI_LIB =
|
||||
endif
|
||||
include_HEADERS = include/secp256k1.h
|
||||
include_HEADERS += include/secp256k1_preallocated.h
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += src/scalar.h
|
||||
noinst_HEADERS += src/scalar_4x64.h
|
||||
|
|
@ -19,29 +18,44 @@ noinst_HEADERS += src/scalar_8x32_impl.h
|
|||
noinst_HEADERS += src/scalar_low_impl.h
|
||||
noinst_HEADERS += src/group.h
|
||||
noinst_HEADERS += src/group_impl.h
|
||||
noinst_HEADERS += src/num_gmp.h
|
||||
noinst_HEADERS += src/num_gmp_impl.h
|
||||
noinst_HEADERS += src/ecdsa.h
|
||||
noinst_HEADERS += src/ecdsa_impl.h
|
||||
noinst_HEADERS += src/eckey.h
|
||||
noinst_HEADERS += src/eckey_impl.h
|
||||
noinst_HEADERS += src/ecmult.h
|
||||
noinst_HEADERS += src/ecmult_impl.h
|
||||
noinst_HEADERS += src/ecmult_compute_table.h
|
||||
noinst_HEADERS += src/ecmult_compute_table_impl.h
|
||||
noinst_HEADERS += src/ecmult_const.h
|
||||
noinst_HEADERS += src/ecmult_const_impl.h
|
||||
noinst_HEADERS += src/ecmult_gen.h
|
||||
noinst_HEADERS += src/ecmult_gen_impl.h
|
||||
noinst_HEADERS += src/num.h
|
||||
noinst_HEADERS += src/num_impl.h
|
||||
noinst_HEADERS += src/ecmult_gen_compute_table.h
|
||||
noinst_HEADERS += src/ecmult_gen_compute_table_impl.h
|
||||
noinst_HEADERS += src/field_10x26.h
|
||||
noinst_HEADERS += src/field_10x26_impl.h
|
||||
noinst_HEADERS += src/field_5x52.h
|
||||
noinst_HEADERS += src/field_5x52_impl.h
|
||||
noinst_HEADERS += src/field_5x52_int128_impl.h
|
||||
noinst_HEADERS += src/field_5x52_asm_impl.h
|
||||
noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h
|
||||
noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
|
||||
noinst_HEADERS += src/modinv32.h
|
||||
noinst_HEADERS += src/modinv32_impl.h
|
||||
noinst_HEADERS += src/modinv64.h
|
||||
noinst_HEADERS += src/modinv64_impl.h
|
||||
noinst_HEADERS += src/precomputed_ecmult.h
|
||||
noinst_HEADERS += src/precomputed_ecmult_gen.h
|
||||
noinst_HEADERS += src/assumptions.h
|
||||
noinst_HEADERS += src/checkmem.h
|
||||
noinst_HEADERS += src/testutil.h
|
||||
noinst_HEADERS += src/util.h
|
||||
noinst_HEADERS += src/int128.h
|
||||
noinst_HEADERS += src/int128_impl.h
|
||||
noinst_HEADERS += src/int128_native.h
|
||||
noinst_HEADERS += src/int128_native_impl.h
|
||||
noinst_HEADERS += src/int128_struct.h
|
||||
noinst_HEADERS += src/int128_struct_impl.h
|
||||
noinst_HEADERS += src/scratch.h
|
||||
noinst_HEADERS += src/scratch_impl.h
|
||||
noinst_HEADERS += src/selftest.h
|
||||
noinst_HEADERS += src/testrand.h
|
||||
noinst_HEADERS += src/testrand_impl.h
|
||||
noinst_HEADERS += src/hash.h
|
||||
|
|
@ -49,17 +63,28 @@ noinst_HEADERS += src/hash_impl.h
|
|||
noinst_HEADERS += src/field.h
|
||||
noinst_HEADERS += src/field_impl.h
|
||||
noinst_HEADERS += src/bench.h
|
||||
noinst_HEADERS += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h
|
||||
noinst_HEADERS += src/hsort.h
|
||||
noinst_HEADERS += src/hsort_impl.h
|
||||
noinst_HEADERS += contrib/lax_der_parsing.h
|
||||
noinst_HEADERS += contrib/lax_der_parsing.c
|
||||
noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
|
||||
noinst_HEADERS += contrib/lax_der_privatekey_parsing.c
|
||||
noinst_HEADERS += examples/examples_util.h
|
||||
|
||||
PRECOMPUTED_LIB = libsecp256k1_precomputed.la
|
||||
noinst_LTLIBRARIES = $(PRECOMPUTED_LIB)
|
||||
libsecp256k1_precomputed_la_SOURCES = src/precomputed_ecmult.c src/precomputed_ecmult_gen.c
|
||||
# We need `-I$(top_srcdir)/src` in VPATH builds if libsecp256k1_precomputed_la_SOURCES have been recreated in the build tree.
|
||||
# This helps users and packagers who insist on recreating the precomputed files (e.g., Gentoo).
|
||||
libsecp256k1_precomputed_la_CPPFLAGS = -I$(top_srcdir)/src $(SECP_CONFIG_DEFINES)
|
||||
|
||||
if USE_EXTERNAL_ASM
|
||||
COMMON_LIB = libsecp256k1_common.la
|
||||
noinst_LTLIBRARIES = $(COMMON_LIB)
|
||||
else
|
||||
COMMON_LIB =
|
||||
endif
|
||||
noinst_LTLIBRARIES += $(COMMON_LIB)
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libsecp256k1.pc
|
||||
|
|
@ -71,102 +96,186 @@ endif
|
|||
endif
|
||||
|
||||
libsecp256k1_la_SOURCES = src/secp256k1.c
|
||||
libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
|
||||
libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB)
|
||||
|
||||
libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c
|
||||
libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)
|
||||
libsecp256k1_la_CPPFLAGS = $(SECP_CONFIG_DEFINES)
|
||||
libsecp256k1_la_LIBADD = $(COMMON_LIB) $(PRECOMPUTED_LIB)
|
||||
libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE)
|
||||
|
||||
noinst_PROGRAMS =
|
||||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_verify bench_sign bench_internal
|
||||
bench_verify_SOURCES = src/bench_verify.c
|
||||
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
|
||||
bench_sign_SOURCES = src/bench_sign.c
|
||||
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
|
||||
noinst_PROGRAMS += bench bench_internal bench_ecmult
|
||||
bench_SOURCES = src/bench.c
|
||||
bench_LDADD = libsecp256k1.la
|
||||
bench_CPPFLAGS = $(SECP_CONFIG_DEFINES)
|
||||
bench_internal_SOURCES = src/bench_internal.c
|
||||
bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
|
||||
bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
|
||||
bench_internal_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB)
|
||||
bench_internal_CPPFLAGS = $(SECP_CONFIG_DEFINES)
|
||||
bench_ecmult_SOURCES = src/bench_ecmult.c
|
||||
bench_ecmult_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB)
|
||||
bench_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES)
|
||||
endif
|
||||
|
||||
TESTS =
|
||||
if USE_TESTS
|
||||
noinst_PROGRAMS += tests
|
||||
tests_SOURCES = src/tests.c
|
||||
tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
|
||||
TESTS += noverify_tests
|
||||
noinst_PROGRAMS += noverify_tests
|
||||
noverify_tests_SOURCES = src/tests.c
|
||||
noverify_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES)
|
||||
noverify_tests_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB)
|
||||
noverify_tests_LDFLAGS = -static
|
||||
if !ENABLE_COVERAGE
|
||||
tests_CPPFLAGS += -DVERIFY
|
||||
endif
|
||||
tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
|
||||
tests_LDFLAGS = -static
|
||||
TESTS += tests
|
||||
noinst_PROGRAMS += tests
|
||||
tests_SOURCES = $(noverify_tests_SOURCES)
|
||||
tests_CPPFLAGS = $(noverify_tests_CPPFLAGS) -DVERIFY
|
||||
tests_LDADD = $(noverify_tests_LDADD)
|
||||
tests_LDFLAGS = $(noverify_tests_LDFLAGS)
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_CTIME_TESTS
|
||||
noinst_PROGRAMS += ctime_tests
|
||||
ctime_tests_SOURCES = src/ctime_tests.c
|
||||
ctime_tests_LDADD = libsecp256k1.la
|
||||
ctime_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES)
|
||||
endif
|
||||
|
||||
if USE_EXHAUSTIVE_TESTS
|
||||
noinst_PROGRAMS += exhaustive_tests
|
||||
exhaustive_tests_SOURCES = src/tests_exhaustive.c
|
||||
exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDES)
|
||||
exhaustive_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES)
|
||||
if !ENABLE_COVERAGE
|
||||
exhaustive_tests_CPPFLAGS += -DVERIFY
|
||||
endif
|
||||
exhaustive_tests_LDADD = $(SECP_LIBS)
|
||||
# Note: do not include $(PRECOMPUTED_LIB) in exhaustive_tests (it uses runtime-generated tables).
|
||||
exhaustive_tests_LDADD = $(COMMON_LIB)
|
||||
exhaustive_tests_LDFLAGS = -static
|
||||
TESTS += exhaustive_tests
|
||||
endif
|
||||
|
||||
JAVAROOT=src/java
|
||||
JAVAORG=org/bitcoin
|
||||
JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar
|
||||
CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA)
|
||||
JAVA_FILES= \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java
|
||||
|
||||
if USE_JNI
|
||||
|
||||
$(JAVA_GUAVA):
|
||||
@echo Guava is missing. Fetch it via: \
|
||||
wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@)
|
||||
@false
|
||||
|
||||
.stamp-java: $(JAVA_FILES)
|
||||
@echo Compiling $^
|
||||
$(AM_V_at)$(CLASSPATH_ENV) javac $^
|
||||
@touch $@
|
||||
|
||||
if USE_TESTS
|
||||
|
||||
check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java
|
||||
$(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test
|
||||
|
||||
if USE_EXAMPLES
|
||||
noinst_PROGRAMS += ecdsa_example
|
||||
ecdsa_example_SOURCES = examples/ecdsa.c
|
||||
ecdsa_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC
|
||||
ecdsa_example_LDADD = libsecp256k1.la
|
||||
ecdsa_example_LDFLAGS = -static
|
||||
if BUILD_WINDOWS
|
||||
ecdsa_example_LDFLAGS += -lbcrypt
|
||||
endif
|
||||
TESTS += ecdsa_example
|
||||
if ENABLE_MODULE_ECDH
|
||||
noinst_PROGRAMS += ecdh_example
|
||||
ecdh_example_SOURCES = examples/ecdh.c
|
||||
ecdh_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC
|
||||
ecdh_example_LDADD = libsecp256k1.la
|
||||
ecdh_example_LDFLAGS = -static
|
||||
if BUILD_WINDOWS
|
||||
ecdh_example_LDFLAGS += -lbcrypt
|
||||
endif
|
||||
TESTS += ecdh_example
|
||||
endif
|
||||
if ENABLE_MODULE_SCHNORRSIG
|
||||
noinst_PROGRAMS += schnorr_example
|
||||
schnorr_example_SOURCES = examples/schnorr.c
|
||||
schnorr_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC
|
||||
schnorr_example_LDADD = libsecp256k1.la
|
||||
schnorr_example_LDFLAGS = -static
|
||||
if BUILD_WINDOWS
|
||||
schnorr_example_LDFLAGS += -lbcrypt
|
||||
endif
|
||||
TESTS += schnorr_example
|
||||
endif
|
||||
if ENABLE_MODULE_ELLSWIFT
|
||||
noinst_PROGRAMS += ellswift_example
|
||||
ellswift_example_SOURCES = examples/ellswift.c
|
||||
ellswift_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC
|
||||
ellswift_example_LDADD = libsecp256k1.la
|
||||
ellswift_example_LDFLAGS = -static
|
||||
if BUILD_WINDOWS
|
||||
ellswift_example_LDFLAGS += -lbcrypt
|
||||
endif
|
||||
TESTS += ellswift_example
|
||||
endif
|
||||
if ENABLE_MODULE_MUSIG
|
||||
noinst_PROGRAMS += musig_example
|
||||
musig_example_SOURCES = examples/musig.c
|
||||
musig_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC
|
||||
musig_example_LDADD = libsecp256k1.la
|
||||
musig_example_LDFLAGS = -static
|
||||
if BUILD_WINDOWS
|
||||
musig_example_LDFLAGS += -lbcrypt
|
||||
endif
|
||||
TESTS += musig_example
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
|
||||
CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
|
||||
### Precomputed tables
|
||||
EXTRA_PROGRAMS = precompute_ecmult precompute_ecmult_gen
|
||||
CLEANFILES = $(EXTRA_PROGRAMS)
|
||||
|
||||
gen_context_OBJECTS = gen_context.o
|
||||
gen_context_BIN = gen_context$(BUILD_EXEEXT)
|
||||
gen_%.o: src/gen_%.c
|
||||
$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
|
||||
precompute_ecmult_SOURCES = src/precompute_ecmult.c
|
||||
precompute_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES) -DVERIFY
|
||||
precompute_ecmult_LDADD = $(COMMON_LIB)
|
||||
|
||||
$(gen_context_BIN): $(gen_context_OBJECTS)
|
||||
$(CC_FOR_BUILD) $^ -o $@
|
||||
precompute_ecmult_gen_SOURCES = src/precompute_ecmult_gen.c
|
||||
precompute_ecmult_gen_CPPFLAGS = $(SECP_CONFIG_DEFINES) -DVERIFY
|
||||
precompute_ecmult_gen_LDADD = $(COMMON_LIB)
|
||||
|
||||
$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h
|
||||
$(tests_OBJECTS): src/ecmult_static_context.h
|
||||
$(bench_internal_OBJECTS): src/ecmult_static_context.h
|
||||
# See Automake manual, Section "Errors with distclean".
|
||||
# We don't list any dependencies for the prebuilt files here because
|
||||
# otherwise make's decision whether to rebuild them (even in the first
|
||||
# build by a normal user) depends on mtimes, and thus is very fragile.
|
||||
# This means that rebuilds of the prebuilt files always need to be
|
||||
# forced by deleting them.
|
||||
src/precomputed_ecmult.c:
|
||||
$(MAKE) $(AM_MAKEFLAGS) precompute_ecmult$(EXEEXT)
|
||||
./precompute_ecmult$(EXEEXT)
|
||||
src/precomputed_ecmult_gen.c:
|
||||
$(MAKE) $(AM_MAKEFLAGS) precompute_ecmult_gen$(EXEEXT)
|
||||
./precompute_ecmult_gen$(EXEEXT)
|
||||
|
||||
src/ecmult_static_context.h: $(gen_context_BIN)
|
||||
./$(gen_context_BIN)
|
||||
PRECOMP = src/precomputed_ecmult_gen.c src/precomputed_ecmult.c
|
||||
precomp: $(PRECOMP)
|
||||
|
||||
CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java
|
||||
endif
|
||||
# Ensure the prebuilt files will be build first (only if they don't exist,
|
||||
# e.g., after `make maintainer-clean`).
|
||||
BUILT_SOURCES = $(PRECOMP)
|
||||
|
||||
EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES)
|
||||
.PHONY: clean-precomp
|
||||
clean-precomp:
|
||||
rm -f $(PRECOMP)
|
||||
maintainer-clean-local: clean-precomp
|
||||
|
||||
### Pregenerated test vectors
|
||||
### (see the comments in the previous section for detailed rationale)
|
||||
TESTVECTORS = src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h
|
||||
|
||||
src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h:
|
||||
mkdir -p $(@D)
|
||||
python3 $(top_srcdir)/tools/tests_wycheproof_generate.py $(top_srcdir)/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json > $@
|
||||
|
||||
testvectors: $(TESTVECTORS)
|
||||
|
||||
BUILT_SOURCES += $(TESTVECTORS)
|
||||
|
||||
.PHONY: clean-testvectors
|
||||
clean-testvectors:
|
||||
rm -f $(TESTVECTORS)
|
||||
maintainer-clean-local: clean-testvectors
|
||||
|
||||
### Additional files to distribute
|
||||
EXTRA_DIST = autogen.sh CHANGELOG.md SECURITY.md
|
||||
EXTRA_DIST += doc/release-process.md doc/safegcd_implementation.md
|
||||
EXTRA_DIST += doc/ellswift.md doc/musig.md
|
||||
EXTRA_DIST += examples/EXAMPLES_COPYING
|
||||
EXTRA_DIST += sage/gen_exhaustive_groups.sage
|
||||
EXTRA_DIST += sage/gen_split_lambda_constants.sage
|
||||
EXTRA_DIST += sage/group_prover.sage
|
||||
EXTRA_DIST += sage/prove_group_implementations.sage
|
||||
EXTRA_DIST += sage/secp256k1_params.sage
|
||||
EXTRA_DIST += sage/weierstrass_prover.sage
|
||||
EXTRA_DIST += src/wycheproof/WYCHEPROOF_COPYING
|
||||
EXTRA_DIST += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json
|
||||
EXTRA_DIST += tools/tests_wycheproof_generate.py
|
||||
|
||||
if ENABLE_MODULE_ECDH
|
||||
include src/modules/ecdh/Makefile.am.include
|
||||
|
|
@ -175,3 +284,19 @@ endif
|
|||
if ENABLE_MODULE_RECOVERY
|
||||
include src/modules/recovery/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_EXTRAKEYS
|
||||
include src/modules/extrakeys/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_SCHNORRSIG
|
||||
include src/modules/schnorrsig/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_MUSIG
|
||||
include src/modules/musig/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_ELLSWIFT
|
||||
include src/modules/ellswift/Makefile.am.include
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
libsecp256k1
|
||||
============
|
||||
|
||||
[](https://travis-ci.org/bitcoin-core/secp256k1)
|
||||

|
||||
[](https://web.libera.chat/#secp256k1)
|
||||
|
||||
Optimized C library for EC operations on curve secp256k1.
|
||||
High-performance high-assurance C library for digital signatures and other cryptographic primitives on the secp256k1 elliptic curve.
|
||||
|
||||
This library is a work in progress and is being used to research best practices. Use at your own risk.
|
||||
This library is intended to be the highest quality publicly available library for cryptography on the secp256k1 curve. However, the primary focus of its development has been for usage in the Bitcoin system and usage unlike Bitcoin's may be less well tested, verified, or suffer from a less well thought out interface. Correct usage requires some care and consideration that the library is fit for your application's purpose.
|
||||
|
||||
Features:
|
||||
* secp256k1 ECDSA signing/verification and key generation.
|
||||
* Adding/multiplying private/public keys.
|
||||
* Serialization/parsing of private keys, public keys, signatures.
|
||||
* Constant time, constant memory access signing and pubkey generation.
|
||||
* Derandomized DSA (via RFC6979 or with a caller provided function.)
|
||||
* Additive and multiplicative tweaking of secret/public keys.
|
||||
* Serialization/parsing of secret keys, public keys, signatures.
|
||||
* Constant time, constant memory access signing and public key generation.
|
||||
* Derandomized ECDSA (via RFC6979 or with a caller provided function.)
|
||||
* Very efficient implementation.
|
||||
* Suitable for embedded systems.
|
||||
* No runtime dependencies.
|
||||
* Optional module for public key recovery.
|
||||
* Optional module for ECDH key exchange.
|
||||
* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
|
||||
* Optional module for ElligatorSwift key exchange according to [BIP-324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki).
|
||||
* Optional module for MuSig2 Schnorr multi-signatures according to [BIP-327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki).
|
||||
|
||||
Implementation details
|
||||
----------------------
|
||||
|
|
@ -23,16 +31,18 @@ Implementation details
|
|||
* Extensive testing infrastructure.
|
||||
* Structured to facilitate review and analysis.
|
||||
* Intended to be portable to any system with a C89 compiler and uint64_t support.
|
||||
* No use of floating types.
|
||||
* Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.")
|
||||
* Field operations
|
||||
* Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1).
|
||||
* Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys).
|
||||
* Using 10 26-bit limbs.
|
||||
* Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman).
|
||||
* Using 5 52-bit limbs
|
||||
* Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan).
|
||||
* This is an experimental feature that has not received enough scrutiny to satisfy the standard of quality of this library but is made available for testing and review by the community.
|
||||
* Scalar operations
|
||||
* Optimized implementation without data-dependent branches of arithmetic modulo the curve's order.
|
||||
* Using 4 64-bit limbs (relying on __int128 support in the compiler).
|
||||
* Using 8 32-bit limbs.
|
||||
* Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman).
|
||||
* Group operations
|
||||
* Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7).
|
||||
* Use addition between points in Jacobian and affine coordinates where possible.
|
||||
|
|
@ -42,20 +52,126 @@ Implementation details
|
|||
* Use wNAF notation for point multiplicands.
|
||||
* Use a much larger window for multiples of G, using precomputed multiples.
|
||||
* Use Shamir's trick to do the multiplication with the public key and the generator simultaneously.
|
||||
* Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
|
||||
* Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
|
||||
* Point multiplication for signing
|
||||
* Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions.
|
||||
* Access the table with branch-free conditional moves so memory access is uniform.
|
||||
* No data-dependent branches
|
||||
* The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally.
|
||||
* Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains)
|
||||
* Access the table with branch-free conditional moves so memory access is uniform.
|
||||
* No data-dependent branches
|
||||
* Optional runtime blinding which attempts to frustrate differential power analysis.
|
||||
* The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally.
|
||||
|
||||
Build steps
|
||||
Obtaining and verifying
|
||||
-----------------------
|
||||
|
||||
The git tag for each release (e.g. `v0.6.0`) is GPG-signed by one of the maintainers.
|
||||
For a fully verified build of this project, it is recommended to obtain this repository
|
||||
via git, obtain the GPG keys of the signing maintainer(s), and then verify the release
|
||||
tag's signature using git.
|
||||
|
||||
This can be done with the following steps:
|
||||
|
||||
1. Obtain the GPG keys listed in [SECURITY.md](./SECURITY.md).
|
||||
2. If possible, cross-reference these key IDs with another source controlled by its owner (e.g.
|
||||
social media, personal website). This is to mitigate the unlikely case that incorrect
|
||||
content is being presented by this repository.
|
||||
3. Clone the repository:
|
||||
```
|
||||
git clone https://github.com/bitcoin-core/secp256k1
|
||||
```
|
||||
4. Check out the latest release tag, e.g.
|
||||
```
|
||||
git checkout v0.6.0
|
||||
```
|
||||
5. Use git to verify the GPG signature:
|
||||
```
|
||||
% git tag -v v0.6.0 | grep -C 3 'Good signature'
|
||||
|
||||
gpg: Signature made Mon 04 Nov 2024 12:14:44 PM EST
|
||||
gpg: using RSA key 4BBB845A6F5A65A69DFAEC234861DBF262123605
|
||||
gpg: Good signature from "Jonas Nick <jonas@n-ck.net>" [unknown]
|
||||
gpg: aka "Jonas Nick <jonasd.nick@gmail.com>" [unknown]
|
||||
gpg: WARNING: This key is not certified with a trusted signature!
|
||||
gpg: There is no indication that the signature belongs to the owner.
|
||||
Primary key fingerprint: 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366
|
||||
Subkey fingerprint: 4BBB 845A 6F5A 65A6 9DFA EC23 4861 DBF2 6212 3605
|
||||
```
|
||||
|
||||
Building with Autotools
|
||||
-----------------------
|
||||
|
||||
$ ./autogen.sh # Generate a ./configure script
|
||||
$ ./configure # Generate a build system
|
||||
$ make # Run the actual build process
|
||||
$ make check # Run the test suite
|
||||
$ sudo make install # Install the library into the system (optional)
|
||||
|
||||
To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags.
|
||||
|
||||
Building with CMake (experimental)
|
||||
----------------------------------
|
||||
|
||||
To maintain a pristine source tree, CMake encourages to perform an out-of-source build by using a separate dedicated build tree.
|
||||
|
||||
### Building on POSIX systems
|
||||
|
||||
$ cmake -B build # Generate a build system in subdirectory "build"
|
||||
$ cmake --build build # Run the actual build process
|
||||
$ ctest --test-dir build # Run the test suite
|
||||
$ sudo cmake --install build # Install the library into the system (optional)
|
||||
|
||||
To compile optional modules (such as Schnorr signatures), you need to run `cmake` with additional flags (such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON`). Run `cmake -B build -LH` or `ccmake -B build` to see the full list of available flags.
|
||||
|
||||
### Cross compiling
|
||||
|
||||
To alleviate issues with cross compiling, preconfigured toolchain files are available in the `cmake` directory.
|
||||
For example, to cross compile for Windows:
|
||||
|
||||
$ cmake -B build -DCMAKE_TOOLCHAIN_FILE=cmake/x86_64-w64-mingw32.toolchain.cmake
|
||||
|
||||
To cross compile for Android with [NDK](https://developer.android.com/ndk/guides/cmake) (using NDK's toolchain file, and assuming the `ANDROID_NDK_ROOT` environment variable has been set):
|
||||
|
||||
$ cmake -B build -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=28
|
||||
|
||||
### Building on Windows
|
||||
|
||||
To build on Windows with Visual Studio, a proper [generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators) must be specified for a new build tree.
|
||||
|
||||
The following example assumes using of Visual Studio 2022 and CMake v3.21+.
|
||||
|
||||
In "Developer Command Prompt for VS 2022":
|
||||
|
||||
>cmake -G "Visual Studio 17 2022" -A x64 -B build
|
||||
>cmake --build build --config RelWithDebInfo
|
||||
|
||||
Usage examples
|
||||
-----------
|
||||
Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`.
|
||||
* [ECDSA example](examples/ecdsa.c)
|
||||
* [Schnorr signatures example](examples/schnorr.c)
|
||||
* [Deriving a shared secret (ECDH) example](examples/ecdh.c)
|
||||
* [ElligatorSwift key exchange example](examples/ellswift.c)
|
||||
|
||||
libsecp256k1 is built using autotools:
|
||||
To compile the Schnorr signature and ECDH examples, you also need to configure with `--enable-module-schnorrsig` and `--enable-module-ecdh`.
|
||||
|
||||
$ ./autogen.sh
|
||||
$ ./configure
|
||||
$ make
|
||||
$ ./tests
|
||||
$ sudo make install # optional
|
||||
Benchmark
|
||||
------------
|
||||
If configured with `--enable-benchmark` (which is the default), binaries for benchmarking the libsecp256k1 functions will be present in the root directory after the build.
|
||||
|
||||
To print the benchmark result to the command line:
|
||||
|
||||
$ ./bench_name
|
||||
|
||||
To create a CSV file for the benchmark result :
|
||||
|
||||
$ ./bench_name | sed '2d;s/ \{1,\}//g' > bench_name.csv
|
||||
|
||||
Reporting a vulnerability
|
||||
------------
|
||||
|
||||
See [SECURITY.md](SECURITY.md)
|
||||
|
||||
Contributing to libsecp256k1
|
||||
------------
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
|
|
|||
15
crypto/secp256k1/libsecp256k1/SECURITY.md
Normal file
15
crypto/secp256k1/libsecp256k1/SECURITY.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
To report security issues send an email to secp256k1-security@bitcoincore.org (not for support).
|
||||
|
||||
The following keys may be used to communicate sensitive information to developers:
|
||||
|
||||
| Name | Fingerprint |
|
||||
|------|-------------|
|
||||
| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 |
|
||||
| Jonas Nick | 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366 |
|
||||
| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 |
|
||||
|
||||
You can import a key by running the following command with that individual’s fingerprint: `gpg --keyserver hkps://keys.openpgp.org --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints containing spaces.
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Unit tests for fieldelem/groupelem, including ones intended to
|
||||
trigger fieldelem's boundary cases.
|
||||
* Complete constant-time operations for signing/keygen
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR finds include directories needed for compiling
|
||||
# programs using the JNI interface.
|
||||
#
|
||||
# JNI include directories are usually in the Java distribution. This is
|
||||
# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
|
||||
# that order. When this macro completes, a list of directories is left in
|
||||
# the variable JNI_INCLUDE_DIRS.
|
||||
#
|
||||
# Example usage follows:
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
|
||||
# do
|
||||
# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
|
||||
# done
|
||||
#
|
||||
# If you want to force a specific compiler:
|
||||
#
|
||||
# - at the configure.in level, set JAVAC=yourcompiler before calling
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# - at the configure level, setenv JAVAC
|
||||
#
|
||||
# Note: This macro can work with the autoconf M4 macros for Java programs.
|
||||
# This particular macro is not part of the original set of macros.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Don Anderson <dda@sleepycat.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 10
|
||||
|
||||
AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
|
||||
AC_DEFUN([AX_JNI_INCLUDE_DIR],[
|
||||
|
||||
JNI_INCLUDE_DIRS=""
|
||||
|
||||
if test "x$JAVA_HOME" != x; then
|
||||
_JTOPDIR="$JAVA_HOME"
|
||||
else
|
||||
if test "x$JAVAC" = x; then
|
||||
JAVAC=javac
|
||||
fi
|
||||
AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
|
||||
if test "x$_ACJNI_JAVAC" = xno; then
|
||||
AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
|
||||
fi
|
||||
_ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
|
||||
_JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
|
||||
fi
|
||||
|
||||
case "$host_os" in
|
||||
darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
|
||||
_JINC="$_JTOPDIR/Headers";;
|
||||
*) _JINC="$_JTOPDIR/include";;
|
||||
esac
|
||||
_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
|
||||
_AS_ECHO_LOG([_JINC=$_JINC])
|
||||
|
||||
# On Mac OS X 10.6.4, jni.h is a symlink:
|
||||
# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
|
||||
# -> ../../CurrentJDK/Headers/jni.h.
|
||||
|
||||
AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path,
|
||||
[
|
||||
if test -f "$_JINC/jni.h"; then
|
||||
ac_cv_jni_header_path="$_JINC"
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
|
||||
else
|
||||
_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
|
||||
if test -f "$_JTOPDIR/include/jni.h"; then
|
||||
ac_cv_jni_header_path="$_JTOPDIR/include"
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
|
||||
else
|
||||
ac_cv_jni_header_path=none
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
|
||||
# get the likely subdirectories for system specific java includes
|
||||
case "$host_os" in
|
||||
bsdi*) _JNI_INC_SUBDIRS="bsdos";;
|
||||
darwin*) _JNI_INC_SUBDIRS="darwin";;
|
||||
freebsd*) _JNI_INC_SUBDIRS="freebsd";;
|
||||
linux*) _JNI_INC_SUBDIRS="linux genunix";;
|
||||
osf*) _JNI_INC_SUBDIRS="alpha";;
|
||||
solaris*) _JNI_INC_SUBDIRS="solaris";;
|
||||
mingw*) _JNI_INC_SUBDIRS="win32";;
|
||||
cygwin*) _JNI_INC_SUBDIRS="win32";;
|
||||
*) _JNI_INC_SUBDIRS="genunix";;
|
||||
esac
|
||||
|
||||
if test "x$ac_cv_jni_header_path" != "xnone"; then
|
||||
# add any subdirectories that are present
|
||||
for JINCSUBDIR in $_JNI_INC_SUBDIRS
|
||||
do
|
||||
if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
])
|
||||
|
||||
# _ACJNI_FOLLOW_SYMLINKS <path>
|
||||
# Follows symbolic links on <path>,
|
||||
# finally setting variable _ACJNI_FOLLOWED
|
||||
# ----------------------------------------
|
||||
AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
|
||||
# find the include directory relative to the javac executable
|
||||
_cur="$1"
|
||||
while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
|
||||
AC_MSG_CHECKING([symlink for $_cur])
|
||||
_slink=`ls -ld "$_cur" | sed 's/.* -> //'`
|
||||
case "$_slink" in
|
||||
/*) _cur="$_slink";;
|
||||
# 'X' avoids triggering unwanted echo options.
|
||||
*) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
|
||||
esac
|
||||
AC_MSG_RESULT([$_cur])
|
||||
done
|
||||
_ACJNI_FOLLOWED="$_cur"
|
||||
])# _ACJNI
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PROG_CC_FOR_BUILD
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro searches for a C compiler that generates native executables,
|
||||
# that is a C compiler that surely is not a cross-compiler. This can be
|
||||
# useful if you have to generate source code at compile-time like for
|
||||
# example GCC does.
|
||||
#
|
||||
# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything
|
||||
# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD).
|
||||
# The value of these variables can be overridden by the user by specifying
|
||||
# a compiler with an environment variable (like you do for standard CC).
|
||||
#
|
||||
# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object
|
||||
# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if
|
||||
# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are
|
||||
# substituted in the Makefile.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Paolo Bonzini <bonzini@gnu.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 8
|
||||
|
||||
AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD])
|
||||
AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl
|
||||
AC_REQUIRE([AC_PROG_CC])dnl
|
||||
AC_REQUIRE([AC_PROG_CPP])dnl
|
||||
AC_REQUIRE([AC_EXEEXT])dnl
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])dnl
|
||||
|
||||
dnl Use the standard macros, but make them use other variable names
|
||||
dnl
|
||||
pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl
|
||||
pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl
|
||||
pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl
|
||||
pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl
|
||||
pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl
|
||||
pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl
|
||||
pushdef([ac_cv_objext], ac_cv_build_objext)dnl
|
||||
pushdef([ac_exeext], ac_build_exeext)dnl
|
||||
pushdef([ac_objext], ac_build_objext)dnl
|
||||
pushdef([CC], CC_FOR_BUILD)dnl
|
||||
pushdef([CPP], CPP_FOR_BUILD)dnl
|
||||
pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl
|
||||
pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl
|
||||
pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl
|
||||
pushdef([host], build)dnl
|
||||
pushdef([host_alias], build_alias)dnl
|
||||
pushdef([host_cpu], build_cpu)dnl
|
||||
pushdef([host_vendor], build_vendor)dnl
|
||||
pushdef([host_os], build_os)dnl
|
||||
pushdef([ac_cv_host], ac_cv_build)dnl
|
||||
pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl
|
||||
pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl
|
||||
pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl
|
||||
pushdef([ac_cv_host_os], ac_cv_build_os)dnl
|
||||
pushdef([ac_cpp], ac_build_cpp)dnl
|
||||
pushdef([ac_compile], ac_build_compile)dnl
|
||||
pushdef([ac_link], ac_build_link)dnl
|
||||
|
||||
save_cross_compiling=$cross_compiling
|
||||
save_ac_tool_prefix=$ac_tool_prefix
|
||||
cross_compiling=no
|
||||
ac_tool_prefix=
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
AC_EXEEXT
|
||||
|
||||
ac_tool_prefix=$save_ac_tool_prefix
|
||||
cross_compiling=$save_cross_compiling
|
||||
|
||||
dnl Restore the old definitions
|
||||
dnl
|
||||
popdef([ac_link])dnl
|
||||
popdef([ac_compile])dnl
|
||||
popdef([ac_cpp])dnl
|
||||
popdef([ac_cv_host_os])dnl
|
||||
popdef([ac_cv_host_vendor])dnl
|
||||
popdef([ac_cv_host_cpu])dnl
|
||||
popdef([ac_cv_host_alias])dnl
|
||||
popdef([ac_cv_host])dnl
|
||||
popdef([host_os])dnl
|
||||
popdef([host_vendor])dnl
|
||||
popdef([host_cpu])dnl
|
||||
popdef([host_alias])dnl
|
||||
popdef([host])dnl
|
||||
popdef([LDFLAGS])dnl
|
||||
popdef([CPPFLAGS])dnl
|
||||
popdef([CFLAGS])dnl
|
||||
popdef([CPP])dnl
|
||||
popdef([CC])dnl
|
||||
popdef([ac_objext])dnl
|
||||
popdef([ac_exeext])dnl
|
||||
popdef([ac_cv_objext])dnl
|
||||
popdef([ac_cv_exeext])dnl
|
||||
popdef([ac_cv_prog_cc_g])dnl
|
||||
popdef([ac_cv_prog_cc_cross])dnl
|
||||
popdef([ac_cv_prog_cc_works])dnl
|
||||
popdef([ac_cv_prog_gcc])dnl
|
||||
popdef([ac_cv_prog_CPP])dnl
|
||||
|
||||
dnl Finally, set Makefile variables
|
||||
dnl
|
||||
BUILD_EXEEXT=$ac_build_exeext
|
||||
BUILD_OBJEXT=$ac_build_objext
|
||||
AC_SUBST(BUILD_EXEEXT)dnl
|
||||
AC_SUBST(BUILD_OBJEXT)dnl
|
||||
AC_SUBST([CFLAGS_FOR_BUILD])dnl
|
||||
AC_SUBST([CPPFLAGS_FOR_BUILD])dnl
|
||||
AC_SUBST([LDFLAGS_FOR_BUILD])dnl
|
||||
])
|
||||
|
|
@ -1,69 +1,91 @@
|
|||
dnl libsecp25k1 helper checks
|
||||
AC_DEFUN([SECP_INT128_CHECK],[
|
||||
has_int128=$ac_cv_type___int128
|
||||
])
|
||||
|
||||
dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell.
|
||||
AC_DEFUN([SECP_64BIT_ASM_CHECK],[
|
||||
AC_DEFUN([SECP_X86_64_ASM_CHECK],[
|
||||
AC_MSG_CHECKING(for x86_64 assembly availability)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <stdint.h>]],[[
|
||||
uint64_t a = 11, tmp;
|
||||
__asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx");
|
||||
]])],[has_64bit_asm=yes],[has_64bit_asm=no])
|
||||
AC_MSG_RESULT([$has_64bit_asm])
|
||||
]])], [has_x86_64_asm=yes], [has_x86_64_asm=no])
|
||||
AC_MSG_RESULT([$has_x86_64_asm])
|
||||
])
|
||||
|
||||
dnl
|
||||
AC_DEFUN([SECP_OPENSSL_CHECK],[
|
||||
has_libcrypto=no
|
||||
m4_ifdef([PKG_CHECK_MODULES],[
|
||||
PKG_CHECK_MODULES([CRYPTO], [libcrypto], [has_libcrypto=yes],[has_libcrypto=no])
|
||||
if test x"$has_libcrypto" = x"yes"; then
|
||||
TEMP_LIBS="$LIBS"
|
||||
LIBS="$LIBS $CRYPTO_LIBS"
|
||||
AC_CHECK_LIB(crypto, main,[AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])],[has_libcrypto=no])
|
||||
LIBS="$TEMP_LIBS"
|
||||
fi
|
||||
])
|
||||
if test x$has_libcrypto = xno; then
|
||||
AC_CHECK_HEADER(openssl/crypto.h,[
|
||||
AC_CHECK_LIB(crypto, main,[
|
||||
has_libcrypto=yes
|
||||
CRYPTO_LIBS=-lcrypto
|
||||
AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])
|
||||
])
|
||||
])
|
||||
LIBS=
|
||||
fi
|
||||
if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
|
||||
AC_MSG_CHECKING(for EC functions in libcrypto)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>]],[[
|
||||
EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
ECDSA_sign(0, NULL, 0, NULL, NULL, eckey);
|
||||
ECDSA_verify(0, NULL, 0, NULL, 0, eckey);
|
||||
EC_KEY_free(eckey);
|
||||
ECDSA_SIG *sig_openssl;
|
||||
sig_openssl = ECDSA_SIG_new();
|
||||
(void)sig_openssl->r;
|
||||
ECDSA_SIG_free(sig_openssl);
|
||||
]])],[has_openssl_ec=yes],[has_openssl_ec=no])
|
||||
AC_MSG_RESULT([$has_openssl_ec])
|
||||
fi
|
||||
AC_DEFUN([SECP_ARM32_ASM_CHECK], [
|
||||
AC_MSG_CHECKING(for ARM32 assembly availability)
|
||||
SECP_ARM32_ASM_CHECK_CFLAGS_saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="-x assembler"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[
|
||||
.syntax unified
|
||||
.eabi_attribute 24, 1
|
||||
.eabi_attribute 25, 1
|
||||
.text
|
||||
.global main
|
||||
main:
|
||||
ldr r0, =0x002A
|
||||
mov r7, #1
|
||||
swi 0
|
||||
]])], [has_arm32_asm=yes], [has_arm32_asm=no])
|
||||
AC_MSG_RESULT([$has_arm32_asm])
|
||||
CFLAGS="$SECP_ARM32_ASM_CHECK_CFLAGS_saved_CFLAGS"
|
||||
])
|
||||
|
||||
dnl
|
||||
AC_DEFUN([SECP_GMP_CHECK],[
|
||||
if test x"$has_gmp" != x"yes"; then
|
||||
AC_DEFUN([SECP_VALGRIND_CHECK],[
|
||||
AC_MSG_CHECKING([for valgrind support])
|
||||
if test x"$has_valgrind" != x"yes"; then
|
||||
CPPFLAGS_TEMP="$CPPFLAGS"
|
||||
CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS"
|
||||
LIBS_TEMP="$LIBS"
|
||||
LIBS="$GMP_LIBS $LIBS"
|
||||
AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])])
|
||||
CPPFLAGS="$VALGRIND_CPPFLAGS $CPPFLAGS"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <valgrind/memcheck.h>
|
||||
]], [[
|
||||
#if defined(NVALGRIND)
|
||||
# error "Valgrind does not support this platform."
|
||||
#endif
|
||||
]])], [has_valgrind=yes])
|
||||
CPPFLAGS="$CPPFLAGS_TEMP"
|
||||
LIBS="$LIBS_TEMP"
|
||||
fi
|
||||
AC_MSG_RESULT($has_valgrind)
|
||||
])
|
||||
|
||||
AC_DEFUN([SECP_MSAN_CHECK], [
|
||||
AC_MSG_CHECKING(whether MemorySanitizer is enabled)
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(memory_sanitizer)
|
||||
/* MemorySanitizer is enabled. */
|
||||
# elif
|
||||
# error "MemorySanitizer is disabled."
|
||||
# endif
|
||||
#else
|
||||
# error "__has_feature is not defined."
|
||||
#endif
|
||||
]])], [msan_enabled=yes], [msan_enabled=no])
|
||||
AC_MSG_RESULT([$msan_enabled])
|
||||
])
|
||||
|
||||
dnl SECP_TRY_APPEND_CFLAGS(flags, VAR)
|
||||
dnl Append flags to VAR if CC accepts them.
|
||||
AC_DEFUN([SECP_TRY_APPEND_CFLAGS], [
|
||||
AC_MSG_CHECKING([if ${CC} supports $1])
|
||||
SECP_TRY_APPEND_CFLAGS_saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$1 $CFLAGS"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], [flag_works=yes], [flag_works=no])
|
||||
AC_MSG_RESULT($flag_works)
|
||||
CFLAGS="$SECP_TRY_APPEND_CFLAGS_saved_CFLAGS"
|
||||
if test x"$flag_works" = x"yes"; then
|
||||
$2="$$2 $1"
|
||||
fi
|
||||
unset flag_works
|
||||
AC_SUBST($2)
|
||||
])
|
||||
|
||||
dnl SECP_SET_DEFAULT(VAR, default, default-dev-mode)
|
||||
dnl Set VAR to default or default-dev-mode, depending on whether dev mode is enabled
|
||||
AC_DEFUN([SECP_SET_DEFAULT], [
|
||||
if test "${enable_dev_mode+set}" != set; then
|
||||
AC_MSG_ERROR([[Set enable_dev_mode before calling SECP_SET_DEFAULT]])
|
||||
fi
|
||||
if test x"$enable_dev_mode" = x"yes"; then
|
||||
$1="$3"
|
||||
else
|
||||
$1="$2"
|
||||
fi
|
||||
])
|
||||
|
|
|
|||
149
crypto/secp256k1/libsecp256k1/ci/ci.sh
Executable file
149
crypto/secp256k1/libsecp256k1/ci/ci.sh
Executable file
|
|
@ -0,0 +1,149 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eux
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
# Print commit and relevant CI environment to allow reproducing the job outside of CI.
|
||||
git show --no-patch
|
||||
print_environment() {
|
||||
# Turn off -x because it messes up the output
|
||||
set +x
|
||||
# There are many ways to print variable names and their content. This one
|
||||
# does not rely on bash.
|
||||
for var in WERROR_CFLAGS MAKEFLAGS BUILD \
|
||||
ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \
|
||||
EXPERIMENTAL ECDH RECOVERY EXTRAKEYS MUSIG SCHNORRSIG ELLSWIFT \
|
||||
SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\
|
||||
EXAMPLES \
|
||||
HOST WRAPPER_CMD \
|
||||
CC CFLAGS CPPFLAGS AR NM \
|
||||
UBSAN_OPTIONS ASAN_OPTIONS LSAN_OPTIONS
|
||||
do
|
||||
eval "isset=\${$var+x}"
|
||||
if [ -n "$isset" ]; then
|
||||
eval "val=\${$var}"
|
||||
# shellcheck disable=SC2154
|
||||
printf '%s="%s" ' "$var" "$val"
|
||||
fi
|
||||
done
|
||||
echo "$0"
|
||||
set -x
|
||||
}
|
||||
print_environment
|
||||
|
||||
env >> test_env.log
|
||||
|
||||
# If gcc is requested, assert that it's in fact gcc (and not some symlinked Apple clang).
|
||||
case "${CC:-undefined}" in
|
||||
*gcc*)
|
||||
$CC -v 2>&1 | grep -q "gcc version" || exit 1;
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "${CC+x}" ]; then
|
||||
# The MSVC compiler "cl" doesn't understand "-v"
|
||||
$CC -v || true
|
||||
fi
|
||||
if [ "$WITH_VALGRIND" = "yes" ]; then
|
||||
valgrind --version
|
||||
fi
|
||||
if [ -n "$WRAPPER_CMD" ]; then
|
||||
$WRAPPER_CMD --version
|
||||
fi
|
||||
|
||||
# Workaround for https://bugs.kde.org/show_bug.cgi?id=452758 (fixed in valgrind 3.20.0).
|
||||
case "${CC:-undefined}" in
|
||||
clang*)
|
||||
if [ "$CTIMETESTS" = "yes" ] && [ "$WITH_VALGRIND" = "yes" ]
|
||||
then
|
||||
export CFLAGS="${CFLAGS:+$CFLAGS }-gdwarf-4"
|
||||
else
|
||||
case "$WRAPPER_CMD" in
|
||||
valgrind*)
|
||||
export CFLAGS="${CFLAGS:+$CFLAGS }-gdwarf-4"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
./autogen.sh
|
||||
|
||||
./configure \
|
||||
--enable-experimental="$EXPERIMENTAL" \
|
||||
--with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \
|
||||
--with-ecmult-window="$ECMULTWINDOW" \
|
||||
--with-ecmult-gen-kb="$ECMULTGENKB" \
|
||||
--enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
|
||||
--enable-module-ellswift="$ELLSWIFT" \
|
||||
--enable-module-extrakeys="$EXTRAKEYS" \
|
||||
--enable-module-schnorrsig="$SCHNORRSIG" \
|
||||
--enable-module-musig="$MUSIG" \
|
||||
--enable-examples="$EXAMPLES" \
|
||||
--enable-ctime-tests="$CTIMETESTS" \
|
||||
--with-valgrind="$WITH_VALGRIND" \
|
||||
--host="$HOST" $EXTRAFLAGS
|
||||
|
||||
# We have set "-j<n>" in MAKEFLAGS.
|
||||
build_exit_code=0
|
||||
make > make.log 2>&1 || build_exit_code=$?
|
||||
cat make.log
|
||||
if [ $build_exit_code -ne 0 ]; then
|
||||
case "${CC:-undefined}" in
|
||||
*snapshot*)
|
||||
# Ignore internal compiler errors in gcc-snapshot and clang-snapshot
|
||||
grep -e "internal compiler error:" -e "PLEASE submit a bug report" make.log
|
||||
return $?;
|
||||
;;
|
||||
*)
|
||||
return 1;
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Print information about binaries so that we can see that the architecture is correct
|
||||
file *tests* || true
|
||||
file bench* || true
|
||||
file .libs/* || true
|
||||
|
||||
# This tells `make check` to wrap test invocations.
|
||||
export LOG_COMPILER="$WRAPPER_CMD"
|
||||
|
||||
make "$BUILD"
|
||||
|
||||
# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
|
||||
EXEC='./libtool --mode=execute'
|
||||
if [ -n "$WRAPPER_CMD" ]
|
||||
then
|
||||
EXEC="$EXEC $WRAPPER_CMD"
|
||||
fi
|
||||
|
||||
if [ "$BENCH" = "yes" ]
|
||||
then
|
||||
{
|
||||
$EXEC ./bench_ecmult
|
||||
$EXEC ./bench_internal
|
||||
$EXEC ./bench
|
||||
} >> bench.log 2>&1
|
||||
fi
|
||||
|
||||
if [ "$CTIMETESTS" = "yes" ]
|
||||
then
|
||||
if [ "$WITH_VALGRIND" = "yes" ]; then
|
||||
./libtool --mode=execute valgrind --error-exitcode=42 ./ctime_tests > ctime_tests.log 2>&1
|
||||
else
|
||||
$EXEC ./ctime_tests > ctime_tests.log 2>&1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Rebuild precomputed files (if not cross-compiling).
|
||||
if [ -z "$HOST" ]
|
||||
then
|
||||
make clean-precomp clean-testvectors
|
||||
make precomp testvectors
|
||||
fi
|
||||
|
||||
# Check that no repo files have been modified by the build.
|
||||
# (This fails for example if the precomp files need to be updated in the repo.)
|
||||
git diff --exit-code
|
||||
79
crypto/secp256k1/libsecp256k1/ci/linux-debian.Dockerfile
Normal file
79
crypto/secp256k1/libsecp256k1/ci/linux-debian.Dockerfile
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
FROM debian:stable-slim
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
WORKDIR /root
|
||||
|
||||
# A too high maximum number of file descriptors (with the default value
|
||||
# inherited from the docker host) can cause issues with some of our tools:
|
||||
# - sanitizers hanging: https://github.com/google/sanitizers/issues/1662
|
||||
# - valgrind crashing: https://stackoverflow.com/a/75293014
|
||||
# This is not be a problem on our CI hosts, but developers who run the image
|
||||
# on their machines may run into this (e.g., on Arch Linux), so warn them.
|
||||
# (Note that .bashrc is only executed in interactive bash shells.)
|
||||
RUN echo 'if [[ $(ulimit -n) -gt 200000 ]]; then echo "WARNING: Very high value reported by \"ulimit -n\". Consider passing \"--ulimit nofile=32768\" to \"docker run\"."; fi' >> /root/.bashrc
|
||||
|
||||
RUN dpkg --add-architecture i386 && \
|
||||
dpkg --add-architecture s390x && \
|
||||
dpkg --add-architecture armhf && \
|
||||
dpkg --add-architecture arm64 && \
|
||||
dpkg --add-architecture ppc64el
|
||||
|
||||
# dkpg-dev: to make pkg-config work in cross-builds
|
||||
# llvm: for llvm-symbolizer, which is used by clang's UBSan for symbolized stack traces
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
git ca-certificates \
|
||||
make automake libtool pkg-config dpkg-dev valgrind qemu-user \
|
||||
gcc clang llvm libclang-rt-dev libc6-dbg \
|
||||
g++ \
|
||||
gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 libubsan1:i386 libasan8:i386 \
|
||||
gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \
|
||||
gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \
|
||||
gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \
|
||||
gcc-mingw-w64-x86-64-win32 wine64 wine \
|
||||
gcc-mingw-w64-i686-win32 wine32 \
|
||||
python3 && \
|
||||
if ! ( dpkg --print-architecture | grep --quiet "arm64" ) ; then \
|
||||
apt-get install --no-install-recommends -y \
|
||||
gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 ;\
|
||||
fi && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Build and install gcc snapshot
|
||||
ARG GCC_SNAPSHOT_MAJOR=15
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y wget libgmp-dev libmpfr-dev libmpc-dev flex && \
|
||||
mkdir gcc && cd gcc && \
|
||||
wget --progress=dot:giga --https-only --recursive --accept '*.tar.xz' --level 1 --no-directories "https://gcc.gnu.org/pub/gcc/snapshots/LATEST-${GCC_SNAPSHOT_MAJOR}" && \
|
||||
wget "https://gcc.gnu.org/pub/gcc/snapshots/LATEST-${GCC_SNAPSHOT_MAJOR}/sha512.sum" && \
|
||||
sha512sum --check --ignore-missing sha512.sum && \
|
||||
# We should have downloaded exactly one tar.xz file
|
||||
ls && \
|
||||
[ $(ls *.tar.xz | wc -l) -eq "1" ] && \
|
||||
tar xf *.tar.xz && \
|
||||
mkdir gcc-build && cd gcc-build && \
|
||||
../*/configure --prefix=/opt/gcc-snapshot --enable-languages=c --disable-bootstrap --disable-multilib --without-isl && \
|
||||
make -j $(nproc) && \
|
||||
make install && \
|
||||
cd ../.. && rm -rf gcc && \
|
||||
ln -s /opt/gcc-snapshot/bin/gcc /usr/bin/gcc-snapshot && \
|
||||
apt-get autoremove -y wget libgmp-dev libmpfr-dev libmpc-dev flex && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install clang snapshot, see https://apt.llvm.org/
|
||||
RUN \
|
||||
# Setup GPG keys of LLVM repository
|
||||
apt-get update && apt-get install --no-install-recommends -y wget && \
|
||||
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && \
|
||||
# Add repository for this Debian release
|
||||
. /etc/os-release && echo "deb http://apt.llvm.org/${VERSION_CODENAME} llvm-toolchain-${VERSION_CODENAME} main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
# Determine the version number of the LLVM development branch
|
||||
LLVM_VERSION=$(apt-cache search --names-only '^clang-[0-9]+$' | sort -V | tail -1 | cut -f1 -d" " | cut -f2 -d"-" ) && \
|
||||
# Install
|
||||
apt-get install --no-install-recommends -y "clang-${LLVM_VERSION}" && \
|
||||
# Create symlink
|
||||
ln -s "/usr/bin/clang-${LLVM_VERSION}" /usr/bin/clang-snapshot && \
|
||||
# Clean up
|
||||
apt-get autoremove -y wget && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
function(check_arm32_assembly)
|
||||
try_compile(HAVE_ARM32_ASM
|
||||
${PROJECT_BINARY_DIR}/check_arm32_assembly
|
||||
SOURCES ${PROJECT_SOURCE_DIR}/cmake/source_arm32.s
|
||||
)
|
||||
endfunction()
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
include_guard(GLOBAL)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
function(check_memory_sanitizer output)
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
check_c_source_compiles("
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(memory_sanitizer)
|
||||
/* MemorySanitizer is enabled. */
|
||||
# elif
|
||||
# error \"MemorySanitizer is disabled.\"
|
||||
# endif
|
||||
#else
|
||||
# error \"__has_feature is not defined.\"
|
||||
#endif
|
||||
" HAVE_MSAN)
|
||||
set(${output} ${HAVE_MSAN} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
function(check_string_option_value option)
|
||||
get_property(expected_values CACHE ${option} PROPERTY STRINGS)
|
||||
if(expected_values)
|
||||
if(${option} IN_LIST expected_values)
|
||||
return()
|
||||
endif()
|
||||
message(FATAL_ERROR "${option} value is \"${${option}}\", but must be one of ${expected_values}.")
|
||||
endif()
|
||||
message(AUTHOR_WARNING "The STRINGS property must be set before invoking `check_string_option_value' function.")
|
||||
endfunction()
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
include(CheckCSourceCompiles)
|
||||
|
||||
function(check_x86_64_assembly)
|
||||
check_c_source_compiles("
|
||||
#include <stdint.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
uint64_t a = 11, tmp;
|
||||
__asm__ __volatile__(\"movq $0x100000000,%1; mulq %%rsi\" : \"+a\"(a) : \"S\"(tmp) : \"cc\", \"%rdx\");
|
||||
}
|
||||
" HAVE_X86_64_ASM)
|
||||
set(HAVE_X86_64_ASM ${HAVE_X86_64_ASM} PARENT_SCOPE)
|
||||
endfunction()
|
||||
41
crypto/secp256k1/libsecp256k1/cmake/FindValgrind.cmake
Normal file
41
crypto/secp256k1/libsecp256k1/cmake/FindValgrind.cmake
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
if(CMAKE_HOST_APPLE)
|
||||
find_program(BREW_COMMAND brew)
|
||||
execute_process(
|
||||
COMMAND ${BREW_COMMAND} --prefix valgrind
|
||||
OUTPUT_VARIABLE valgrind_brew_prefix
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endif()
|
||||
|
||||
set(hints_paths)
|
||||
if(valgrind_brew_prefix)
|
||||
set(hints_paths ${valgrind_brew_prefix}/include)
|
||||
endif()
|
||||
|
||||
find_path(Valgrind_INCLUDE_DIR
|
||||
NAMES valgrind/memcheck.h
|
||||
HINTS ${hints_paths}
|
||||
)
|
||||
|
||||
if(Valgrind_INCLUDE_DIR)
|
||||
include(CheckCSourceCompiles)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${Valgrind_INCLUDE_DIR})
|
||||
check_c_source_compiles("
|
||||
#include <valgrind/memcheck.h>
|
||||
#if defined(NVALGRIND)
|
||||
# error \"Valgrind does not support this platform.\"
|
||||
#endif
|
||||
|
||||
int main() {}
|
||||
" Valgrind_WORKS)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Valgrind
|
||||
REQUIRED_VARS Valgrind_INCLUDE_DIR Valgrind_WORKS
|
||||
)
|
||||
|
||||
mark_as_advanced(
|
||||
Valgrind_INCLUDE_DIR
|
||||
)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
function(generate_pkg_config_file in_file)
|
||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
set(exec_prefix \${prefix})
|
||||
set(libdir \${exec_prefix}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(includedir \${prefix}/${CMAKE_INSTALL_INCLUDEDIR})
|
||||
set(PACKAGE_VERSION ${PROJECT_VERSION})
|
||||
configure_file(${in_file} ${PROJECT_NAME}.pc @ONLY)
|
||||
endfunction()
|
||||
24
crypto/secp256k1/libsecp256k1/cmake/TryAppendCFlags.cmake
Normal file
24
crypto/secp256k1/libsecp256k1/cmake/TryAppendCFlags.cmake
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
include(CheckCCompilerFlag)
|
||||
|
||||
function(secp256k1_check_c_flags_internal flags output)
|
||||
string(MAKE_C_IDENTIFIER "${flags}" result)
|
||||
string(TOUPPER "${result}" result)
|
||||
set(result "C_SUPPORTS_${result}")
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-Werror")
|
||||
endif()
|
||||
|
||||
# This avoids running a linker.
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
check_c_compiler_flag("${flags}" ${result})
|
||||
|
||||
set(${output} ${${result}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Append flags to the COMPILE_OPTIONS directory property if CC accepts them.
|
||||
macro(try_append_c_flags)
|
||||
secp256k1_check_c_flags_internal("${ARGV}" result)
|
||||
if(result)
|
||||
add_compile_options(${ARGV})
|
||||
endif()
|
||||
endmacro()
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
|
||||
5
crypto/secp256k1/libsecp256k1/cmake/config.cmake.in
Normal file
5
crypto/secp256k1/libsecp256k1/cmake/config.cmake.in
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
|
||||
|
||||
check_required_components(@PROJECT_NAME@)
|
||||
9
crypto/secp256k1/libsecp256k1/cmake/source_arm32.s
Normal file
9
crypto/secp256k1/libsecp256k1/cmake/source_arm32.s
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.syntax unified
|
||||
.eabi_attribute 24, 1
|
||||
.eabi_attribute 25, 1
|
||||
.text
|
||||
.global main
|
||||
main:
|
||||
ldr r0, =0x002A
|
||||
mov r7, #1
|
||||
swi 0
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
|
||||
|
|
@ -1,203 +1,290 @@
|
|||
AC_PREREQ([2.60])
|
||||
AC_INIT([libsecp256k1],[0.1])
|
||||
|
||||
# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of
|
||||
# the API. All changes in experimental modules are treated as
|
||||
# backwards-compatible and therefore at most increase the minor version.
|
||||
define(_PKG_VERSION_MAJOR, 0)
|
||||
define(_PKG_VERSION_MINOR, 6)
|
||||
define(_PKG_VERSION_PATCH, 1)
|
||||
define(_PKG_VERSION_IS_RELEASE, false)
|
||||
|
||||
# The library version is based on libtool versioning of the ABI. The set of
|
||||
# rules for updating the version can be found here:
|
||||
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
# All changes in experimental modules are treated as if they don't affect the
|
||||
# interface and therefore only increase the revision.
|
||||
define(_LIB_VERSION_CURRENT, 5)
|
||||
define(_LIB_VERSION_REVISION, 1)
|
||||
define(_LIB_VERSION_AGE, 0)
|
||||
|
||||
AC_INIT([libsecp256k1],m4_join([.], _PKG_VERSION_MAJOR, _PKG_VERSION_MINOR, _PKG_VERSION_PATCH)m4_if(_PKG_VERSION_IS_RELEASE, [true], [], [-dev]),[https://github.com/bitcoin-core/secp256k1/issues],[libsecp256k1],[https://github.com/bitcoin-core/secp256k1])
|
||||
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([build-aux/m4])
|
||||
AC_CANONICAL_HOST
|
||||
AH_TOP([#ifndef LIBSECP256K1_CONFIG_H])
|
||||
AH_TOP([#define LIBSECP256K1_CONFIG_H])
|
||||
AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
LT_INIT
|
||||
|
||||
dnl make the compilation flags quiet unless V=1 is used
|
||||
# Require Automake 1.11.2 for AM_PROG_AR
|
||||
AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects])
|
||||
|
||||
# Make the compilation flags quiet unless V=1 is used.
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
AC_PATH_TOOL(AR, ar)
|
||||
AC_PATH_TOOL(RANLIB, ranlib)
|
||||
AC_PATH_TOOL(STRIP, strip)
|
||||
AX_PROG_CC_FOR_BUILD
|
||||
|
||||
if test "x$CFLAGS" = "x"; then
|
||||
CFLAGS="-g"
|
||||
fi
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
AC_PROG_CC_C89
|
||||
if test x"$ac_cv_prog_cc_c89" = x"no"; then
|
||||
AC_MSG_ERROR([c89 compiler support required])
|
||||
if test "${CFLAGS+set}" = "set"; then
|
||||
CFLAGS_overridden=yes
|
||||
else
|
||||
CFLAGS_overridden=no
|
||||
fi
|
||||
AC_PROG_CC
|
||||
AM_PROG_AS
|
||||
AM_PROG_AR
|
||||
|
||||
# Clear some cache variables as a workaround for a bug that appears due to a bad
|
||||
# interaction between AM_PROG_AR and LT_INIT when combining MSVC's archiver lib.exe.
|
||||
# https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54421
|
||||
AS_UNSET(ac_cv_prog_AR)
|
||||
AS_UNSET(ac_cv_prog_ac_ct_AR)
|
||||
LT_INIT([win32-dll])
|
||||
|
||||
build_windows=no
|
||||
|
||||
case $host_os in
|
||||
*darwin*)
|
||||
if test x$cross_compiling != xyes; then
|
||||
AC_PATH_PROG([BREW],brew,)
|
||||
if test x$BREW != x; then
|
||||
dnl These Homebrew packages may be keg-only, meaning that they won't be found
|
||||
dnl in expected paths because they may conflict with system files. Ask
|
||||
dnl Homebrew where each one is located, then adjust paths accordingly.
|
||||
|
||||
openssl_prefix=`$BREW --prefix openssl 2>/dev/null`
|
||||
gmp_prefix=`$BREW --prefix gmp 2>/dev/null`
|
||||
if test x$openssl_prefix != x; then
|
||||
PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export PKG_CONFIG_PATH
|
||||
fi
|
||||
if test x$gmp_prefix != x; then
|
||||
GMP_CPPFLAGS="-I$gmp_prefix/include"
|
||||
GMP_LIBS="-L$gmp_prefix/lib"
|
||||
AC_CHECK_PROG([BREW], brew, brew)
|
||||
if test x$BREW = xbrew; then
|
||||
# These Homebrew packages may be keg-only, meaning that they won't be found
|
||||
# in expected paths because they may conflict with system files. Ask
|
||||
# Homebrew where each one is located, then adjust paths accordingly.
|
||||
if $BREW list --versions valgrind >/dev/null; then
|
||||
valgrind_prefix=$($BREW --prefix valgrind 2>/dev/null)
|
||||
VALGRIND_CPPFLAGS="-I$valgrind_prefix/include"
|
||||
fi
|
||||
else
|
||||
AC_PATH_PROG([PORT],port,)
|
||||
dnl if homebrew isn't installed and macports is, add the macports default paths
|
||||
dnl as a last resort.
|
||||
if test x$PORT != x; then
|
||||
AC_CHECK_PROG([PORT], port, port)
|
||||
# If homebrew isn't installed and macports is, add the macports default paths
|
||||
# as a last resort.
|
||||
if test x$PORT = xport; then
|
||||
CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"
|
||||
LDFLAGS="$LDFLAGS -L/opt/local/lib"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
cygwin*|mingw*)
|
||||
build_windows=yes
|
||||
;;
|
||||
esac
|
||||
|
||||
CFLAGS="$CFLAGS -W"
|
||||
# Try if some desirable compiler flags are supported and append them to SECP_CFLAGS.
|
||||
#
|
||||
# These are our own flags, so we append them to our own SECP_CFLAGS variable (instead of CFLAGS) as
|
||||
# recommended in the automake manual (Section "Flag Variables Ordering"). CFLAGS belongs to the user
|
||||
# and we are not supposed to touch it. In the Makefile, we will need to ensure that SECP_CFLAGS
|
||||
# is prepended to CFLAGS when invoking the compiler so that the user always has the last word (flag).
|
||||
#
|
||||
# Another advantage of not touching CFLAGS is that the contents of CFLAGS will be picked up by
|
||||
# libtool for compiling helper executables. For example, when compiling for Windows, libtool will
|
||||
# generate entire wrapper executables (instead of simple wrapper scripts as on Unix) to ensure
|
||||
# proper operation of uninstalled programs linked by libtool against the uninstalled shared library.
|
||||
# These executables are compiled from C source file for which our flags may not be appropriate,
|
||||
# e.g., -std=c89 flag has lead to undesirable warnings in the past.
|
||||
#
|
||||
# TODO We should analogously not touch CPPFLAGS and LDFLAGS but currently there are no issues.
|
||||
AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [
|
||||
# GCC and compatible (incl. clang)
|
||||
if test "x$GCC" = "xyes"; then
|
||||
# Try to append -Werror to CFLAGS temporarily. Otherwise checks for some unsupported
|
||||
# flags will succeed.
|
||||
# Note that failure to append -Werror does not necessarily mean that -Werror is not
|
||||
# supported. The compiler may already be warning about something unrelated, for example
|
||||
# about some path issue. If that is the case, -Werror cannot be used because all
|
||||
# of those warnings would be turned into errors.
|
||||
SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS"
|
||||
SECP_TRY_APPEND_CFLAGS([-Werror], CFLAGS)
|
||||
|
||||
warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $warn_CFLAGS"
|
||||
AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
|
||||
[ AC_MSG_RESULT([yes]) ],
|
||||
[ AC_MSG_RESULT([no])
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
])
|
||||
SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic.
|
||||
SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic.
|
||||
SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers
|
||||
SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall.
|
||||
SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions.
|
||||
SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95
|
||||
SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0
|
||||
SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only
|
||||
SECP_TRY_APPEND_CFLAGS([-Wreserved-identifier], $1) # Clang >= 13.0 only
|
||||
SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0
|
||||
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fvisibility=hidden"
|
||||
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
|
||||
[ AC_MSG_RESULT([yes]) ],
|
||||
[ AC_MSG_RESULT([no])
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
])
|
||||
CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS"
|
||||
fi
|
||||
|
||||
# MSVC
|
||||
# Assume MSVC if we're building for Windows but not with GCC or compatible;
|
||||
# libtool makes the same assumption internally.
|
||||
# Note that "/opt" and "-opt" are equivalent for MSVC; we use "-opt" because "/opt" looks like a path.
|
||||
if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then
|
||||
SECP_TRY_APPEND_CFLAGS([-W3], $1) # Production quality warning level.
|
||||
SECP_TRY_APPEND_CFLAGS([-wd4146], $1) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned".
|
||||
SECP_TRY_APPEND_CFLAGS([-wd4244], $1) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data".
|
||||
SECP_TRY_APPEND_CFLAGS([-wd4267], $1) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data".
|
||||
# Eliminate deprecation warnings for the older, less secure functions.
|
||||
CPPFLAGS="-D_CRT_SECURE_NO_WARNINGS $CPPFLAGS"
|
||||
fi
|
||||
])
|
||||
SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS)
|
||||
|
||||
###
|
||||
### Define config arguments
|
||||
###
|
||||
|
||||
# In dev mode, we enable all binaries and modules by default but individual options can still be overridden explicitly.
|
||||
# Check for dev mode first because SECP_SET_DEFAULT needs enable_dev_mode set.
|
||||
AC_ARG_ENABLE(dev_mode, [], [],
|
||||
[enable_dev_mode=no])
|
||||
|
||||
AC_ARG_ENABLE(benchmark,
|
||||
AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]),
|
||||
[use_benchmark=$enableval],
|
||||
[use_benchmark=no])
|
||||
AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_benchmark], [yes], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(coverage,
|
||||
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]),
|
||||
[enable_coverage=$enableval],
|
||||
[enable_coverage=no])
|
||||
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]), [],
|
||||
[SECP_SET_DEFAULT([enable_coverage], [no], [no])])
|
||||
|
||||
AC_ARG_ENABLE(tests,
|
||||
AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]),
|
||||
[use_tests=$enableval],
|
||||
[use_tests=yes])
|
||||
AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_tests], [yes], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(openssl_tests,
|
||||
AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]),
|
||||
[enable_openssl_tests=$enableval],
|
||||
[enable_openssl_tests=auto])
|
||||
AC_ARG_ENABLE(ctime_tests,
|
||||
AS_HELP_STRING([--enable-ctime-tests],[compile constant-time tests [default=yes if valgrind enabled]]), [],
|
||||
[SECP_SET_DEFAULT([enable_ctime_tests], [auto], [auto])])
|
||||
|
||||
AC_ARG_ENABLE(experimental,
|
||||
AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]),
|
||||
[use_experimental=$enableval],
|
||||
[use_experimental=no])
|
||||
AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [],
|
||||
[SECP_SET_DEFAULT([enable_experimental], [no], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(exhaustive_tests,
|
||||
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
|
||||
[use_exhaustive_tests=$enableval],
|
||||
[use_exhaustive_tests=yes])
|
||||
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_exhaustive_tests], [yes], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(endomorphism,
|
||||
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
|
||||
[use_endomorphism=$enableval],
|
||||
[use_endomorphism=no])
|
||||
|
||||
AC_ARG_ENABLE(ecmult_static_precomputation,
|
||||
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]),
|
||||
[use_ecmult_static_precomputation=$enableval],
|
||||
[use_ecmult_static_precomputation=auto])
|
||||
AC_ARG_ENABLE(examples,
|
||||
AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [],
|
||||
[SECP_SET_DEFAULT([enable_examples], [no], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(module_ecdh,
|
||||
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]),
|
||||
[enable_module_ecdh=$enableval],
|
||||
[enable_module_ecdh=no])
|
||||
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_ecdh], [yes], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(module_recovery,
|
||||
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
|
||||
[enable_module_recovery=$enableval],
|
||||
[enable_module_recovery=no])
|
||||
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_recovery], [no], [yes])])
|
||||
|
||||
AC_ARG_ENABLE(jni,
|
||||
AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]),
|
||||
[use_jni=$enableval],
|
||||
[use_jni=auto])
|
||||
AC_ARG_ENABLE(module_extrakeys,
|
||||
AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_extrakeys], [yes], [yes])])
|
||||
|
||||
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
|
||||
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
|
||||
AC_ARG_ENABLE(module_schnorrsig,
|
||||
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_schnorrsig], [yes], [yes])])
|
||||
|
||||
AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
|
||||
[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto])
|
||||
AC_ARG_ENABLE(module_musig,
|
||||
AS_HELP_STRING([--enable-module-musig],[enable MuSig2 module [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_musig], [yes], [yes])])
|
||||
|
||||
AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
|
||||
[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto])
|
||||
AC_ARG_ENABLE(module_ellswift,
|
||||
AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [],
|
||||
[SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])])
|
||||
|
||||
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto]
|
||||
[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto])
|
||||
AC_ARG_ENABLE(external_default_callbacks,
|
||||
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [],
|
||||
[SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])])
|
||||
|
||||
AC_CHECK_TYPES([__int128])
|
||||
# Test-only override of the (autodetected by the C code) "widemul" setting.
|
||||
# Legal values are:
|
||||
# * int64 (for [u]int64_t),
|
||||
# * int128 (for [unsigned] __int128),
|
||||
# * int128_struct (for int128 implemented as a structure),
|
||||
# * and auto (the default).
|
||||
AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
|
||||
|
||||
AC_MSG_CHECKING([for __builtin_expect])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
|
||||
[ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ],
|
||||
[ AC_MSG_RESULT([no])
|
||||
])
|
||||
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm32|no|auto],
|
||||
[assembly to use (experimental: arm32) [default=auto]])],[req_asm=$withval], [req_asm=auto])
|
||||
|
||||
if test x"$enable_coverage" = x"yes"; then
|
||||
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
|
||||
CFLAGS="$CFLAGS -O0 --coverage"
|
||||
LDFLAGS="--coverage"
|
||||
AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE],
|
||||
[window size for ecmult precomputation for verification, specified as integer in range [2..24].]
|
||||
[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.]
|
||||
[The table will store 2^(SIZE-1) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.]
|
||||
[A window size larger than 15 will require you delete the prebuilt precomputed_ecmult.c file so that it can be rebuilt.]
|
||||
[For very large window sizes, use "make -j 1" to reduce memory use during compilation.]
|
||||
[The default value is a reasonable setting for desktop machines (currently 15). [default=15]]
|
||||
)],
|
||||
[set_ecmult_window=$withval], [set_ecmult_window=15])
|
||||
|
||||
AC_ARG_WITH([ecmult-gen-kb], [AS_HELP_STRING([--with-ecmult-gen-kb=2|22|86],
|
||||
[The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms).]
|
||||
[Larger values result in possibly better signing/keygeneration performance at the cost of a larger table.]
|
||||
[The default value is a reasonable setting for desktop machines (currently 86). [default=86]]
|
||||
)],
|
||||
[set_ecmult_gen_kb=$withval], [set_ecmult_gen_kb=86])
|
||||
|
||||
AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto],
|
||||
[Build with extra checks for running inside Valgrind [default=auto]]
|
||||
)],
|
||||
[req_valgrind=$withval], [req_valgrind=auto])
|
||||
|
||||
###
|
||||
### Handle config options (except for modules)
|
||||
###
|
||||
|
||||
if test x"$req_valgrind" = x"no"; then
|
||||
enable_valgrind=no
|
||||
else
|
||||
CFLAGS="$CFLAGS -O3"
|
||||
SECP_VALGRIND_CHECK
|
||||
if test x"$has_valgrind" != x"yes"; then
|
||||
if test x"$req_valgrind" = x"yes"; then
|
||||
AC_MSG_ERROR([Valgrind support explicitly requested but valgrind/memcheck.h header not available])
|
||||
fi
|
||||
enable_valgrind=no
|
||||
else
|
||||
enable_valgrind=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$use_ecmult_static_precomputation" != x"no"; then
|
||||
save_cross_compiling=$cross_compiling
|
||||
cross_compiling=no
|
||||
TEMP_CC="$CC"
|
||||
CC="$CC_FOR_BUILD"
|
||||
AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}])
|
||||
AC_RUN_IFELSE(
|
||||
[AC_LANG_PROGRAM([], [return 0])],
|
||||
[working_native_cc=yes],
|
||||
[working_native_cc=no],[dnl])
|
||||
CC="$TEMP_CC"
|
||||
cross_compiling=$save_cross_compiling
|
||||
if test x"$enable_ctime_tests" = x"auto"; then
|
||||
enable_ctime_tests=$enable_valgrind
|
||||
fi
|
||||
|
||||
if test x"$working_native_cc" = x"no"; then
|
||||
set_precomp=no
|
||||
if test x"$use_ecmult_static_precomputation" = x"yes"; then
|
||||
AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
|
||||
else
|
||||
AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT([ok])
|
||||
set_precomp=yes
|
||||
print_msan_notice=no
|
||||
if test x"$enable_ctime_tests" = x"yes"; then
|
||||
SECP_MSAN_CHECK
|
||||
# MSan on Clang >=16 reports uninitialized memory in function parameters and return values, even if
|
||||
# the uninitialized variable is never actually "used". This is called "eager" checking, and it's
|
||||
# sounds like good idea for normal use of MSan. However, it yields many false positives in the
|
||||
# ctime_tests because many return values depend on secret (i.e., "uninitialized") values, and
|
||||
# we're only interested in detecting branches (which count as "uses") on secret data.
|
||||
if test x"$msan_enabled" = x"yes"; then
|
||||
SECP_TRY_APPEND_CFLAGS([-fno-sanitize-memory-param-retval], SECP_CFLAGS)
|
||||
print_msan_notice=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$enable_coverage" = x"yes"; then
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOVERAGE=1"
|
||||
SECP_CFLAGS="-O0 --coverage $SECP_CFLAGS"
|
||||
# If coverage is enabled, and the user has not overridden CFLAGS,
|
||||
# override Autoconf's value "-g -O2" with "-g". Otherwise we'd end up
|
||||
# with "-O0 --coverage -g -O2".
|
||||
if test "$CFLAGS_overridden" = "no"; then
|
||||
CFLAGS="-g"
|
||||
fi
|
||||
LDFLAGS="--coverage $LDFLAGS"
|
||||
else
|
||||
set_precomp=no
|
||||
# Most likely the CFLAGS already contain -O2 because that is autoconf's default.
|
||||
# We still add it here because passing it twice is not an issue, and handling
|
||||
# this case would just add unnecessary complexity (see #896).
|
||||
SECP_CFLAGS="-O2 $SECP_CFLAGS"
|
||||
fi
|
||||
|
||||
if test x"$req_asm" = x"auto"; then
|
||||
SECP_64BIT_ASM_CHECK
|
||||
if test x"$has_64bit_asm" = x"yes"; then
|
||||
SECP_X86_64_ASM_CHECK
|
||||
if test x"$has_x86_64_asm" = x"yes"; then
|
||||
set_asm=x86_64
|
||||
fi
|
||||
if test x"$set_asm" = x; then
|
||||
|
|
@ -207,287 +294,224 @@ else
|
|||
set_asm=$req_asm
|
||||
case $set_asm in
|
||||
x86_64)
|
||||
SECP_64BIT_ASM_CHECK
|
||||
if test x"$has_64bit_asm" != x"yes"; then
|
||||
AC_MSG_ERROR([x86_64 assembly optimization requested but not available])
|
||||
SECP_X86_64_ASM_CHECK
|
||||
if test x"$has_x86_64_asm" != x"yes"; then
|
||||
AC_MSG_ERROR([x86_64 assembly requested but not available])
|
||||
fi
|
||||
;;
|
||||
arm)
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid assembly optimization selection])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_field" = x"auto"; then
|
||||
if test x"set_asm" = x"x86_64"; then
|
||||
set_field=64bit
|
||||
fi
|
||||
if test x"$set_field" = x; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" = x"yes"; then
|
||||
set_field=64bit
|
||||
fi
|
||||
fi
|
||||
if test x"$set_field" = x; then
|
||||
set_field=32bit
|
||||
fi
|
||||
else
|
||||
set_field=$req_field
|
||||
case $set_field in
|
||||
64bit)
|
||||
if test x"$set_asm" != x"x86_64"; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" != x"yes"; then
|
||||
AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available])
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
32bit)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid field implementation selection])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_scalar" = x"auto"; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" = x"yes"; then
|
||||
set_scalar=64bit
|
||||
fi
|
||||
if test x"$set_scalar" = x; then
|
||||
set_scalar=32bit
|
||||
fi
|
||||
else
|
||||
set_scalar=$req_scalar
|
||||
case $set_scalar in
|
||||
64bit)
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" != x"yes"; then
|
||||
AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available])
|
||||
fi
|
||||
;;
|
||||
32bit)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid scalar implementation selected])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_bignum" = x"auto"; then
|
||||
SECP_GMP_CHECK
|
||||
if test x"$has_gmp" = x"yes"; then
|
||||
set_bignum=gmp
|
||||
fi
|
||||
|
||||
if test x"$set_bignum" = x; then
|
||||
set_bignum=no
|
||||
fi
|
||||
else
|
||||
set_bignum=$req_bignum
|
||||
case $set_bignum in
|
||||
gmp)
|
||||
SECP_GMP_CHECK
|
||||
if test x"$has_gmp" != x"yes"; then
|
||||
AC_MSG_ERROR([gmp bignum explicitly requested but libgmp not available])
|
||||
arm32)
|
||||
SECP_ARM32_ASM_CHECK
|
||||
if test x"$has_arm32_asm" != x"yes"; then
|
||||
AC_MSG_ERROR([ARM32 assembly requested but not available])
|
||||
fi
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid bignum implementation selection])
|
||||
AC_MSG_ERROR([invalid assembly selection])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# select assembly optimization
|
||||
use_external_asm=no
|
||||
# Select assembly
|
||||
enable_external_asm=no
|
||||
|
||||
case $set_asm in
|
||||
x86_64)
|
||||
AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations])
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_ASM_X86_64=1"
|
||||
;;
|
||||
arm)
|
||||
use_external_asm=yes
|
||||
arm32)
|
||||
enable_external_asm=yes
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid assembly optimizations])
|
||||
AC_MSG_ERROR([invalid assembly selection])
|
||||
;;
|
||||
esac
|
||||
|
||||
# select field implementation
|
||||
case $set_field in
|
||||
64bit)
|
||||
AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation])
|
||||
if test x"$enable_external_asm" = x"yes"; then
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_ASM=1"
|
||||
fi
|
||||
|
||||
|
||||
# Select wide multiplication implementation
|
||||
case $set_widemul in
|
||||
int128_struct)
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128_STRUCT=1"
|
||||
;;
|
||||
32bit)
|
||||
AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation])
|
||||
int128)
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128=1"
|
||||
;;
|
||||
int64)
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT64=1"
|
||||
;;
|
||||
auto)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid field implementation])
|
||||
AC_MSG_ERROR([invalid wide multiplication implementation])
|
||||
;;
|
||||
esac
|
||||
|
||||
# select bignum implementation
|
||||
case $set_bignum in
|
||||
gmp)
|
||||
AC_DEFINE(HAVE_LIBGMP, 1, [Define this symbol if libgmp is installed])
|
||||
AC_DEFINE(USE_NUM_GMP, 1, [Define this symbol to use the gmp implementation for num])
|
||||
AC_DEFINE(USE_FIELD_INV_NUM, 1, [Define this symbol to use the num-based field inverse implementation])
|
||||
AC_DEFINE(USE_SCALAR_INV_NUM, 1, [Define this symbol to use the num-based scalar inverse implementation])
|
||||
;;
|
||||
no)
|
||||
AC_DEFINE(USE_NUM_NONE, 1, [Define this symbol to use no num implementation])
|
||||
AC_DEFINE(USE_FIELD_INV_BUILTIN, 1, [Define this symbol to use the native field inverse implementation])
|
||||
AC_DEFINE(USE_SCALAR_INV_BUILTIN, 1, [Define this symbol to use the native scalar inverse implementation])
|
||||
error_window_size=['window size for ecmult precomputation not an integer in range [2..24]']
|
||||
case $set_ecmult_window in
|
||||
''|*[[!0-9]]*)
|
||||
# no valid integer
|
||||
AC_MSG_ERROR($error_window_size)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid bignum implementation])
|
||||
if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then
|
||||
# not in range
|
||||
AC_MSG_ERROR($error_window_size)
|
||||
fi
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DECMULT_WINDOW_SIZE=$set_ecmult_window"
|
||||
;;
|
||||
esac
|
||||
|
||||
#select scalar implementation
|
||||
case $set_scalar in
|
||||
64bit)
|
||||
AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation])
|
||||
case $set_ecmult_gen_kb in
|
||||
2)
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=2 -DCOMB_TEETH=5"
|
||||
;;
|
||||
32bit)
|
||||
AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation])
|
||||
22)
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=11 -DCOMB_TEETH=6"
|
||||
;;
|
||||
86)
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=43 -DCOMB_TEETH=6"
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid scalar implementation])
|
||||
AC_MSG_ERROR(['ecmult gen table size not 2, 22 or 86'])
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$use_tests" = x"yes"; then
|
||||
SECP_OPENSSL_CHECK
|
||||
if test x"$has_openssl_ec" = x"yes"; then
|
||||
if test x"$enable_openssl_tests" != x"no"; then
|
||||
AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
|
||||
SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
|
||||
SECP_TEST_LIBS="$CRYPTO_LIBS"
|
||||
|
||||
case $host in
|
||||
*mingw*)
|
||||
SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
if test x"$enable_openssl_tests" = x"yes"; then
|
||||
AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available])
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if test x"$enable_openssl_tests" = x"yes"; then
|
||||
AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled])
|
||||
fi
|
||||
if test x"$enable_valgrind" = x"yes"; then
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES $VALGRIND_CPPFLAGS -DVALGRIND"
|
||||
fi
|
||||
|
||||
if test x"$use_jni" != x"no"; then
|
||||
AX_JNI_INCLUDE_DIR
|
||||
have_jni_dependencies=yes
|
||||
if test x"$enable_module_ecdh" = x"no"; then
|
||||
have_jni_dependencies=no
|
||||
# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI).
|
||||
# We don't want to set the user variable CFLAGS in CI because this would disable
|
||||
# autoconf's logic for setting default CFLAGS, which we would like to test in CI.
|
||||
SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS"
|
||||
|
||||
###
|
||||
### Handle module options
|
||||
###
|
||||
|
||||
# Processing must be done in a reverse topological sorting of the dependency graph
|
||||
# (dependent module first).
|
||||
if test x"$enable_module_ellswift" = x"yes"; then
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1"
|
||||
fi
|
||||
|
||||
if test x"$enable_module_musig" = x"yes"; then
|
||||
if test x"$enable_module_schnorrsig" = x"no"; then
|
||||
AC_MSG_ERROR([Module dependency error: You have disabled the schnorrsig module explicitly, but it is required by the musig module.])
|
||||
fi
|
||||
if test "x$JNI_INCLUDE_DIRS" = "x"; then
|
||||
have_jni_dependencies=no
|
||||
fi
|
||||
if test "x$have_jni_dependencies" = "xno"; then
|
||||
if test x"$use_jni" = x"yes"; then
|
||||
AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.])
|
||||
fi
|
||||
AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
|
||||
use_jni=no
|
||||
else
|
||||
use_jni=yes
|
||||
for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
|
||||
JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR"
|
||||
done
|
||||
enable_module_schnorrsig=yes
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_MUSIG=1"
|
||||
fi
|
||||
|
||||
if test x"$enable_module_schnorrsig" = x"yes"; then
|
||||
if test x"$enable_module_extrakeys" = x"no"; then
|
||||
AC_MSG_ERROR([Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.])
|
||||
fi
|
||||
enable_module_extrakeys=yes
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1"
|
||||
fi
|
||||
|
||||
if test x"$set_bignum" = x"gmp"; then
|
||||
SECP_LIBS="$SECP_LIBS $GMP_LIBS"
|
||||
SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS"
|
||||
fi
|
||||
|
||||
if test x"$use_endomorphism" = x"yes"; then
|
||||
AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization])
|
||||
fi
|
||||
|
||||
if test x"$set_precomp" = x"yes"; then
|
||||
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
|
||||
fi
|
||||
|
||||
if test x"$enable_module_ecdh" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
|
||||
if test x"$enable_module_extrakeys" = x"yes"; then
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_EXTRAKEYS=1"
|
||||
fi
|
||||
|
||||
if test x"$enable_module_recovery" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1"
|
||||
fi
|
||||
|
||||
AC_C_BIGENDIAN()
|
||||
|
||||
if test x"$use_external_asm" = x"yes"; then
|
||||
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
|
||||
if test x"$enable_module_ecdh" = x"yes"; then
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1"
|
||||
fi
|
||||
|
||||
AC_MSG_NOTICE([Using static precomputation: $set_precomp])
|
||||
AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
|
||||
AC_MSG_NOTICE([Using field implementation: $set_field])
|
||||
AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
|
||||
AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
|
||||
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
|
||||
AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage])
|
||||
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
|
||||
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
|
||||
AC_MSG_NOTICE([Using jni: $use_jni])
|
||||
if test x"$enable_external_default_callbacks" = x"yes"; then
|
||||
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1"
|
||||
fi
|
||||
|
||||
if test x"$enable_experimental" = x"yes"; then
|
||||
AC_MSG_NOTICE([******])
|
||||
AC_MSG_NOTICE([WARNING: experimental build])
|
||||
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
|
||||
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
|
||||
AC_MSG_NOTICE([******])
|
||||
else
|
||||
if test x"$enable_module_ecdh" = x"yes"; then
|
||||
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
|
||||
fi
|
||||
if test x"$set_asm" = x"arm"; then
|
||||
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
|
||||
###
|
||||
### Check for --enable-experimental if necessary
|
||||
###
|
||||
|
||||
if test x"$enable_experimental" = x"no"; then
|
||||
if test x"$set_asm" = x"arm32"; then
|
||||
AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
|
||||
###
|
||||
### Generate output
|
||||
###
|
||||
|
||||
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
|
||||
AC_SUBST(JNI_INCLUDES)
|
||||
AC_SUBST(SECP_INCLUDES)
|
||||
AC_SUBST(SECP_LIBS)
|
||||
AC_SUBST(SECP_TEST_LIBS)
|
||||
AC_SUBST(SECP_TEST_INCLUDES)
|
||||
AC_SUBST(SECP_CFLAGS)
|
||||
AC_SUBST(SECP_CONFIG_DEFINES)
|
||||
AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"])
|
||||
AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
|
||||
AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
|
||||
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
|
||||
AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"])
|
||||
AM_CONDITIONAL([USE_CTIME_TESTS], [test x"$enable_ctime_tests" = x"yes"])
|
||||
AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"])
|
||||
AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"])
|
||||
AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
|
||||
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
|
||||
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
|
||||
|
||||
dnl make sure nothing new is exported so that we don't break the cache
|
||||
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
|
||||
unset PKG_CONFIG_PATH
|
||||
PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP"
|
||||
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"])
|
||||
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"])
|
||||
AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"])
|
||||
AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT)
|
||||
AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION)
|
||||
AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE)
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
echo
|
||||
echo "Build Options:"
|
||||
echo " with external callbacks = $enable_external_default_callbacks"
|
||||
echo " with benchmarks = $enable_benchmark"
|
||||
echo " with tests = $enable_tests"
|
||||
echo " with ctime tests = $enable_ctime_tests"
|
||||
echo " with coverage = $enable_coverage"
|
||||
echo " with examples = $enable_examples"
|
||||
echo " module ecdh = $enable_module_ecdh"
|
||||
echo " module recovery = $enable_module_recovery"
|
||||
echo " module extrakeys = $enable_module_extrakeys"
|
||||
echo " module schnorrsig = $enable_module_schnorrsig"
|
||||
echo " module musig = $enable_module_musig"
|
||||
echo " module ellswift = $enable_module_ellswift"
|
||||
echo
|
||||
echo " asm = $set_asm"
|
||||
echo " ecmult window size = $set_ecmult_window"
|
||||
echo " ecmult gen table size = $set_ecmult_gen_kb KiB"
|
||||
# Hide test-only options unless they're used.
|
||||
if test x"$set_widemul" != xauto; then
|
||||
echo " wide multiplication = $set_widemul"
|
||||
fi
|
||||
echo
|
||||
echo " valgrind = $enable_valgrind"
|
||||
echo " CC = $CC"
|
||||
echo " CPPFLAGS = $CPPFLAGS"
|
||||
echo " SECP_CFLAGS = $SECP_CFLAGS"
|
||||
echo " CFLAGS = $CFLAGS"
|
||||
echo " LDFLAGS = $LDFLAGS"
|
||||
|
||||
if test x"$print_msan_notice" = x"yes"; then
|
||||
echo
|
||||
echo "Note:"
|
||||
echo " MemorySanitizer detected, tried to add -fno-sanitize-memory-param-retval to SECP_CFLAGS"
|
||||
echo " to avoid false positives in ctime_tests. Pass --disable-ctime-tests to avoid this."
|
||||
fi
|
||||
|
||||
if test x"$enable_experimental" = x"yes"; then
|
||||
echo
|
||||
echo "WARNING: Experimental build"
|
||||
echo " Experimental features do not have stable APIs or properties, and may not be safe for"
|
||||
echo " production use."
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <secp256k1.h>
|
||||
|
||||
#include "lax_der_parsing.h"
|
||||
|
||||
|
|
@ -32,7 +31,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (pos + lenbyte > inputlen) {
|
||||
if (lenbyte > inputlen - pos) {
|
||||
return 0;
|
||||
}
|
||||
pos += lenbyte;
|
||||
|
|
@ -51,7 +50,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (pos + lenbyte > inputlen) {
|
||||
if (lenbyte > inputlen - pos) {
|
||||
return 0;
|
||||
}
|
||||
while (lenbyte > 0 && input[pos] == 0) {
|
||||
|
|
@ -89,7 +88,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (pos + lenbyte > inputlen) {
|
||||
if (lenbyte > inputlen - pos) {
|
||||
return 0;
|
||||
}
|
||||
while (lenbyte > 0 && input[pos] == 0) {
|
||||
|
|
@ -112,7 +111,6 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
return 0;
|
||||
}
|
||||
spos = pos;
|
||||
pos += slen;
|
||||
|
||||
/* Ignore leading zeroes in R */
|
||||
while (rlen > 0 && input[rpos] == 0) {
|
||||
|
|
@ -122,7 +120,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
/* Copy R value */
|
||||
if (rlen > 32) {
|
||||
overflow = 1;
|
||||
} else {
|
||||
} else if (rlen) {
|
||||
memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +132,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
/* Copy S value */
|
||||
if (slen > 32) {
|
||||
overflow = 1;
|
||||
} else {
|
||||
} else if (slen) {
|
||||
memcpy(tmpsig + 64 - slen, input + spos, slen);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
/****
|
||||
* Please do not link this file directly. It is not part of the libsecp256k1
|
||||
|
|
@ -48,21 +48,27 @@
|
|||
* 8.3.1.
|
||||
*/
|
||||
|
||||
#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
|
||||
#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
|
||||
#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H
|
||||
#define SECP256K1_CONTRIB_LAX_DER_PARSING_H
|
||||
|
||||
/* #include secp256k1.h only when it hasn't been included yet.
|
||||
This enables this file to be #included directly in other project
|
||||
files (such as tests.c) without the need to set an explicit -I flag,
|
||||
which would be necessary to locate secp256k1.h. */
|
||||
#ifndef SECP256K1_H
|
||||
#include <secp256k1.h>
|
||||
#endif
|
||||
|
||||
# ifdef __cplusplus
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** Parse a signature in "lax DER" format
|
||||
*
|
||||
* Returns: 1 when the signature could be parsed, 0 otherwise.
|
||||
* Args: ctx: a secp256k1 context object
|
||||
* Out: sig: a pointer to a signature object
|
||||
* In: input: a pointer to the signature to be parsed
|
||||
* Out: sig: pointer to a signature object
|
||||
* In: input: pointer to the signature to be parsed
|
||||
* inputlen: the length of the array pointed to be input
|
||||
*
|
||||
* This function will accept any valid DER encoded signature, even if the
|
||||
|
|
@ -88,4 +94,4 @@ int ecdsa_signature_parse_der_lax(
|
|||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif /* SECP256K1_CONTRIB_LAX_DER_PARSING_H */
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2014, 2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2014, 2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <secp256k1.h>
|
||||
|
||||
#include "lax_der_privatekey_parsing.h"
|
||||
|
||||
|
|
@ -45,7 +44,7 @@ int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, co
|
|||
if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]);
|
||||
if (privkey[1]) memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]);
|
||||
if (!secp256k1_ec_seckey_verify(ctx, out32)) {
|
||||
memset(out32, 0, 32);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2014, 2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2014, 2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
/****
|
||||
* Please do not link this file directly. It is not part of the libsecp256k1
|
||||
|
|
@ -25,20 +25,25 @@
|
|||
* library are sufficient.
|
||||
*/
|
||||
|
||||
#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
|
||||
#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
|
||||
#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H
|
||||
#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H
|
||||
|
||||
/* #include secp256k1.h only when it hasn't been included yet.
|
||||
This enables this file to be #included directly in other project
|
||||
files (such as tests.c) without the need to set an explicit -I flag,
|
||||
which would be necessary to locate secp256k1.h. */
|
||||
#ifndef SECP256K1_H
|
||||
#include <secp256k1.h>
|
||||
#endif
|
||||
|
||||
# ifdef __cplusplus
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** Export a private key in DER format.
|
||||
*
|
||||
* Returns: 1 if the private key was valid.
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot
|
||||
* be NULL)
|
||||
* Args: ctx: pointer to a context object (not secp256k1_context_static).
|
||||
* Out: privkey: pointer to an array for storing the private key in BER.
|
||||
* Should have space for 279 bytes, and cannot be NULL.
|
||||
* privkeylen: Pointer to an int where the length of the private key in
|
||||
|
|
@ -87,4 +92,4 @@ SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der(
|
|||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif /* SECP256K1_CONTRIB_BER_PRIVATEKEY_H */
|
||||
|
|
|
|||
483
crypto/secp256k1/libsecp256k1/doc/ellswift.md
Normal file
483
crypto/secp256k1/libsecp256k1/doc/ellswift.md
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
# ElligatorSwift for secp256k1 explained
|
||||
|
||||
In this document we explain how the `ellswift` module implementation is related to the
|
||||
construction in the
|
||||
["SwiftEC: Shallue–van de Woestijne Indifferentiable Function To Elliptic Curves"](https://eprint.iacr.org/2022/759)
|
||||
paper by Jorge Chávez-Saab, Francisco Rodríguez-Henríquez, and Mehdi Tibouchi.
|
||||
|
||||
* [1. Introduction](#1-introduction)
|
||||
* [2. The decoding function](#2-the-decoding-function)
|
||||
+ [2.1 Decoding for `secp256k1`](#21-decoding-for-secp256k1)
|
||||
* [3. The encoding function](#3-the-encoding-function)
|
||||
+ [3.1 Switching to *v, w* coordinates](#31-switching-to-v-w-coordinates)
|
||||
+ [3.2 Avoiding computing all inverses](#32-avoiding-computing-all-inverses)
|
||||
+ [3.3 Finding the inverse](#33-finding-the-inverse)
|
||||
+ [3.4 Dealing with special cases](#34-dealing-with-special-cases)
|
||||
+ [3.5 Encoding for `secp256k1`](#35-encoding-for-secp256k1)
|
||||
* [4. Encoding and decoding full *(x, y)* coordinates](#4-encoding-and-decoding-full-x-y-coordinates)
|
||||
+ [4.1 Full *(x, y)* coordinates for `secp256k1`](#41-full-x-y-coordinates-for-secp256k1)
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
The `ellswift` module effectively introduces a new 64-byte public key format, with the property
|
||||
that (uniformly random) public keys can be encoded as 64-byte arrays which are computationally
|
||||
indistinguishable from uniform byte arrays. The module provides functions to convert public keys
|
||||
from and to this format, as well as convenience functions for key generation and ECDH that operate
|
||||
directly on ellswift-encoded keys.
|
||||
|
||||
The encoding consists of the concatenation of two (32-byte big endian) encoded field elements $u$
|
||||
and $t.$ Together they encode an x-coordinate on the curve $x$, or (see further) a full point $(x, y)$ on
|
||||
the curve.
|
||||
|
||||
**Decoding** consists of decoding the field elements $u$ and $t$ (values above the field size $p$
|
||||
are taken modulo $p$), and then evaluating $F_u(t)$, which for every $u$ and $t$ results in a valid
|
||||
x-coordinate on the curve. The functions $F_u$ will be defined in [Section 2](#2-the-decoding-function).
|
||||
|
||||
**Encoding** a given $x$ coordinate is conceptually done as follows:
|
||||
* Loop:
|
||||
* Pick a uniformly random field element $u.$
|
||||
* Compute the set $L = F_u^{-1}(x)$ of $t$ values for which $F_u(t) = x$, which may have up to *8* elements.
|
||||
* With probability $1 - \dfrac{\\#L}{8}$, restart the loop.
|
||||
* Select a uniformly random $t \in L$ and return $(u, t).$
|
||||
|
||||
This is the *ElligatorSwift* algorithm, here given for just x-coordinates. An extension to full
|
||||
$(x, y)$ points will be given in [Section 4](#4-encoding-and-decoding-full-x-y-coordinates).
|
||||
The algorithm finds a uniformly random $(u, t)$ among (almost all) those
|
||||
for which $F_u(t) = x.$ Section 3.2 in the paper proves that the number of such encodings for
|
||||
almost all x-coordinates on the curve (all but at most 39) is close to two times the field size
|
||||
(specifically, it lies in the range $2q \pm (22\sqrt{q} + O(1))$, where $q$ is the size of the field).
|
||||
|
||||
## 2. The decoding function
|
||||
|
||||
First some definitions:
|
||||
* $\mathbb{F}$ is the finite field of size $q$, of characteristic 5 or more, and $q \equiv 1 \mod 3.$
|
||||
* For `secp256k1`, $q = 2^{256} - 2^{32} - 977$, which satisfies that requirement.
|
||||
* Let $E$ be the elliptic curve of points $(x, y) \in \mathbb{F}^2$ for which $y^2 = x^3 + ax + b$, with $a$ and $b$
|
||||
public constants, for which $\Delta_E = -16(4a^3 + 27b^2)$ is a square, and at least one of $(-b \pm \sqrt{-3 \Delta_E} / 36)/2$ is a square.
|
||||
This implies that the order of $E$ is either odd, or a multiple of *4*.
|
||||
If $a=0$, this condition is always fulfilled.
|
||||
* For `secp256k1`, $a=0$ and $b=7.$
|
||||
* Let the function $g(x) = x^3 + ax + b$, so the $E$ curve equation is also $y^2 = g(x).$
|
||||
* Let the function $h(x) = 3x^3 + 4a.$
|
||||
* Define $V$ as the set of solutions $(x_1, x_2, x_3, z)$ to $z^2 = g(x_1)g(x_2)g(x_3).$
|
||||
* Define $S_u$ as the set of solutions $(X, Y)$ to $X^2 + h(u)Y^2 = -g(u)$ and $Y \neq 0.$
|
||||
* $P_u$ is a function from $\mathbb{F}$ to $S_u$ that will be defined below.
|
||||
* $\psi_u$ is a function from $S_u$ to $V$ that will be defined below.
|
||||
|
||||
**Note**: In the paper:
|
||||
* $F_u$ corresponds to $F_{0,u}$ there.
|
||||
* $P_u(t)$ is called $P$ there.
|
||||
* All $S_u$ sets together correspond to $S$ there.
|
||||
* All $\psi_u$ functions together (operating on elements of $S$) correspond to $\psi$ there.
|
||||
|
||||
Note that for $V$, the left hand side of the equation $z^2$ is square, and thus the right
|
||||
hand must also be square. As multiplying non-squares results in a square in $\mathbb{F}$,
|
||||
out of the three right-hand side factors an even number must be non-squares.
|
||||
This implies that exactly *1* or exactly *3* out of
|
||||
$\\{g(x_1), g(x_2), g(x_3)\\}$ must be square, and thus that for any $(x_1,x_2,x_3,z) \in V$,
|
||||
at least one of $\\{x_1, x_2, x_3\\}$ must be a valid x-coordinate on $E.$ There is one exception
|
||||
to this, namely when $z=0$, but even then one of the three values is a valid x-coordinate.
|
||||
|
||||
**Define** the decoding function $F_u(t)$ as:
|
||||
* Let $(x_1, x_2, x_3, z) = \psi_u(P_u(t)).$
|
||||
* Return the first element $x$ of $(x_3, x_2, x_1)$ which is a valid x-coordinate on $E$ (i.e., $g(x)$ is square).
|
||||
|
||||
$P_u(t) = (X(u, t), Y(u, t))$, where:
|
||||
|
||||
$$
|
||||
\begin{array}{lcl}
|
||||
X(u, t) & = & \left\\{\begin{array}{ll}
|
||||
\dfrac{g(u) - t^2}{2t} & a = 0 \\
|
||||
\dfrac{g(u) + h(u)(Y_0(u) - X_0(u)t)^2}{X_0(u)(1 + h(u)t^2)} & a \neq 0
|
||||
\end{array}\right. \\
|
||||
Y(u, t) & = & \left\\{\begin{array}{ll}
|
||||
\dfrac{X(u, t) + t}{u \sqrt{-3}} = \dfrac{g(u) + t^2}{2tu\sqrt{-3}} & a = 0 \\
|
||||
Y_0(u) + t(X(u, t) - X_0(u)) & a \neq 0
|
||||
\end{array}\right.
|
||||
\end{array}
|
||||
$$
|
||||
|
||||
$P_u(t)$ is defined:
|
||||
* For $a=0$, unless:
|
||||
* $u = 0$ or $t = 0$ (division by zero)
|
||||
* $g(u) = -t^2$ (would give $Y=0$).
|
||||
* For $a \neq 0$, unless:
|
||||
* $X_0(u) = 0$ or $h(u)t^2 = -1$ (division by zero)
|
||||
* $Y_0(u) (1 - h(u)t^2) = 2X_0(u)t$ (would give $Y=0$).
|
||||
|
||||
The functions $X_0(u)$ and $Y_0(u)$ are defined in Appendix A of the paper, and depend on various properties of $E.$
|
||||
|
||||
The function $\psi_u$ is the same for all curves: $\psi_u(X, Y) = (x_1, x_2, x_3, z)$, where:
|
||||
|
||||
$$
|
||||
\begin{array}{lcl}
|
||||
x_1 & = & \dfrac{X}{2Y} - \dfrac{u}{2} && \\
|
||||
x_2 & = & -\dfrac{X}{2Y} - \dfrac{u}{2} && \\
|
||||
x_3 & = & u + 4Y^2 && \\
|
||||
z & = & \dfrac{g(x_3)}{2Y}(u^2 + ux_1 + x_1^2 + a) = \dfrac{-g(u)g(x_3)}{8Y^3}
|
||||
\end{array}
|
||||
$$
|
||||
|
||||
### 2.1 Decoding for `secp256k1`
|
||||
|
||||
Put together and specialized for $a=0$ curves, decoding $(u, t)$ to an x-coordinate is:
|
||||
|
||||
**Define** $F_u(t)$ as:
|
||||
* Let $X = \dfrac{u^3 + b - t^2}{2t}.$
|
||||
* Let $Y = \dfrac{X + t}{u\sqrt{-3}}.$
|
||||
* Return the first $x$ in $(u + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u}{2}, \dfrac{X}{2Y} - \dfrac{u}{2})$ for which $g(x)$ is square.
|
||||
|
||||
To make sure that every input decodes to a valid x-coordinate, we remap the inputs in case
|
||||
$P_u$ is not defined (when $u=0$, $t=0$, or $g(u) = -t^2$):
|
||||
|
||||
**Define** $F_u(t)$ as:
|
||||
* Let $u'=u$ if $u \neq 0$; $1$ otherwise (guaranteeing $u' \neq 0$).
|
||||
* Let $t'=t$ if $t \neq 0$; $1$ otherwise (guaranteeing $t' \neq 0$).
|
||||
* Let $t''=t'$ if $g(u') \neq -t'^2$; $2t'$ otherwise (guaranteeing $t'' \neq 0$ and $g(u') \neq -t''^2$).
|
||||
* Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$
|
||||
* Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$
|
||||
* Return the first $x$ in $(u' + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u'}{2}, \dfrac{X}{2Y} - \dfrac{u'}{2})$ for which $x^3 + b$ is square.
|
||||
|
||||
The choices here are not strictly necessary. Just returning a fixed constant in any of the undefined cases would suffice,
|
||||
but the approach here is simple enough and gives fairly uniform output even in these cases.
|
||||
|
||||
**Note**: in the paper these conditions result in $\infty$ as output, due to the use of projective coordinates there.
|
||||
We wish to avoid the need for callers to deal with this special case.
|
||||
|
||||
This is implemented in `secp256k1_ellswift_xswiftec_frac_var` (which decodes to an x-coordinate represented as a fraction), and
|
||||
in `secp256k1_ellswift_xswiftec_var` (which outputs the actual x-coordinate).
|
||||
|
||||
## 3. The encoding function
|
||||
|
||||
To implement $F_u^{-1}(x)$, the function to find the set of inverses $t$ for which $F_u(t) = x$, we have to reverse the process:
|
||||
* Find all the $(X, Y) \in S_u$ that could have given rise to $x$, through the $x_1$, $x_2$, or $x_3$ formulas in $\psi_u.$
|
||||
* Map those $(X, Y)$ solutions to $t$ values using $P_u^{-1}(X, Y).$
|
||||
* For each of the found $t$ values, verify that $F_u(t) = x.$
|
||||
* Return the remaining $t$ values.
|
||||
|
||||
The function $P_u^{-1}$, which finds $t$ given $(X, Y) \in S_u$, is significantly simpler than $P_u:$
|
||||
|
||||
$$
|
||||
P_u^{-1}(X, Y) = \left\\{\begin{array}{ll}
|
||||
Yu\sqrt{-3} - X & a = 0 \\
|
||||
\dfrac{Y-Y_0(u)}{X-X_0(u)} & a \neq 0 \land X \neq X_0(u) \\
|
||||
\dfrac{-X_0(u)}{h(u)Y_0(u)} & a \neq 0 \land X = X_0(u) \land Y = Y_0(u)
|
||||
\end{array}\right.
|
||||
$$
|
||||
|
||||
The third step above, verifying that $F_u(t) = x$, is necessary because for the $(X, Y)$ values found through the $x_1$ and $x_2$ expressions,
|
||||
it is possible that decoding through $\psi_u(X, Y)$ yields a valid $x_3$ on the curve, which would take precedence over the
|
||||
$x_1$ or $x_2$ decoding. These $(X, Y)$ solutions must be rejected.
|
||||
|
||||
Since we know that exactly one or exactly three out of $\\{x_1, x_2, x_3\\}$ are valid x-coordinates for any $t$,
|
||||
the case where either $x_1$ or $x_2$ is valid and in addition also $x_3$ is valid must mean that all three are valid.
|
||||
This means that instead of checking whether $x_3$ is on the curve, it is also possible to check whether the other one out of
|
||||
$x_1$ and $x_2$ is on the curve. This is significantly simpler, as it turns out.
|
||||
|
||||
Observe that $\psi_u$ guarantees that $x_1 + x_2 = -u.$ So given either $x = x_1$ or $x = x_2$, the other one of the two can be computed as
|
||||
$-u - x.$ Thus, when encoding $x$ through the $x_1$ or $x_2$ expressions, one can simply check whether $g(-u-x)$ is a square,
|
||||
and if so, not include the corresponding $t$ values in the returned set. As this does not need $X$, $Y$, or $t$, this condition can be determined
|
||||
before those values are computed.
|
||||
|
||||
It is not possible that an encoding found through the $x_1$ expression decodes to a different valid x-coordinate using $x_2$ (which would
|
||||
take precedence), for the same reason: if both $x_1$ and $x_2$ decodings were valid, $x_3$ would be valid as well, and thus take
|
||||
precedence over both. Because of this, the $g(-u-x)$ being square test for $x_1$ and $x_2$ is the only test necessary to guarantee the found $t$
|
||||
values round-trip back to the input $x$ correctly. This is the reason for choosing the $(x_3, x_2, x_1)$ precedence order in the decoder;
|
||||
any order which does not place $x_3$ first requires more complicated round-trip checks in the encoder.
|
||||
|
||||
### 3.1 Switching to *v, w* coordinates
|
||||
|
||||
Before working out the formulas for all this, we switch to different variables for $S_u.$ Let $v = (X/Y - u)/2$, and
|
||||
$w = 2Y.$ Or in the other direction, $X = w(u/2 + v)$ and $Y = w/2:$
|
||||
* $S_u'$ becomes the set of $(v, w)$ for which $w^2 (u^2 + uv + v^2 + a) = -g(u)$ and $w \neq 0.$
|
||||
* For $a=0$ curves, $P_u^{-1}$ can be stated for $(v,w)$ as $P_u^{'-1}(v, w) = w\left(\frac{\sqrt{-3}-1}{2}u - v\right).$
|
||||
* $\psi_u$ can be stated for $(v, w)$ as $\psi_u'(v, w) = (x_1, x_2, x_3, z)$, where
|
||||
|
||||
$$
|
||||
\begin{array}{lcl}
|
||||
x_1 & = & v \\
|
||||
x_2 & = & -u - v \\
|
||||
x_3 & = & u + w^2 \\
|
||||
z & = & \dfrac{g(x_3)}{w}(u^2 + uv + v^2 + a) = \dfrac{-g(u)g(x_3)}{w^3}
|
||||
\end{array}
|
||||
$$
|
||||
|
||||
We can now write the expressions for finding $(v, w)$ given $x$ explicitly, by solving each of the $\\{x_1, x_2, x_3\\}$
|
||||
expressions for $v$ or $w$, and using the $S_u'$ equation to find the other variable:
|
||||
* Assuming $x = x_1$, we find $v = x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions).
|
||||
* Assuming $x = x_2$, we find $v = -u-x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions).
|
||||
* Assuming $x = x_3$, we find $w = \pm\sqrt{x-u}$ and $v = -u/2 \pm \sqrt{-w^2(4g(u) + w^2h(u))}/(2w^2)$ (four solutions).
|
||||
|
||||
### 3.2 Avoiding computing all inverses
|
||||
|
||||
The *ElligatorSwift* algorithm as stated in Section 1 requires the computation of $L = F_u^{-1}(x)$ (the
|
||||
set of all $t$ such that $(u, t)$ decode to $x$) in full. This is unnecessary.
|
||||
|
||||
Observe that the procedure of restarting with probability $(1 - \frac{\\#L}{8})$ and otherwise returning a
|
||||
uniformly random element from $L$ is actually equivalent to always padding $L$ with $\bot$ values up to length 8,
|
||||
picking a uniformly random element from that, restarting whenever $\bot$ is picked:
|
||||
|
||||
**Define** *ElligatorSwift(x)* as:
|
||||
* Loop:
|
||||
* Pick a uniformly random field element $u.$
|
||||
* Compute the set $L = F_u^{-1}(x).$
|
||||
* Let $T$ be the 8-element vector consisting of the elements of $L$, plus $8 - \\#L$ times $\\{\bot\\}.$
|
||||
* Select a uniformly random $t \in T.$
|
||||
* If $t \neq \bot$, return $(u, t)$; restart loop otherwise.
|
||||
|
||||
Now notice that the order of elements in $T$ does not matter, as all we do is pick a uniformly
|
||||
random element in it, so we do not need to have all $\bot$ values at the end.
|
||||
As we have 8 distinct formulas for finding $(v, w)$ (taking the variants due to $\pm$ into account),
|
||||
we can associate every index in $T$ with exactly one of those formulas, making sure that:
|
||||
* Formulas that yield no solutions (due to division by zero or non-existing square roots) or invalid solutions are made to return $\bot.$
|
||||
* For the $x_1$ and $x_2$ cases, if $g(-u-x)$ is a square, $\bot$ is returned instead (the round-trip check).
|
||||
* In case multiple formulas would return the same non- $\bot$ result, all but one of those must be turned into $\bot$ to avoid biasing those.
|
||||
|
||||
The last condition above only occurs with negligible probability for cryptographically-sized curves, but is interesting
|
||||
to take into account as it allows exhaustive testing in small groups. See [Section 3.4](#34-dealing-with-special-cases)
|
||||
for an analysis of all the negligible cases.
|
||||
|
||||
If we define $T = (G_{0,u}(x), G_{1,u}(x), \ldots, G_{7,u}(x))$, with each $G_{i,u}$ matching one of the formulas,
|
||||
the loop can be simplified to only compute one of the inverses instead of all of them:
|
||||
|
||||
**Define** *ElligatorSwift(x)* as:
|
||||
* Loop:
|
||||
* Pick a uniformly random field element $u.$
|
||||
* Pick a uniformly random integer $c$ in $[0,8).$
|
||||
* Let $t = G_{c,u}(x).$
|
||||
* If $t \neq \bot$, return $(u, t)$; restart loop otherwise.
|
||||
|
||||
This is implemented in `secp256k1_ellswift_xelligatorswift_var`.
|
||||
|
||||
### 3.3 Finding the inverse
|
||||
|
||||
To implement $G_{c,u}$, we map $c=0$ to the $x_1$ formula, $c=1$ to the $x_2$ formula, and $c=2$ and $c=3$ to the $x_3$ formula.
|
||||
Those are then repeated as $c=4$ through $c=7$ for the other sign of $w$ (noting that in each formula, $w$ is a square root of some expression).
|
||||
Ignoring the negligible cases, we get:
|
||||
|
||||
**Define** $G_{c,u}(x)$ as:
|
||||
* If $c \in \\{0, 1, 4, 5\\}$ (for $x_1$ and $x_2$ formulas):
|
||||
* If $g(-u-x)$ is square, return $\bot$ (as $x_3$ would be valid and take precedence).
|
||||
* If $c \in \\{0, 4\\}$ (the $x_1$ formula) let $v = x$, otherwise let $v = -u-x$ (the $x_2$ formula)
|
||||
* Let $s = -g(u)/(u^2 + uv + v^2 + a)$ (using $s = w^2$ in what follows).
|
||||
* Otherwise, when $c \in \\{2, 3, 6, 7\\}$ (for $x_3$ formulas):
|
||||
* Let $s = x-u.$
|
||||
* Let $r = \sqrt{-s(4g(u) + sh(u))}.$
|
||||
* Let $v = (r/s - u)/2$ if $c \in \\{3, 7\\}$; $(-r/s - u)/2$ otherwise.
|
||||
* Let $w = \sqrt{s}.$
|
||||
* Depending on $c:$
|
||||
* If $c \in \\{0, 1, 2, 3\\}:$ return $P_u^{'-1}(v, w).$
|
||||
* If $c \in \\{4, 5, 6, 7\\}:$ return $P_u^{'-1}(v, -w).$
|
||||
|
||||
Whenever a square root of a non-square is taken, $\bot$ is returned; for both square roots this happens with roughly
|
||||
50% on random inputs. Similarly, when a division by 0 would occur, $\bot$ is returned as well; this will only happen
|
||||
with negligible probability. A division by 0 in the first branch in fact cannot occur at all, because $u^2 + uv + v^2 + a = 0$
|
||||
implies $g(-u-x) = g(x)$ which would mean the $g(-u-x)$ is square condition has triggered
|
||||
and $\bot$ would have been returned already.
|
||||
|
||||
**Note**: In the paper, the $case$ variable corresponds roughly to the $c$ above, but only takes on 4 possible values (1 to 4).
|
||||
The conditional negation of $w$ at the end is done randomly, which is equivalent, but makes testing harder. We choose to
|
||||
have the $G_{c,u}$ be deterministic, and capture all choices in $c.$
|
||||
|
||||
Now observe that the $c \in \\{1, 5\\}$ and $c \in \\{3, 7\\}$ conditions effectively perform the same $v \rightarrow -u-v$
|
||||
transformation. Furthermore, that transformation has no effect on $s$ in the first branch
|
||||
as $u^2 + ux + x^2 + a = u^2 + u(-u-x) + (-u-x)^2 + a.$ Thus we can extract it out and move it down:
|
||||
|
||||
**Define** $G_{c,u}(x)$ as:
|
||||
* If $c \in \\{0, 1, 4, 5\\}:$
|
||||
* If $g(-u-x)$ is square, return $\bot.$
|
||||
* Let $s = -g(u)/(u^2 + ux + x^2 + a).$
|
||||
* Let $v = x.$
|
||||
* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$
|
||||
* Let $s = x-u.$
|
||||
* Let $r = \sqrt{-s(4g(u) + sh(u))}.$
|
||||
* Let $v = (r/s - u)/2.$
|
||||
* Let $w = \sqrt{s}.$
|
||||
* Depending on $c:$
|
||||
* If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w).$
|
||||
* If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w).$
|
||||
* If $c \in \\{4, 6\\}:$ return $P_u^{'-1}(v, -w).$
|
||||
* If $c \in \\{5, 7\\}:$ return $P_u^{'-1}(-u-v, -w).$
|
||||
|
||||
This shows there will always be exactly 0, 4, or 8 $t$ values for a given $(u, x)$ input.
|
||||
There can be 0, 1, or 2 $(v, w)$ pairs before invoking $P_u^{'-1}$, and each results in 4 distinct $t$ values.
|
||||
|
||||
### 3.4 Dealing with special cases
|
||||
|
||||
As mentioned before there are a few cases to deal with which only happen in a negligibly small subset of inputs.
|
||||
For cryptographically sized fields, if only random inputs are going to be considered, it is unnecessary to deal with these. Still, for completeness
|
||||
we analyse them here. They generally fall into two categories: cases in which the encoder would produce $t$ values that
|
||||
do not decode back to $x$ (or at least cannot guarantee that they do), and cases in which the encoder might produce the same
|
||||
$t$ value for multiple $c$ inputs (thereby biasing that encoding):
|
||||
|
||||
* In the branch for $x_1$ and $x_2$ (where $c \in \\{0, 1, 4, 5\\}$):
|
||||
* When $g(u) = 0$, we would have $s=w=Y=0$, which is not on $S_u.$ This is only possible on even-ordered curves.
|
||||
Excluding this also removes the one condition under which the simplified check for $x_3$ on the curve
|
||||
fails (namely when $g(x_1)=g(x_2)=0$ but $g(x_3)$ is not square).
|
||||
This does exclude some valid encodings: when both $g(u)=0$ and $u^2+ux+x^2+a=0$ (also implying $g(x)=0$),
|
||||
the $S_u'$ equation degenerates to $0 = 0$, and many valid $t$ values may exist. Yet, these cannot be targeted uniformly by the
|
||||
encoder anyway as there will generally be more than 8.
|
||||
* When $g(x) = 0$, the same $t$ would be produced as in the $x_3$ branch (where $c \in \\{2, 3, 6, 7\\}$) which we give precedence
|
||||
as it can deal with $g(u)=0$.
|
||||
This is again only possible on even-ordered curves.
|
||||
* In the branch for $x_3$ (where $c \in \\{2, 3, 6, 7\\}$):
|
||||
* When $s=0$, a division by zero would occur.
|
||||
* When $v = -u-v$ and $c \in \\{3, 7\\}$, the same $t$ would be returned as in the $c \in \\{2, 6\\}$ cases.
|
||||
It is equivalent to checking whether $r=0$.
|
||||
This cannot occur in the $x_1$ or $x_2$ branches, as it would trigger the $g(-u-x)$ is square condition.
|
||||
A similar concern for $w = -w$ does not exist, as $w=0$ is already impossible in both branches: in the first
|
||||
it requires $g(u)=0$ which is already outlawed on even-ordered curves and impossible on others; in the second it would trigger division by zero.
|
||||
* Curve-specific special cases also exist that need to be rejected, because they result in $(u,t)$ which is invalid to the decoder, or because of division by zero in the encoder:
|
||||
* For $a=0$ curves, when $u=0$ or when $t=0$. The latter can only be reached by the encoder when $g(u)=0$, which requires an even-ordered curve.
|
||||
* For $a \neq 0$ curves, when $X_0(u)=0$, when $h(u)t^2 = -1$, or when $w(u + 2v) = 2X_0(u)$ while also either $w \neq 2Y_0(u)$ or $h(u)=0$.
|
||||
|
||||
**Define** a version of $G_{c,u}(x)$ which deals with all these cases:
|
||||
* If $a=0$ and $u=0$, return $\bot.$
|
||||
* If $a \neq 0$ and $X_0(u)=0$, return $\bot.$
|
||||
* If $c \in \\{0, 1, 4, 5\\}:$
|
||||
* If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only).
|
||||
* If $g(-u-x)$ is square, return $\bot.$
|
||||
* Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero).
|
||||
* Let $v = x.$
|
||||
* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$
|
||||
* Let $s = x-u.$
|
||||
* Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square.
|
||||
* If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$
|
||||
* If $s = 0$, return $\bot.$
|
||||
* Let $v = (r/s - u)/2.$
|
||||
* Let $w = \sqrt{s}$; return $\bot$ if not square.
|
||||
* If $a \neq 0$ and $w(u+2v) = 2X_0(u)$ and either $w \neq 2Y_0(u)$ or $h(u) = 0$, return $\bot.$
|
||||
* Depending on $c:$
|
||||
* If $c \in \\{0, 2\\}$, let $t = P_u^{'-1}(v, w).$
|
||||
* If $c \in \\{1, 3\\}$, let $t = P_u^{'-1}(-u-v, w).$
|
||||
* If $c \in \\{4, 6\\}$, let $t = P_u^{'-1}(v, -w).$
|
||||
* If $c \in \\{5, 7\\}$, let $t = P_u^{'-1}(-u-v, -w).$
|
||||
* If $a=0$ and $t=0$, return $\bot$ (even curves only).
|
||||
* If $a \neq 0$ and $h(u)t^2 = -1$, return $\bot.$
|
||||
* Return $t.$
|
||||
|
||||
Given any $u$, using this algorithm over all $x$ and $c$ values, every $t$ value will be reached exactly once,
|
||||
for an $x$ for which $F_u(t) = x$ holds, except for these cases that will not be reached:
|
||||
* All cases where $P_u(t)$ is not defined:
|
||||
* For $a=0$ curves, when $u=0$, $t=0$, or $g(u) = -t^2.$
|
||||
* For $a \neq 0$ curves, when $h(u)t^2 = -1$, $X_0(u) = 0$, or $Y_0(u) (1 - h(u) t^2) = 2X_0(u)t.$
|
||||
* When $g(u)=0$, the potentially many $t$ values that decode to an $x$ satisfying $g(x)=0$ using the $x_2$ formula. These were excluded by the $g(u)=0$ condition in the $c \in \\{0, 1, 4, 5\\}$ branch.
|
||||
|
||||
These cases form a negligible subset of all $(u, t)$ for cryptographically sized curves.
|
||||
|
||||
### 3.5 Encoding for `secp256k1`
|
||||
|
||||
Specialized for odd-ordered $a=0$ curves:
|
||||
|
||||
**Define** $G_{c,u}(x)$ as:
|
||||
* If $u=0$, return $\bot.$
|
||||
* If $c \in \\{0, 1, 4, 5\\}:$
|
||||
* If $(-u-x)^3 + b$ is square, return $\bot$
|
||||
* Let $s = -(u^3 + b)/(u^2 + ux + x^2)$ (cannot cause division by 0).
|
||||
* Let $v = x.$
|
||||
* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$
|
||||
* Let $s = x-u.$
|
||||
* Let $r = \sqrt{-s(4(u^3 + b) + 3su^2)}$; return $\bot$ if not square.
|
||||
* If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$
|
||||
* If $s = 0$, return $\bot.$
|
||||
* Let $v = (r/s - u)/2.$
|
||||
* Let $w = \sqrt{s}$; return $\bot$ if not square.
|
||||
* Depending on $c:$
|
||||
* If $c \in \\{0, 2\\}:$ return $w(\frac{\sqrt{-3}-1}{2}u - v).$
|
||||
* If $c \in \\{1, 3\\}:$ return $w(\frac{\sqrt{-3}+1}{2}u + v).$
|
||||
* If $c \in \\{4, 6\\}:$ return $w(\frac{-\sqrt{-3}+1}{2}u + v).$
|
||||
* If $c \in \\{5, 7\\}:$ return $w(\frac{-\sqrt{-3}-1}{2}u - v).$
|
||||
|
||||
This is implemented in `secp256k1_ellswift_xswiftec_inv_var`.
|
||||
|
||||
And the x-only ElligatorSwift encoding algorithm is still:
|
||||
|
||||
**Define** *ElligatorSwift(x)* as:
|
||||
* Loop:
|
||||
* Pick a uniformly random field element $u.$
|
||||
* Pick a uniformly random integer $c$ in $[0,8).$
|
||||
* Let $t = G_{c,u}(x).$
|
||||
* If $t \neq \bot$, return $(u, t)$; restart loop otherwise.
|
||||
|
||||
Note that this logic does not take the remapped $u=0$, $t=0$, and $g(u) = -t^2$ cases into account; it just avoids them.
|
||||
While it is not impossible to make the encoder target them, this would increase the maximum number of $t$ values for a given $(u, x)$
|
||||
combination beyond 8, and thereby slow down the ElligatorSwift loop proportionally, for a negligible gain in uniformity.
|
||||
|
||||
## 4. Encoding and decoding full *(x, y)* coordinates
|
||||
|
||||
So far we have only addressed encoding and decoding x-coordinates, but in some cases an encoding
|
||||
for full points with $(x, y)$ coordinates is desirable. It is possible to encode this information
|
||||
in $t$ as well.
|
||||
|
||||
Note that for any $(X, Y) \in S_u$, $(\pm X, \pm Y)$ are all on $S_u.$ Moreover, all of these are
|
||||
mapped to the same x-coordinate. Negating $X$ or negating $Y$ just results in $x_1$ and $x_2$
|
||||
being swapped, and does not affect $x_3.$ This will not change the outcome x-coordinate as the order
|
||||
of $x_1$ and $x_2$ only matters if both were to be valid, and in that case $x_3$ would be used instead.
|
||||
|
||||
Still, these four $(X, Y)$ combinations all correspond to distinct $t$ values, so we can encode
|
||||
the sign of the y-coordinate in the sign of $X$ or the sign of $Y.$ They correspond to the
|
||||
four distinct $P_u^{'-1}$ calls in the definition of $G_{u,c}.$
|
||||
|
||||
**Note**: In the paper, the sign of the y coordinate is encoded in a separately-coded bit.
|
||||
|
||||
To encode the sign of $y$ in the sign of $Y:$
|
||||
|
||||
**Define** *Decode(u, t)* for full $(x, y)$ as:
|
||||
* Let $(X, Y) = P_u(t).$
|
||||
* Let $x$ be the first value in $(u + 4Y^2, \frac{-X}{2Y} - \frac{u}{2}, \frac{X}{2Y} - \frac{u}{2})$ for which $g(x)$ is square.
|
||||
* Let $y = \sqrt{g(x)}.$
|
||||
* If $sign(y) = sign(Y)$, return $(x, y)$; otherwise return $(x, -y).$
|
||||
|
||||
And encoding would be done using a $G_{c,u}(x, y)$ function defined as:
|
||||
|
||||
**Define** $G_{c,u}(x, y)$ as:
|
||||
* If $c \in \\{0, 1\\}:$
|
||||
* If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only).
|
||||
* If $g(-u-x)$ is square, return $\bot.$
|
||||
* Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero).
|
||||
* Let $v = x.$
|
||||
* Otherwise, when $c \in \\{2, 3\\}:$
|
||||
* Let $s = x-u.$
|
||||
* Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square.
|
||||
* If $c = 3$ and $r = 0$, return $\bot.$
|
||||
* Let $v = (r/s - u)/2.$
|
||||
* Let $w = \sqrt{s}$; return $\bot$ if not square.
|
||||
* Let $w' = w$ if $sign(w/2) = sign(y)$; $-w$ otherwise.
|
||||
* Depending on $c:$
|
||||
* If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w').$
|
||||
* If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w').$
|
||||
|
||||
Note that $c$ now only ranges $[0,4)$, as the sign of $w'$ is decided based on that of $y$, rather than on $c.$
|
||||
This change makes some valid encodings unreachable: when $y = 0$ and $sign(Y) \neq sign(0)$.
|
||||
|
||||
In the above logic, $sign$ can be implemented in several ways, such as parity of the integer representation
|
||||
of the input field element (for prime-sized fields) or the quadratic residuosity (for fields where
|
||||
$-1$ is not square). The choice does not matter, as long as it only takes on two possible values, and for $x \neq 0$ it holds that $sign(x) \neq sign(-x)$.
|
||||
|
||||
### 4.1 Full *(x, y)* coordinates for `secp256k1`
|
||||
|
||||
For $a=0$ curves, there is another option. Note that for those,
|
||||
the $P_u(t)$ function translates negations of $t$ to negations of (both) $X$ and $Y.$ Thus, we can use $sign(t)$ to
|
||||
encode the y-coordinate directly. Combined with the earlier remapping to guarantee all inputs land on the curve, we get
|
||||
as decoder:
|
||||
|
||||
**Define** *Decode(u, t)* as:
|
||||
* Let $u'=u$ if $u \neq 0$; $1$ otherwise.
|
||||
* Let $t'=t$ if $t \neq 0$; $1$ otherwise.
|
||||
* Let $t''=t'$ if $u'^3 + b + t'^2 \neq 0$; $2t'$ otherwise.
|
||||
* Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$
|
||||
* Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$
|
||||
* Let $x$ be the first element of $(u' + 4Y^2, \frac{-X}{2Y} - \frac{u'}{2}, \frac{X}{2Y} - \frac{u'}{2})$ for which $g(x)$ is square.
|
||||
* Let $y = \sqrt{g(x)}.$
|
||||
* Return $(x, y)$ if $sign(y) = sign(t)$; $(x, -y)$ otherwise.
|
||||
|
||||
This is implemented in `secp256k1_ellswift_swiftec_var`. The used $sign(x)$ function is the parity of $x$ when represented as in integer in $[0,q).$
|
||||
|
||||
The corresponding encoder would invoke the x-only one, but negating the output $t$ if $sign(t) \neq sign(y).$
|
||||
|
||||
This is implemented in `secp256k1_ellswift_elligatorswift_var`.
|
||||
|
||||
Note that this is only intended for encoding points where both the x-coordinate and y-coordinate are unpredictable. When encoding x-only points
|
||||
where the y-coordinate is implicitly even (or implicitly square, or implicitly in $[0,q/2]$), the encoder in
|
||||
[Section 3.5](#35-encoding-for-secp256k1) must be used, or a bias is reintroduced that undoes all the benefit of using ElligatorSwift
|
||||
in the first place.
|
||||
54
crypto/secp256k1/libsecp256k1/doc/musig.md
Normal file
54
crypto/secp256k1/libsecp256k1/doc/musig.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
Notes on the musig module API
|
||||
===========================
|
||||
|
||||
The following sections contain additional notes on the API of the musig module (`include/secp256k1_musig.h`).
|
||||
A usage example can be found in `examples/musig.c`.
|
||||
|
||||
## API misuse
|
||||
|
||||
The musig API is designed with a focus on misuse resistance.
|
||||
However, due to the interactive nature of the MuSig protocol, there are additional failure modes that are not present in regular (single-party) Schnorr signature creation.
|
||||
While the results can be catastrophic (e.g. leaking of the secret key), it is unfortunately not possible for the musig implementation to prevent all such failure modes.
|
||||
|
||||
Therefore, users of the musig module must take great care to make sure of the following:
|
||||
|
||||
1. A unique nonce per signing session is generated in `secp256k1_musig_nonce_gen`.
|
||||
See the corresponding comment in `include/secp256k1_musig.h` for how to ensure that.
|
||||
2. The `secp256k1_musig_secnonce` structure is never copied or serialized.
|
||||
See also the comment on `secp256k1_musig_secnonce` in `include/secp256k1_musig.h`.
|
||||
3. Opaque data structures are never written to or read from directly.
|
||||
Instead, only the provided accessor functions are used.
|
||||
|
||||
## Key Aggregation and (Taproot) Tweaking
|
||||
|
||||
Given a set of public keys, the aggregate public key is computed with `secp256k1_musig_pubkey_agg`.
|
||||
A plain tweak can be added to the resulting public key with `secp256k1_ec_pubkey_tweak_add` by setting the `tweak32` argument to the hash defined in BIP 32. Similarly, a Taproot tweak can be added with `secp256k1_xonly_pubkey_tweak_add` by setting the `tweak32` argument to the TapTweak hash defined in BIP 341.
|
||||
Both types of tweaking can be combined and invoked multiple times if the specific application requires it.
|
||||
|
||||
## Signing
|
||||
|
||||
This is covered by `examples/musig.c`.
|
||||
Essentially, the protocol proceeds in the following steps:
|
||||
|
||||
1. Generate a keypair with `secp256k1_keypair_create` and obtain the public key with `secp256k1_keypair_pub`.
|
||||
2. Call `secp256k1_musig_pubkey_agg` with the pubkeys of all participants.
|
||||
3. Optionally add a (Taproot) tweak with `secp256k1_musig_pubkey_xonly_tweak_add` and a plain tweak with `secp256k1_musig_pubkey_ec_tweak_add`.
|
||||
4. Generate a pair of secret and public nonce with `secp256k1_musig_nonce_gen` and send the public nonce to the other signers.
|
||||
5. Someone (not necessarily the signer) aggregates the public nonces with `secp256k1_musig_nonce_agg` and sends it to the signers.
|
||||
6. Process the aggregate nonce with `secp256k1_musig_nonce_process`.
|
||||
7. Create a partial signature with `secp256k1_musig_partial_sign`.
|
||||
8. Verify the partial signatures (optional in some scenarios) with `secp256k1_musig_partial_sig_verify`.
|
||||
9. Someone (not necessarily the signer) obtains all partial signatures and aggregates them into the final Schnorr signature using `secp256k1_musig_partial_sig_agg`.
|
||||
|
||||
The aggregate signature can be verified with `secp256k1_schnorrsig_verify`.
|
||||
|
||||
Steps 1 through 5 above can occur before or after the signers are aware of the message to be signed.
|
||||
Whenever possible, it is recommended to generate the nonces only after the message is known.
|
||||
This provides enhanced defense-in-depth measures, protecting against potential API misuse in certain scenarios.
|
||||
However, it does require two rounds of communication during the signing process.
|
||||
The alternative, generating the nonces in a pre-processing step before the message is known, eliminates these additional protective measures but allows for non-interactive signing.
|
||||
Similarly, the API supports an alternative protocol flow where generating the aggregate key (steps 1 to 3) is allowed to happen after exchanging nonces (steps 4 to 5).
|
||||
|
||||
## Verification
|
||||
|
||||
A participant who wants to verify the partial signatures, but does not sign itself may do so using the above instructions except that the verifier skips steps 1, 4 and 7.
|
||||
94
crypto/secp256k1/libsecp256k1/doc/release-process.md
Normal file
94
crypto/secp256k1/libsecp256k1/doc/release-process.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Release process
|
||||
|
||||
This document outlines the process for releasing versions of the form `$MAJOR.$MINOR.$PATCH`.
|
||||
|
||||
We distinguish between two types of releases: *regular* and *maintenance* releases.
|
||||
Regular releases are releases of a new major or minor version as well as patches of the most recent release.
|
||||
Maintenance releases, on the other hand, are required for patches of older releases.
|
||||
|
||||
You should coordinate with the other maintainers on the release date, if possible.
|
||||
This date will be part of the release entry in [CHANGELOG.md](../CHANGELOG.md) and it should match the dates of the remaining steps in the release process (including the date of the tag and the GitHub release).
|
||||
It is best if the maintainers are present during the release, so they can help ensure that the process is followed correctly and, in the case of a regular release, they are aware that they should not modify the master branch between merging the PR in step 1 and the PR in step 3.
|
||||
|
||||
This process also assumes that there will be no minor releases for old major releases.
|
||||
|
||||
We aim to cut a regular release every 3-4 months, approximately twice as frequent as major Bitcoin Core releases. Every second release should be published one month before the feature freeze of the next major Bitcoin Core release, allowing sufficient time to update the library in Core.
|
||||
|
||||
## Sanity checks
|
||||
Perform these checks when reviewing the release PR (see below):
|
||||
|
||||
1. Ensure `make distcheck` doesn't fail.
|
||||
```shell
|
||||
./autogen.sh && ./configure --enable-dev-mode && make distcheck
|
||||
```
|
||||
2. Check installation with autotools:
|
||||
```shell
|
||||
dir=$(mktemp -d)
|
||||
./autogen.sh && ./configure --prefix=$dir && make clean && make install && ls -RlAh $dir
|
||||
gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=$dir/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"$dir/lib" && ./ecdsa
|
||||
```
|
||||
3. Check installation with CMake:
|
||||
```shell
|
||||
dir=$(mktemp -d)
|
||||
build=$(mktemp -d)
|
||||
cmake -B $build -DCMAKE_INSTALL_PREFIX=$dir && cmake --build $build && cmake --install $build && ls -RlAh $dir
|
||||
gcc -o ecdsa examples/ecdsa.c -I $dir/include -L $dir/lib*/ -l secp256k1 -Wl,-rpath,"$dir/lib",-rpath,"$dir/lib64" && ./ecdsa
|
||||
```
|
||||
4. Use the [`check-abi.sh`](/tools/check-abi.sh) tool to verify that there are no unexpected ABI incompatibilities and that the version number and the release notes accurately reflect all potential ABI changes. To run this tool, the `abi-dumper` and `abi-compliance-checker` packages are required.
|
||||
```shell
|
||||
tools/check-abi.sh
|
||||
```
|
||||
|
||||
## Regular release
|
||||
|
||||
1. Open a PR to the master branch with a commit (using message `"release: prepare for $MAJOR.$MINOR.$PATCH"`, for example) that
|
||||
* finalizes the release notes in [CHANGELOG.md](../CHANGELOG.md) by
|
||||
* adding a section for the release (make sure that the version number is a link to a diff between the previous and new version),
|
||||
* removing the `[Unreleased]` section header,
|
||||
* ensuring that the release notes are not missing entries (check the `needs-changelog` label on github), and
|
||||
* including an entry for `### ABI Compatibility` if it doesn't exist,
|
||||
* sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and,
|
||||
* if this is not a patch release,
|
||||
* updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac`, and
|
||||
* updates `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_*` in `CMakeLists.txt`.
|
||||
2. Perform the [sanity checks](#sanity-checks) on the PR branch.
|
||||
3. After the PR is merged, tag the commit, and push the tag:
|
||||
```
|
||||
RELEASE_COMMIT=<merge commit of step 1>
|
||||
git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" $RELEASE_COMMIT
|
||||
git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH
|
||||
```
|
||||
4. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that
|
||||
* sets `_PKG_VERSION_IS_RELEASE` to `false` and increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`,
|
||||
* increments the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`, and
|
||||
* adds an `[Unreleased]` section header to the [CHANGELOG.md](../CHANGELOG.md).
|
||||
|
||||
If other maintainers are not present to approve the PR, it can be merged without ACKs.
|
||||
5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md).
|
||||
6. Send an announcement email to the bitcoin-dev mailing list.
|
||||
|
||||
## Maintenance release
|
||||
|
||||
Note that bug fixes need to be backported only to releases for which no compatible release without the bug exists.
|
||||
|
||||
1. If there's no maintenance branch `$MAJOR.$MINOR`, create one:
|
||||
```
|
||||
git checkout -b $MAJOR.$MINOR v$MAJOR.$MINOR.$((PATCH - 1))
|
||||
git push git@github.com:bitcoin-core/secp256k1.git $MAJOR.$MINOR
|
||||
```
|
||||
2. Open a pull request to the `$MAJOR.$MINOR` branch that
|
||||
* includes the bug fixes,
|
||||
* finalizes the release notes similar to a regular release,
|
||||
* increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`
|
||||
and the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`
|
||||
(with commit message `"release: bump versions for $MAJOR.$MINOR.$PATCH"`, for example).
|
||||
3. Perform the [sanity checks](#sanity-checks) on the PR branch.
|
||||
4. After the PRs are merged, update the release branch, tag the commit, and push the tag:
|
||||
```
|
||||
git checkout $MAJOR.$MINOR && git pull
|
||||
git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH"
|
||||
git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH
|
||||
```
|
||||
6. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md).
|
||||
7. Send an announcement email to the bitcoin-dev mailing list.
|
||||
8. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md).
|
||||
819
crypto/secp256k1/libsecp256k1/doc/safegcd_implementation.md
Normal file
819
crypto/secp256k1/libsecp256k1/doc/safegcd_implementation.md
Normal file
|
|
@ -0,0 +1,819 @@
|
|||
# The safegcd implementation in libsecp256k1 explained
|
||||
|
||||
This document explains the modular inverse and Jacobi symbol implementations in the `src/modinv*.h` files.
|
||||
It is based on the paper
|
||||
["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd)
|
||||
by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version.
|
||||
|
||||
The actual implementation is in C of course, but for demonstration purposes Python3 is used here.
|
||||
Most implementation aspects and optimizations are explained, except those that depend on the specific
|
||||
number representation used in the C code.
|
||||
|
||||
## 1. Computing the Greatest Common Divisor (GCD) using divsteps
|
||||
|
||||
The algorithm from the paper (section 11), at a very high level, is this:
|
||||
|
||||
```python
|
||||
def gcd(f, g):
|
||||
"""Compute the GCD of an odd integer f and another integer g."""
|
||||
assert f & 1 # require f to be odd
|
||||
delta = 1 # additional state variable
|
||||
while g != 0:
|
||||
assert f & 1 # f will be odd in every iteration
|
||||
if delta > 0 and g & 1:
|
||||
delta, f, g = 1 - delta, g, (g - f) // 2
|
||||
elif g & 1:
|
||||
delta, f, g = 1 + delta, f, (g + f) // 2
|
||||
else:
|
||||
delta, f, g = 1 + delta, f, (g ) // 2
|
||||
return abs(f)
|
||||
```
|
||||
|
||||
It computes the greatest common divisor of an odd integer *f* and any integer *g*. Its inner loop
|
||||
keeps rewriting the variables *f* and *g* alongside a state variable *δ* that starts at *1*, until
|
||||
*g=0* is reached. At that point, *|f|* gives the GCD. Each of the transitions in the loop is called a
|
||||
"division step" (referred to as divstep in what follows).
|
||||
|
||||
For example, *gcd(21, 14)* would be computed as:
|
||||
- Start with *δ=1 f=21 g=14*
|
||||
- Take the third branch: *δ=2 f=21 g=7*
|
||||
- Take the first branch: *δ=-1 f=7 g=-7*
|
||||
- Take the second branch: *δ=0 f=7 g=0*
|
||||
- The answer *|f| = 7*.
|
||||
|
||||
Why it works:
|
||||
- Divsteps can be decomposed into two steps (see paragraph 8.2 in the paper):
|
||||
- (a) If *g* is odd, replace *(f,g)* with *(g,g-f)* or (f,g+f), resulting in an even *g*.
|
||||
- (b) Replace *(f,g)* with *(f,g/2)* (where *g* is guaranteed to be even).
|
||||
- Neither of those two operations change the GCD:
|
||||
- For (a), assume *gcd(f,g)=c*, then it must be the case that *f=a c* and *g=b c* for some integers *a*
|
||||
and *b*. As *(g,g-f)=(b c,(b-a)c)* and *(f,f+g)=(a c,(a+b)c)*, the result clearly still has
|
||||
common factor *c*. Reasoning in the other direction shows that no common factor can be added by
|
||||
doing so either.
|
||||
- For (b), we know that *f* is odd, so *gcd(f,g)* clearly has no factor *2*, and we can remove
|
||||
it from *g*.
|
||||
- The algorithm will eventually converge to *g=0*. This is proven in the paper (see theorem G.3).
|
||||
- It follows that eventually we find a final value *f'* for which *gcd(f,g) = gcd(f',0)*. As the
|
||||
gcd of *f'* and *0* is *|f'|* by definition, that is our answer.
|
||||
|
||||
Compared to more [traditional GCD algorithms](https://en.wikipedia.org/wiki/Euclidean_algorithm), this one has the property of only ever looking at
|
||||
the low-order bits of the variables to decide the next steps, and being easy to make
|
||||
constant-time (in more low-level languages than Python). The *δ* parameter is necessary to
|
||||
guide the algorithm towards shrinking the numbers' magnitudes without explicitly needing to look
|
||||
at high order bits.
|
||||
|
||||
Properties that will become important later:
|
||||
- Performing more divsteps than needed is not a problem, as *f* does not change anymore after *g=0*.
|
||||
- Only even numbers are divided by *2*. This means that when reasoning about it algebraically we
|
||||
do not need to worry about rounding.
|
||||
- At every point during the algorithm's execution the next *N* steps only depend on the bottom *N*
|
||||
bits of *f* and *g*, and on *δ*.
|
||||
|
||||
|
||||
## 2. From GCDs to modular inverses
|
||||
|
||||
We want an algorithm to compute the inverse *a* of *x* modulo *M*, i.e. the number a such that *a x=1
|
||||
mod M*. This inverse only exists if the GCD of *x* and *M* is *1*, but that is always the case if *M* is
|
||||
prime and *0 < x < M*. In what follows, assume that the modular inverse exists.
|
||||
It turns out this inverse can be computed as a side effect of computing the GCD by keeping track
|
||||
of how the internal variables can be written as linear combinations of the inputs at every step
|
||||
(see the [extended Euclidean algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm)).
|
||||
Since the GCD is *1*, such an algorithm will compute numbers *a* and *b* such that a x + b M = 1*.
|
||||
Taking that expression *mod M* gives *a x mod M = 1*, and we see that *a* is the modular inverse of *x
|
||||
mod M*.
|
||||
|
||||
A similar approach can be used to calculate modular inverses using the divsteps-based GCD
|
||||
algorithm shown above, if the modulus *M* is odd. To do so, compute *gcd(f=M,g=x)*, while keeping
|
||||
track of extra variables *d* and *e*, for which at every step *d = f/x (mod M)* and *e = g/x (mod M)*.
|
||||
*f/x* here means the number which multiplied with *x* gives *f mod M*. As *f* and *g* are initialized to *M*
|
||||
and *x* respectively, *d* and *e* just start off being *0* (*M/x mod M = 0/x mod M = 0*) and *1* (*x/x mod M
|
||||
= 1*).
|
||||
|
||||
```python
|
||||
def div2(M, x):
|
||||
"""Helper routine to compute x/2 mod M (where M is odd)."""
|
||||
assert M & 1
|
||||
if x & 1: # If x is odd, make it even by adding M.
|
||||
x += M
|
||||
# x must be even now, so a clean division by 2 is possible.
|
||||
return x // 2
|
||||
|
||||
def modinv(M, x):
|
||||
"""Compute the inverse of x mod M (given that it exists, and M is odd)."""
|
||||
assert M & 1
|
||||
delta, f, g, d, e = 1, M, x, 0, 1
|
||||
while g != 0:
|
||||
# Note that while division by two for f and g is only ever done on even inputs, this is
|
||||
# not true for d and e, so we need the div2 helper function.
|
||||
if delta > 0 and g & 1:
|
||||
delta, f, g, d, e = 1 - delta, g, (g - f) // 2, e, div2(M, e - d)
|
||||
elif g & 1:
|
||||
delta, f, g, d, e = 1 + delta, f, (g + f) // 2, d, div2(M, e + d)
|
||||
else:
|
||||
delta, f, g, d, e = 1 + delta, f, (g ) // 2, d, div2(M, e )
|
||||
# Verify that the invariants d=f/x mod M, e=g/x mod M are maintained.
|
||||
assert f % M == (d * x) % M
|
||||
assert g % M == (e * x) % M
|
||||
assert f == 1 or f == -1 # |f| is the GCD, it must be 1
|
||||
# Because of invariant d = f/x (mod M), 1/x = d/f (mod M). As |f|=1, d/f = d*f.
|
||||
return (d * f) % M
|
||||
```
|
||||
|
||||
Also note that this approach to track *d* and *e* throughout the computation to determine the inverse
|
||||
is different from the paper. There (see paragraph 12.1 in the paper) a transition matrix for the
|
||||
entire computation is determined (see section 3 below) and the inverse is computed from that.
|
||||
The approach here avoids the need for 2x2 matrix multiplications of various sizes, and appears to
|
||||
be faster at the level of optimization we're able to do in C.
|
||||
|
||||
|
||||
## 3. Batching multiple divsteps
|
||||
|
||||
Every divstep can be expressed as a matrix multiplication, applying a transition matrix *(1/2 t)*
|
||||
to both vectors *[f, g]* and *[d, e]* (see paragraph 8.1 in the paper):
|
||||
|
||||
```
|
||||
t = [ u, v ]
|
||||
[ q, r ]
|
||||
|
||||
[ out_f ] = (1/2 * t) * [ in_f ]
|
||||
[ out_g ] = [ in_g ]
|
||||
|
||||
[ out_d ] = (1/2 * t) * [ in_d ] (mod M)
|
||||
[ out_e ] [ in_e ]
|
||||
```
|
||||
|
||||
where *(u, v, q, r)* is *(0, 2, -1, 1)*, *(2, 0, 1, 1)*, or *(2, 0, 0, 1)*, depending on which branch is
|
||||
taken. As above, the resulting *f* and *g* are always integers.
|
||||
|
||||
Performing multiple divsteps corresponds to a multiplication with the product of all the
|
||||
individual divsteps' transition matrices. As each transition matrix consists of integers
|
||||
divided by *2*, the product of these matrices will consist of integers divided by *2<sup>N</sup>* (see also
|
||||
theorem 9.2 in the paper). These divisions are expensive when updating *d* and *e*, so we delay
|
||||
them: we compute the integer coefficients of the combined transition matrix scaled by *2<sup>N</sup>*, and
|
||||
do one division by *2<sup>N</sup>* as a final step:
|
||||
|
||||
```python
|
||||
def divsteps_n_matrix(delta, f, g):
|
||||
"""Compute delta and transition matrix t after N divsteps (multiplied by 2^N)."""
|
||||
u, v, q, r = 1, 0, 0, 1 # start with identity matrix
|
||||
for _ in range(N):
|
||||
if delta > 0 and g & 1:
|
||||
delta, f, g, u, v, q, r = 1 - delta, g, (g - f) // 2, 2*q, 2*r, q-u, r-v
|
||||
elif g & 1:
|
||||
delta, f, g, u, v, q, r = 1 + delta, f, (g + f) // 2, 2*u, 2*v, q+u, r+v
|
||||
else:
|
||||
delta, f, g, u, v, q, r = 1 + delta, f, (g ) // 2, 2*u, 2*v, q , r
|
||||
return delta, (u, v, q, r)
|
||||
```
|
||||
|
||||
As the branches in the divsteps are completely determined by the bottom *N* bits of *f* and *g*, this
|
||||
function to compute the transition matrix only needs to see those bottom bits. Furthermore all
|
||||
intermediate results and outputs fit in *(N+1)*-bit numbers (unsigned for *f* and *g*; signed for *u*, *v*,
|
||||
*q*, and *r*) (see also paragraph 8.3 in the paper). This means that an implementation using 64-bit
|
||||
integers could set *N=62* and compute the full transition matrix for 62 steps at once without any
|
||||
big integer arithmetic at all. This is the reason why this algorithm is efficient: it only needs
|
||||
to update the full-size *f*, *g*, *d*, and *e* numbers once every *N* steps.
|
||||
|
||||
We still need functions to compute:
|
||||
|
||||
```
|
||||
[ out_f ] = (1/2^N * [ u, v ]) * [ in_f ]
|
||||
[ out_g ] ( [ q, r ]) [ in_g ]
|
||||
|
||||
[ out_d ] = (1/2^N * [ u, v ]) * [ in_d ] (mod M)
|
||||
[ out_e ] ( [ q, r ]) [ in_e ]
|
||||
```
|
||||
|
||||
Because the divsteps transformation only ever divides even numbers by two, the result of *t [f,g]* is always even. When *t* is a composition of *N* divsteps, it follows that the resulting *f*
|
||||
and *g* will be multiple of *2<sup>N</sup>*, and division by *2<sup>N</sup>* is simply shifting them down:
|
||||
|
||||
```python
|
||||
def update_fg(f, g, t):
|
||||
"""Multiply matrix t/2^N with [f, g]."""
|
||||
u, v, q, r = t
|
||||
cf, cg = u*f + v*g, q*f + r*g
|
||||
# (t / 2^N) should cleanly apply to [f,g] so the result of t*[f,g] should have N zero
|
||||
# bottom bits.
|
||||
assert cf % 2**N == 0
|
||||
assert cg % 2**N == 0
|
||||
return cf >> N, cg >> N
|
||||
```
|
||||
|
||||
The same is not true for *d* and *e*, and we need an equivalent of the `div2` function for division by *2<sup>N</sup> mod M*.
|
||||
This is easy if we have precomputed *1/M mod 2<sup>N</sup>* (which always exists for odd *M*):
|
||||
|
||||
```python
|
||||
def div2n(M, Mi, x):
|
||||
"""Compute x/2^N mod M, given Mi = 1/M mod 2^N."""
|
||||
assert (M * Mi) % 2**N == 1
|
||||
# Find a factor m such that m*M has the same bottom N bits as x. We want:
|
||||
# (m * M) mod 2^N = x mod 2^N
|
||||
# <=> m mod 2^N = (x / M) mod 2^N
|
||||
# <=> m mod 2^N = (x * Mi) mod 2^N
|
||||
m = (Mi * x) % 2**N
|
||||
# Subtract that multiple from x, cancelling its bottom N bits.
|
||||
x -= m * M
|
||||
# Now a clean division by 2^N is possible.
|
||||
assert x % 2**N == 0
|
||||
return (x >> N) % M
|
||||
|
||||
def update_de(d, e, t, M, Mi):
|
||||
"""Multiply matrix t/2^N with [d, e], modulo M."""
|
||||
u, v, q, r = t
|
||||
cd, ce = u*d + v*e, q*d + r*e
|
||||
return div2n(M, Mi, cd), div2n(M, Mi, ce)
|
||||
```
|
||||
|
||||
With all of those, we can write a version of `modinv` that performs *N* divsteps at once:
|
||||
|
||||
```python3
|
||||
def modinv(M, Mi, x):
|
||||
"""Compute the modular inverse of x mod M, given Mi=1/M mod 2^N."""
|
||||
assert M & 1
|
||||
delta, f, g, d, e = 1, M, x, 0, 1
|
||||
while g != 0:
|
||||
# Compute the delta and transition matrix t for the next N divsteps (this only needs
|
||||
# (N+1)-bit signed integer arithmetic).
|
||||
delta, t = divsteps_n_matrix(delta, f % 2**N, g % 2**N)
|
||||
# Apply the transition matrix t to [f, g]:
|
||||
f, g = update_fg(f, g, t)
|
||||
# Apply the transition matrix t to [d, e]:
|
||||
d, e = update_de(d, e, t, M, Mi)
|
||||
return (d * f) % M
|
||||
```
|
||||
|
||||
This means that in practice we'll always perform a multiple of *N* divsteps. This is not a problem
|
||||
because once *g=0*, further divsteps do not affect *f*, *g*, *d*, or *e* anymore (only *δ* keeps
|
||||
increasing). For variable time code such excess iterations will be mostly optimized away in later
|
||||
sections.
|
||||
|
||||
|
||||
## 4. Avoiding modulus operations
|
||||
|
||||
So far, there are two places where we compute a remainder of big numbers modulo *M*: at the end of
|
||||
`div2n` in every `update_de`, and at the very end of `modinv` after potentially negating *d* due to the
|
||||
sign of *f*. These are relatively expensive operations when done generically.
|
||||
|
||||
To deal with the modulus operation in `div2n`, we simply stop requiring *d* and *e* to be in range
|
||||
*[0,M)* all the time. Let's start by inlining `div2n` into `update_de`, and dropping the modulus
|
||||
operation at the end:
|
||||
|
||||
```python
|
||||
def update_de(d, e, t, M, Mi):
|
||||
"""Multiply matrix t/2^N with [d, e] mod M, given Mi=1/M mod 2^N."""
|
||||
u, v, q, r = t
|
||||
cd, ce = u*d + v*e, q*d + r*e
|
||||
# Cancel out bottom N bits of cd and ce.
|
||||
md = -((Mi * cd) % 2**N)
|
||||
me = -((Mi * ce) % 2**N)
|
||||
cd += md * M
|
||||
ce += me * M
|
||||
# And cleanly divide by 2**N.
|
||||
return cd >> N, ce >> N
|
||||
```
|
||||
|
||||
Let's look at bounds on the ranges of these numbers. It can be shown that *|u|+|v|* and *|q|+|r|*
|
||||
never exceed *2<sup>N</sup>* (see paragraph 8.3 in the paper), and thus a multiplication with *t* will have
|
||||
outputs whose absolute values are at most *2<sup>N</sup>* times the maximum absolute input value. In case the
|
||||
inputs *d* and *e* are in *(-M,M)*, which is certainly true for the initial values *d=0* and *e=1* assuming
|
||||
*M > 1*, the multiplication results in numbers in range *(-2<sup>N</sup>M,2<sup>N</sup>M)*. Subtracting less than *2<sup>N</sup>*
|
||||
times *M* to cancel out *N* bits brings that up to *(-2<sup>N+1</sup>M,2<sup>N</sup>M)*, and
|
||||
dividing by *2<sup>N</sup>* at the end takes it to *(-2M,M)*. Another application of `update_de` would take that
|
||||
to *(-3M,2M)*, and so forth. This progressive expansion of the variables' ranges can be
|
||||
counteracted by incrementing *d* and *e* by *M* whenever they're negative:
|
||||
|
||||
```python
|
||||
...
|
||||
if d < 0:
|
||||
d += M
|
||||
if e < 0:
|
||||
e += M
|
||||
cd, ce = u*d + v*e, q*d + r*e
|
||||
# Cancel out bottom N bits of cd and ce.
|
||||
...
|
||||
```
|
||||
|
||||
With inputs in *(-2M,M)*, they will first be shifted into range *(-M,M)*, which means that the
|
||||
output will again be in *(-2M,M)*, and this remains the case regardless of how many `update_de`
|
||||
invocations there are. In what follows, we will try to make this more efficient.
|
||||
|
||||
Note that increasing *d* by *M* is equal to incrementing *cd* by *u M* and *ce* by *q M*. Similarly,
|
||||
increasing *e* by *M* is equal to incrementing *cd* by *v M* and *ce* by *r M*. So we could instead write:
|
||||
|
||||
```python
|
||||
...
|
||||
cd, ce = u*d + v*e, q*d + r*e
|
||||
# Perform the equivalent of incrementing d, e by M when they're negative.
|
||||
if d < 0:
|
||||
cd += u*M
|
||||
ce += q*M
|
||||
if e < 0:
|
||||
cd += v*M
|
||||
ce += r*M
|
||||
# Cancel out bottom N bits of cd and ce.
|
||||
md = -((Mi * cd) % 2**N)
|
||||
me = -((Mi * ce) % 2**N)
|
||||
cd += md * M
|
||||
ce += me * M
|
||||
...
|
||||
```
|
||||
|
||||
Now note that we have two steps of corrections to *cd* and *ce* that add multiples of *M*: this
|
||||
increment, and the decrement that cancels out bottom bits. The second one depends on the first
|
||||
one, but they can still be efficiently combined by only computing the bottom bits of *cd* and *ce*
|
||||
at first, and using that to compute the final *md*, *me* values:
|
||||
|
||||
```python
|
||||
def update_de(d, e, t, M, Mi):
|
||||
"""Multiply matrix t/2^N with [d, e], modulo M."""
|
||||
u, v, q, r = t
|
||||
md, me = 0, 0
|
||||
# Compute what multiples of M to add to cd and ce.
|
||||
if d < 0:
|
||||
md += u
|
||||
me += q
|
||||
if e < 0:
|
||||
md += v
|
||||
me += r
|
||||
# Compute bottom N bits of t*[d,e] + M*[md,me].
|
||||
cd, ce = (u*d + v*e + md*M) % 2**N, (q*d + r*e + me*M) % 2**N
|
||||
# Correct md and me such that the bottom N bits of t*[d,e] + M*[md,me] are zero.
|
||||
md -= (Mi * cd) % 2**N
|
||||
me -= (Mi * ce) % 2**N
|
||||
# Do the full computation.
|
||||
cd, ce = u*d + v*e + md*M, q*d + r*e + me*M
|
||||
# And cleanly divide by 2**N.
|
||||
return cd >> N, ce >> N
|
||||
```
|
||||
|
||||
One last optimization: we can avoid the *md M* and *me M* multiplications in the bottom bits of *cd*
|
||||
and *ce* by moving them to the *md* and *me* correction:
|
||||
|
||||
```python
|
||||
...
|
||||
# Compute bottom N bits of t*[d,e].
|
||||
cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N
|
||||
# Correct md and me such that the bottom N bits of t*[d,e]+M*[md,me] are zero.
|
||||
# Note that this is not the same as {md = (-Mi * cd) % 2**N} etc. That would also result in N
|
||||
# zero bottom bits, but isn't guaranteed to be a reduction of [0,2^N) compared to the
|
||||
# previous md and me values, and thus would violate our bounds analysis.
|
||||
md -= (Mi*cd + md) % 2**N
|
||||
me -= (Mi*ce + me) % 2**N
|
||||
...
|
||||
```
|
||||
|
||||
The resulting function takes *d* and *e* in range *(-2M,M)* as inputs, and outputs values in the same
|
||||
range. That also means that the *d* value at the end of `modinv` will be in that range, while we want
|
||||
a result in *[0,M)*. To do that, we need a normalization function. It's easy to integrate the
|
||||
conditional negation of *d* (based on the sign of *f*) into it as well:
|
||||
|
||||
```python
|
||||
def normalize(sign, v, M):
|
||||
"""Compute sign*v mod M, where v is in range (-2*M,M); output in [0,M)."""
|
||||
assert sign == 1 or sign == -1
|
||||
# v in (-2*M,M)
|
||||
if v < 0:
|
||||
v += M
|
||||
# v in (-M,M). Now multiply v with sign (which can only be 1 or -1).
|
||||
if sign == -1:
|
||||
v = -v
|
||||
# v in (-M,M)
|
||||
if v < 0:
|
||||
v += M
|
||||
# v in [0,M)
|
||||
return v
|
||||
```
|
||||
|
||||
And calling it in `modinv` is simply:
|
||||
|
||||
```python
|
||||
...
|
||||
return normalize(f, d, M)
|
||||
```
|
||||
|
||||
|
||||
## 5. Constant-time operation
|
||||
|
||||
The primary selling point of the algorithm is fast constant-time operation. What code flow still
|
||||
depends on the input data so far?
|
||||
|
||||
- the number of iterations of the while *g ≠ 0* loop in `modinv`
|
||||
- the branches inside `divsteps_n_matrix`
|
||||
- the sign checks in `update_de`
|
||||
- the sign checks in `normalize`
|
||||
|
||||
To make the while loop in `modinv` constant time it can be replaced with a constant number of
|
||||
iterations. The paper proves (Theorem 11.2) that *741* divsteps are sufficient for any *256*-bit
|
||||
inputs, and [safegcd-bounds](https://github.com/sipa/safegcd-bounds) shows that the slightly better bound *724* is
|
||||
sufficient even. Given that every loop iteration performs *N* divsteps, it will run a total of
|
||||
*⌈724/N⌉* times.
|
||||
|
||||
To deal with the branches in `divsteps_n_matrix` we will replace them with constant-time bitwise
|
||||
operations (and hope the C compiler isn't smart enough to turn them back into branches; see
|
||||
`ctime_tests.c` for automated tests that this isn't the case). To do so, observe that a
|
||||
divstep can be written instead as (compare to the inner loop of `gcd` in section 1).
|
||||
|
||||
```python
|
||||
x = -f if delta > 0 else f # set x equal to (input) -f or f
|
||||
if g & 1:
|
||||
g += x # set g to (input) g-f or g+f
|
||||
if delta > 0:
|
||||
delta = -delta
|
||||
f += g # set f to (input) g (note that g was set to g-f before)
|
||||
delta += 1
|
||||
g >>= 1
|
||||
```
|
||||
|
||||
To convert the above to bitwise operations, we rely on a trick to negate conditionally: per the
|
||||
definition of negative numbers in two's complement, (*-v == ~v + 1*) holds for every number *v*. As
|
||||
*-1* in two's complement is all *1* bits, bitflipping can be expressed as xor with *-1*. It follows
|
||||
that *-v == (v ^ -1) - (-1)*. Thus, if we have a variable *c* that takes on values *0* or *-1*, then
|
||||
*(v ^ c) - c* is *v* if *c=0* and *-v* if *c=-1*.
|
||||
|
||||
Using this we can write:
|
||||
|
||||
```python
|
||||
x = -f if delta > 0 else f
|
||||
```
|
||||
|
||||
in constant-time form as:
|
||||
|
||||
```python
|
||||
c1 = (-delta) >> 63
|
||||
# Conditionally negate f based on c1:
|
||||
x = (f ^ c1) - c1
|
||||
```
|
||||
|
||||
To use that trick, we need a helper mask variable *c1* that resolves the condition *δ>0* to *-1*
|
||||
(if true) or *0* (if false). We compute *c1* using right shifting, which is equivalent to dividing by
|
||||
the specified power of *2* and rounding down (in Python, and also in C under the assumption of a typical two's complement system; see
|
||||
`assumptions.h` for tests that this is the case). Right shifting by *63* thus maps all
|
||||
numbers in range *[-2<sup>63</sup>,0)* to *-1*, and numbers in range *[0,2<sup>63</sup>)* to *0*.
|
||||
|
||||
Using the facts that *x&0=0* and *x&(-1)=x* (on two's complement systems again), we can write:
|
||||
|
||||
```python
|
||||
if g & 1:
|
||||
g += x
|
||||
```
|
||||
|
||||
as:
|
||||
|
||||
```python
|
||||
# Compute c2=0 if g is even and c2=-1 if g is odd.
|
||||
c2 = -(g & 1)
|
||||
# This masks out x if g is even, and leaves x be if g is odd.
|
||||
g += x & c2
|
||||
```
|
||||
|
||||
Using the conditional negation trick again we can write:
|
||||
|
||||
```python
|
||||
if g & 1:
|
||||
if delta > 0:
|
||||
delta = -delta
|
||||
```
|
||||
|
||||
as:
|
||||
|
||||
```python
|
||||
# Compute c3=-1 if g is odd and delta>0, and 0 otherwise.
|
||||
c3 = c1 & c2
|
||||
# Conditionally negate delta based on c3:
|
||||
delta = (delta ^ c3) - c3
|
||||
```
|
||||
|
||||
Finally:
|
||||
|
||||
```python
|
||||
if g & 1:
|
||||
if delta > 0:
|
||||
f += g
|
||||
```
|
||||
|
||||
becomes:
|
||||
|
||||
```python
|
||||
f += g & c3
|
||||
```
|
||||
|
||||
It turns out that this can be implemented more efficiently by applying the substitution
|
||||
*η=-δ*. In this representation, negating *δ* corresponds to negating *η*, and incrementing
|
||||
*δ* corresponds to decrementing *η*. This allows us to remove the negation in the *c1*
|
||||
computation:
|
||||
|
||||
```python
|
||||
# Compute a mask c1 for eta < 0, and compute the conditional negation x of f:
|
||||
c1 = eta >> 63
|
||||
x = (f ^ c1) - c1
|
||||
# Compute a mask c2 for odd g, and conditionally add x to g:
|
||||
c2 = -(g & 1)
|
||||
g += x & c2
|
||||
# Compute a mask c for (eta < 0) and odd (input) g, and use it to conditionally negate eta,
|
||||
# and add g to f:
|
||||
c3 = c1 & c2
|
||||
eta = (eta ^ c3) - c3
|
||||
f += g & c3
|
||||
# Incrementing delta corresponds to decrementing eta.
|
||||
eta -= 1
|
||||
g >>= 1
|
||||
```
|
||||
|
||||
A variant of divsteps with better worst-case performance can be used instead: starting *δ* at
|
||||
*1/2* instead of *1*. This reduces the worst case number of iterations to *590* for *256*-bit inputs
|
||||
(which can be shown using convex hull analysis). In this case, the substitution *ζ=-(δ+1/2)*
|
||||
is used instead to keep the variable integral. Incrementing *δ* by *1* still translates to
|
||||
decrementing *ζ* by *1*, but negating *δ* now corresponds to going from *ζ* to *-(ζ+1)*, or
|
||||
*~ζ*. Doing that conditionally based on *c3* is simply:
|
||||
|
||||
```python
|
||||
...
|
||||
c3 = c1 & c2
|
||||
zeta ^= c3
|
||||
...
|
||||
```
|
||||
|
||||
By replacing the loop in `divsteps_n_matrix` with a variant of the divstep code above (extended to
|
||||
also apply all *f* operations to *u*, *v* and all *g* operations to *q*, *r*), a constant-time version of
|
||||
`divsteps_n_matrix` is obtained. The full code will be in section 7.
|
||||
|
||||
These bit fiddling tricks can also be used to make the conditional negations and additions in
|
||||
`update_de` and `normalize` constant-time.
|
||||
|
||||
|
||||
## 6. Variable-time optimizations
|
||||
|
||||
In section 5, we modified the `divsteps_n_matrix` function (and a few others) to be constant time.
|
||||
Constant time operations are only necessary when computing modular inverses of secret data. In
|
||||
other cases, it slows down calculations unnecessarily. In this section, we will construct a
|
||||
faster non-constant time `divsteps_n_matrix` function.
|
||||
|
||||
To do so, first consider yet another way of writing the inner loop of divstep operations in
|
||||
`gcd` from section 1. This decomposition is also explained in the paper in section 8.2. We use
|
||||
the original version with initial *δ=1* and *η=-δ* here.
|
||||
|
||||
```python
|
||||
for _ in range(N):
|
||||
if g & 1 and eta < 0:
|
||||
eta, f, g = -eta, g, -f
|
||||
if g & 1:
|
||||
g += f
|
||||
eta -= 1
|
||||
g >>= 1
|
||||
```
|
||||
|
||||
Whenever *g* is even, the loop only shifts *g* down and decreases *η*. When *g* ends in multiple zero
|
||||
bits, these iterations can be consolidated into one step. This requires counting the bottom zero
|
||||
bits efficiently, which is possible on most platforms; it is abstracted here as the function
|
||||
`count_trailing_zeros`.
|
||||
|
||||
```python
|
||||
def count_trailing_zeros(v):
|
||||
"""
|
||||
When v is zero, consider all N zero bits as "trailing".
|
||||
For a non-zero value v, find z such that v=(d<<z) for some odd d.
|
||||
"""
|
||||
if v == 0:
|
||||
return N
|
||||
else:
|
||||
return (v & -v).bit_length() - 1
|
||||
|
||||
i = N # divsteps left to do
|
||||
while True:
|
||||
# Get rid of all bottom zeros at once. In the first iteration, g may be odd and the following
|
||||
# lines have no effect (until "if eta < 0").
|
||||
zeros = min(i, count_trailing_zeros(g))
|
||||
eta -= zeros
|
||||
g >>= zeros
|
||||
i -= zeros
|
||||
if i == 0:
|
||||
break
|
||||
# We know g is odd now
|
||||
if eta < 0:
|
||||
eta, f, g = -eta, g, -f
|
||||
g += f
|
||||
# g is even now, and the eta decrement and g shift will happen in the next loop.
|
||||
```
|
||||
|
||||
We can now remove multiple bottom *0* bits from *g* at once, but still need a full iteration whenever
|
||||
there is a bottom *1* bit. In what follows, we will get rid of multiple *1* bits simultaneously as
|
||||
well.
|
||||
|
||||
Observe that as long as *η ≥ 0*, the loop does not modify *f*. Instead, it cancels out bottom
|
||||
bits of *g* and shifts them out, and decreases *η* and *i* accordingly - interrupting only when *η*
|
||||
becomes negative, or when *i* reaches *0*. Combined, this is equivalent to adding a multiple of *f* to
|
||||
*g* to cancel out multiple bottom bits, and then shifting them out.
|
||||
|
||||
It is easy to find what that multiple is: we want a number *w* such that *g+w f* has a few bottom
|
||||
zero bits. If that number of bits is *L*, we want *g+w f mod 2<sup>L</sup> = 0*, or *w = -g/f mod 2<sup>L</sup>*. Since *f*
|
||||
is odd, such a *w* exists for any *L*. *L* cannot be more than *i* steps (as we'd finish the loop before
|
||||
doing more) or more than *η+1* steps (as we'd run `eta, f, g = -eta, g, -f` at that point), but
|
||||
apart from that, we're only limited by the complexity of computing *w*.
|
||||
|
||||
This code demonstrates how to cancel up to 4 bits per step:
|
||||
|
||||
```python
|
||||
NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n
|
||||
i = N
|
||||
while True:
|
||||
zeros = min(i, count_trailing_zeros(g))
|
||||
eta -= zeros
|
||||
g >>= zeros
|
||||
i -= zeros
|
||||
if i == 0:
|
||||
break
|
||||
# We know g is odd now
|
||||
if eta < 0:
|
||||
eta, f, g = -eta, g, -f
|
||||
# Compute limit on number of bits to cancel
|
||||
limit = min(min(eta + 1, i), 4)
|
||||
# Compute w = -g/f mod 2**limit, using the table value for -1/f mod 2**4. Note that f is
|
||||
# always odd, so its inverse modulo a power of two always exists.
|
||||
w = (g * NEGINV16[(f & 15) // 2]) % (2**limit)
|
||||
# As w = -g/f mod (2**limit), g+w*f mod 2**limit = 0 mod 2**limit.
|
||||
g += w * f
|
||||
assert g % (2**limit) == 0
|
||||
# The next iteration will now shift out at least limit bottom zero bits from g.
|
||||
```
|
||||
|
||||
By using a bigger table more bits can be cancelled at once. The table can also be implemented
|
||||
as a formula. Several formulas are known for computing modular inverses modulo powers of two;
|
||||
some can be found in Hacker's Delight second edition by Henry S. Warren, Jr. pages 245-247.
|
||||
Here we need the negated modular inverse, which is a simple transformation of those:
|
||||
|
||||
- Instead of a 3-bit table:
|
||||
- *-f* or *f ^ 6*
|
||||
- Instead of a 4-bit table:
|
||||
- *1 - f(f + 1)*
|
||||
- *-(f + (((f + 1) & 4) << 1))*
|
||||
- For larger tables the following technique can be used: if *w=-1/f mod 2<sup>L</sup>*, then *w(w f+2)* is
|
||||
*-1/f mod 2<sup>2L</sup>*. This allows extending the previous formulas (or tables). In particular we
|
||||
have this 6-bit function (based on the 3-bit function above):
|
||||
- *f(f<sup>2</sup> - 2)*
|
||||
|
||||
This loop, again extended to also handle *u*, *v*, *q*, and *r* alongside *f* and *g*, placed in
|
||||
`divsteps_n_matrix`, gives a significantly faster, but non-constant time version.
|
||||
|
||||
|
||||
## 7. Final Python version
|
||||
|
||||
All together we need the following functions:
|
||||
|
||||
- A way to compute the transition matrix in constant time, using the `divsteps_n_matrix` function
|
||||
from section 2, but with its loop replaced by a variant of the constant-time divstep from
|
||||
section 5, extended to handle *u*, *v*, *q*, *r*:
|
||||
|
||||
```python
|
||||
def divsteps_n_matrix(zeta, f, g):
|
||||
"""Compute zeta and transition matrix t after N divsteps (multiplied by 2^N)."""
|
||||
u, v, q, r = 1, 0, 0, 1 # start with identity matrix
|
||||
for _ in range(N):
|
||||
c1 = zeta >> 63
|
||||
# Compute x, y, z as conditionally-negated versions of f, u, v.
|
||||
x, y, z = (f ^ c1) - c1, (u ^ c1) - c1, (v ^ c1) - c1
|
||||
c2 = -(g & 1)
|
||||
# Conditionally add x, y, z to g, q, r.
|
||||
g, q, r = g + (x & c2), q + (y & c2), r + (z & c2)
|
||||
c1 &= c2 # reusing c1 here for the earlier c3 variable
|
||||
zeta = (zeta ^ c1) - 1 # inlining the unconditional zeta decrement here
|
||||
# Conditionally add g, q, r to f, u, v.
|
||||
f, u, v = f + (g & c1), u + (q & c1), v + (r & c1)
|
||||
# When shifting g down, don't shift q, r, as we construct a transition matrix multiplied
|
||||
# by 2^N. Instead, shift f's coefficients u and v up.
|
||||
g, u, v = g >> 1, u << 1, v << 1
|
||||
return zeta, (u, v, q, r)
|
||||
```
|
||||
|
||||
- The functions to update *f* and *g*, and *d* and *e*, from section 2 and section 4, with the constant-time
|
||||
changes to `update_de` from section 5:
|
||||
|
||||
```python
|
||||
def update_fg(f, g, t):
|
||||
"""Multiply matrix t/2^N with [f, g]."""
|
||||
u, v, q, r = t
|
||||
cf, cg = u*f + v*g, q*f + r*g
|
||||
return cf >> N, cg >> N
|
||||
|
||||
def update_de(d, e, t, M, Mi):
|
||||
"""Multiply matrix t/2^N with [d, e], modulo M."""
|
||||
u, v, q, r = t
|
||||
d_sign, e_sign = d >> 257, e >> 257
|
||||
md, me = (u & d_sign) + (v & e_sign), (q & d_sign) + (r & e_sign)
|
||||
cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N
|
||||
md -= (Mi*cd + md) % 2**N
|
||||
me -= (Mi*ce + me) % 2**N
|
||||
cd, ce = u*d + v*e + M*md, q*d + r*e + M*me
|
||||
return cd >> N, ce >> N
|
||||
```
|
||||
|
||||
- The `normalize` function from section 4, made constant time as well:
|
||||
|
||||
```python
|
||||
def normalize(sign, v, M):
|
||||
"""Compute sign*v mod M, where v in (-2*M,M); output in [0,M)."""
|
||||
v_sign = v >> 257
|
||||
# Conditionally add M to v.
|
||||
v += M & v_sign
|
||||
c = (sign - 1) >> 1
|
||||
# Conditionally negate v.
|
||||
v = (v ^ c) - c
|
||||
v_sign = v >> 257
|
||||
# Conditionally add M to v again.
|
||||
v += M & v_sign
|
||||
return v
|
||||
```
|
||||
|
||||
- And finally the `modinv` function too, adapted to use *ζ* instead of *δ*, and using the fixed
|
||||
iteration count from section 5:
|
||||
|
||||
```python
|
||||
def modinv(M, Mi, x):
|
||||
"""Compute the modular inverse of x mod M, given Mi=1/M mod 2^N."""
|
||||
zeta, f, g, d, e = -1, M, x, 0, 1
|
||||
for _ in range((590 + N - 1) // N):
|
||||
zeta, t = divsteps_n_matrix(zeta, f % 2**N, g % 2**N)
|
||||
f, g = update_fg(f, g, t)
|
||||
d, e = update_de(d, e, t, M, Mi)
|
||||
return normalize(f, d, M)
|
||||
```
|
||||
|
||||
- To get a variable time version, replace the `divsteps_n_matrix` function with one that uses the
|
||||
divsteps loop from section 5, and a `modinv` version that calls it without the fixed iteration
|
||||
count:
|
||||
|
||||
```python
|
||||
NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n
|
||||
def divsteps_n_matrix_var(eta, f, g):
|
||||
"""Compute eta and transition matrix t after N divsteps (multiplied by 2^N)."""
|
||||
u, v, q, r = 1, 0, 0, 1
|
||||
i = N
|
||||
while True:
|
||||
zeros = min(i, count_trailing_zeros(g))
|
||||
eta, i = eta - zeros, i - zeros
|
||||
g, u, v = g >> zeros, u << zeros, v << zeros
|
||||
if i == 0:
|
||||
break
|
||||
if eta < 0:
|
||||
eta, f, u, v, g, q, r = -eta, g, q, r, -f, -u, -v
|
||||
limit = min(min(eta + 1, i), 4)
|
||||
w = (g * NEGINV16[(f & 15) // 2]) % (2**limit)
|
||||
g, q, r = g + w*f, q + w*u, r + w*v
|
||||
return eta, (u, v, q, r)
|
||||
|
||||
def modinv_var(M, Mi, x):
|
||||
"""Compute the modular inverse of x mod M, given Mi = 1/M mod 2^N."""
|
||||
eta, f, g, d, e = -1, M, x, 0, 1
|
||||
while g != 0:
|
||||
eta, t = divsteps_n_matrix_var(eta, f % 2**N, g % 2**N)
|
||||
f, g = update_fg(f, g, t)
|
||||
d, e = update_de(d, e, t, M, Mi)
|
||||
return normalize(f, d, Mi)
|
||||
```
|
||||
|
||||
## 8. From GCDs to Jacobi symbol
|
||||
|
||||
We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an
|
||||
extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we
|
||||
make corresponding updates to *j* using
|
||||
[properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties):
|
||||
* *((g/2) | f)* is either *(g | f)* or *-(g | f)*, depending on the value of *f mod 8* (negating if it's *3* or *5*).
|
||||
* *(f | g)* is either *(g | f)* or *-(g | f)*, depending on *f mod 4* and *g mod 4* (negating if both are *3*).
|
||||
|
||||
These updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied
|
||||
very quickly, as long as we keep track of a few additional bits of *f* and *g*. Overall, this
|
||||
calculation is slightly simpler than the one for the modular inverse because we no longer need to
|
||||
keep track of *d* and *e*.
|
||||
|
||||
However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for
|
||||
positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative
|
||||
values. We resolve this by using the following modified steps:
|
||||
|
||||
```python
|
||||
# Before
|
||||
if delta > 0 and g & 1:
|
||||
delta, f, g = 1 - delta, g, (g - f) // 2
|
||||
|
||||
# After
|
||||
if delta > 0 and g & 1:
|
||||
delta, f, g = 1 - delta, g, (g + f) // 2
|
||||
```
|
||||
|
||||
The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4
|
||||
and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm
|
||||
will converge. The justification for posdivsteps is completely empirical: in practice, it appears
|
||||
that the vast majority of nonzero inputs converge to *f=g=gcd(f<sub>0</sub>, g<sub>0</sub>)* in a
|
||||
number of steps proportional to their logarithm.
|
||||
|
||||
Note that:
|
||||
- We require inputs to satisfy *gcd(x, M) = 1*, as otherwise *f=1* is not reached.
|
||||
- We require inputs *x &neq; 0*, because applying posdivstep with *g=0* has no effect.
|
||||
- We need to update the termination condition from *g=0* to *f=1*.
|
||||
|
||||
We account for the possibility of nonconvergence by only performing a bounded number of
|
||||
posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not
|
||||
yet been found.
|
||||
|
||||
The optimizations in sections 3-7 above are described in the context of the original divsteps, but
|
||||
in the C implementation we also adapt most of them (not including "avoiding modulus operations",
|
||||
since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate
|
||||
Jacobi symbols for secret data) to the posdivsteps version.
|
||||
31
crypto/secp256k1/libsecp256k1/examples/CMakeLists.txt
Normal file
31
crypto/secp256k1/libsecp256k1/examples/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
function(add_example name)
|
||||
set(target_name ${name}_example)
|
||||
add_executable(${target_name} ${name}.c)
|
||||
target_include_directories(${target_name} PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
)
|
||||
target_link_libraries(${target_name}
|
||||
secp256k1
|
||||
$<$<PLATFORM_ID:Windows>:bcrypt>
|
||||
)
|
||||
set(test_name ${name}_example)
|
||||
add_test(NAME secp256k1_${test_name} COMMAND ${target_name})
|
||||
endfunction()
|
||||
|
||||
add_example(ecdsa)
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_ECDH)
|
||||
add_example(ecdh)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
|
||||
add_example(schnorr)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_ELLSWIFT)
|
||||
add_example(ellswift)
|
||||
endif()
|
||||
|
||||
if(SECP256K1_ENABLE_MODULE_MUSIG)
|
||||
add_example(musig)
|
||||
endif()
|
||||
121
crypto/secp256k1/libsecp256k1/examples/EXAMPLES_COPYING
Normal file
121
crypto/secp256k1/libsecp256k1/examples/EXAMPLES_COPYING
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
121
crypto/secp256k1/libsecp256k1/examples/ecdh.c
Normal file
121
crypto/secp256k1/libsecp256k1/examples/ecdh.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*************************************************************************
|
||||
* Written in 2020-2022 by Elichai Turkel *
|
||||
* To the extent possible under law, the author(s) have dedicated all *
|
||||
* copyright and related and neighboring rights to the software in this *
|
||||
* file to the public domain worldwide. This software is distributed *
|
||||
* without any warranty. For the CC0 Public Domain Dedication, see *
|
||||
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
||||
*************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_ecdh.h>
|
||||
|
||||
#include "examples_util.h"
|
||||
|
||||
int main(void) {
|
||||
unsigned char seckey1[32];
|
||||
unsigned char seckey2[32];
|
||||
unsigned char compressed_pubkey1[33];
|
||||
unsigned char compressed_pubkey2[33];
|
||||
unsigned char shared_secret1[32];
|
||||
unsigned char shared_secret2[32];
|
||||
unsigned char randomize[32];
|
||||
int return_val;
|
||||
size_t len;
|
||||
secp256k1_pubkey pubkey1;
|
||||
secp256k1_pubkey pubkey2;
|
||||
|
||||
/* Before we can call actual API functions, we need to create a "context". */
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
if (!fill_random(randomize, sizeof(randomize))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Randomizing the context is recommended to protect against side-channel
|
||||
* leakage See `secp256k1_context_randomize` in secp256k1.h for more
|
||||
* information about it. This should never fail. */
|
||||
return_val = secp256k1_context_randomize(ctx, randomize);
|
||||
assert(return_val);
|
||||
|
||||
/*** Key Generation ***/
|
||||
if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* If the secret key is zero or out of range (greater than secp256k1's
|
||||
* order), we fail. Note that the probability of this occurring is negligible
|
||||
* with a properly functioning random number generator. */
|
||||
if (!secp256k1_ec_seckey_verify(ctx, seckey1) || !secp256k1_ec_seckey_verify(ctx, seckey2)) {
|
||||
printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Public key creation using a valid context with a verified secret key should never fail */
|
||||
return_val = secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1);
|
||||
assert(return_val);
|
||||
return_val = secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2);
|
||||
assert(return_val);
|
||||
|
||||
/* Serialize pubkey1 in a compressed form (33 bytes), should always return 1 */
|
||||
len = sizeof(compressed_pubkey1);
|
||||
return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey1, &len, &pubkey1, SECP256K1_EC_COMPRESSED);
|
||||
assert(return_val);
|
||||
/* Should be the same size as the size of the output, because we passed a 33 byte array. */
|
||||
assert(len == sizeof(compressed_pubkey1));
|
||||
|
||||
/* Serialize pubkey2 in a compressed form (33 bytes) */
|
||||
len = sizeof(compressed_pubkey2);
|
||||
return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey2, &len, &pubkey2, SECP256K1_EC_COMPRESSED);
|
||||
assert(return_val);
|
||||
/* Should be the same size as the size of the output, because we passed a 33 byte array. */
|
||||
assert(len == sizeof(compressed_pubkey2));
|
||||
|
||||
/*** Creating the shared secret ***/
|
||||
|
||||
/* Perform ECDH with seckey1 and pubkey2. Should never fail with a verified
|
||||
* seckey and valid pubkey */
|
||||
return_val = secp256k1_ecdh(ctx, shared_secret1, &pubkey2, seckey1, NULL, NULL);
|
||||
assert(return_val);
|
||||
|
||||
/* Perform ECDH with seckey2 and pubkey1. Should never fail with a verified
|
||||
* seckey and valid pubkey */
|
||||
return_val = secp256k1_ecdh(ctx, shared_secret2, &pubkey1, seckey2, NULL, NULL);
|
||||
assert(return_val);
|
||||
|
||||
/* Both parties should end up with the same shared secret */
|
||||
return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1));
|
||||
assert(return_val == 0);
|
||||
|
||||
printf("Secret Key1: ");
|
||||
print_hex(seckey1, sizeof(seckey1));
|
||||
printf("Compressed Pubkey1: ");
|
||||
print_hex(compressed_pubkey1, sizeof(compressed_pubkey1));
|
||||
printf("\nSecret Key2: ");
|
||||
print_hex(seckey2, sizeof(seckey2));
|
||||
printf("Compressed Pubkey2: ");
|
||||
print_hex(compressed_pubkey2, sizeof(compressed_pubkey2));
|
||||
printf("\nShared Secret: ");
|
||||
print_hex(shared_secret1, sizeof(shared_secret1));
|
||||
|
||||
/* This will clear everything from the context and free the memory */
|
||||
secp256k1_context_destroy(ctx);
|
||||
|
||||
/* It's best practice to try to clear secrets from memory after using them.
|
||||
* This is done because some bugs can allow an attacker to leak memory, for
|
||||
* example through "out of bounds" array access (see Heartbleed), or the OS
|
||||
* swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
|
||||
*
|
||||
* Here we are preventing these writes from being optimized out, as any good compiler
|
||||
* will remove any writes that aren't used. */
|
||||
secure_erase(seckey1, sizeof(seckey1));
|
||||
secure_erase(seckey2, sizeof(seckey2));
|
||||
secure_erase(shared_secret1, sizeof(shared_secret1));
|
||||
secure_erase(shared_secret2, sizeof(shared_secret2));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
138
crypto/secp256k1/libsecp256k1/examples/ecdsa.c
Normal file
138
crypto/secp256k1/libsecp256k1/examples/ecdsa.c
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*************************************************************************
|
||||
* Written in 2020-2022 by Elichai Turkel *
|
||||
* To the extent possible under law, the author(s) have dedicated all *
|
||||
* copyright and related and neighboring rights to the software in this *
|
||||
* file to the public domain worldwide. This software is distributed *
|
||||
* without any warranty. For the CC0 Public Domain Dedication, see *
|
||||
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
||||
*************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
|
||||
#include "examples_util.h"
|
||||
|
||||
int main(void) {
|
||||
/* Instead of signing the message directly, we must sign a 32-byte hash.
|
||||
* Here the message is "Hello, world!" and the hash function was SHA-256.
|
||||
* An actual implementation should just call SHA-256, but this example
|
||||
* hardcodes the output to avoid depending on an additional library.
|
||||
* See https://bitcoin.stackexchange.com/questions/81115/if-someone-wanted-to-pretend-to-be-satoshi-by-posting-a-fake-signature-to-defrau/81116#81116 */
|
||||
unsigned char msg_hash[32] = {
|
||||
0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4,
|
||||
0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64,
|
||||
0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34,
|
||||
0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3,
|
||||
};
|
||||
unsigned char seckey[32];
|
||||
unsigned char randomize[32];
|
||||
unsigned char compressed_pubkey[33];
|
||||
unsigned char serialized_signature[64];
|
||||
size_t len;
|
||||
int is_signature_valid, is_signature_valid2;
|
||||
int return_val;
|
||||
secp256k1_pubkey pubkey;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
/* Before we can call actual API functions, we need to create a "context". */
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
if (!fill_random(randomize, sizeof(randomize))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Randomizing the context is recommended to protect against side-channel
|
||||
* leakage See `secp256k1_context_randomize` in secp256k1.h for more
|
||||
* information about it. This should never fail. */
|
||||
return_val = secp256k1_context_randomize(ctx, randomize);
|
||||
assert(return_val);
|
||||
|
||||
/*** Key Generation ***/
|
||||
if (!fill_random(seckey, sizeof(seckey))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* If the secret key is zero or out of range (greater than secp256k1's
|
||||
* order), we fail. Note that the probability of this occurring is negligible
|
||||
* with a properly functioning random number generator. */
|
||||
if (!secp256k1_ec_seckey_verify(ctx, seckey)) {
|
||||
printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Public key creation using a valid context with a verified secret key should never fail */
|
||||
return_val = secp256k1_ec_pubkey_create(ctx, &pubkey, seckey);
|
||||
assert(return_val);
|
||||
|
||||
/* Serialize the pubkey in a compressed form(33 bytes). Should always return 1. */
|
||||
len = sizeof(compressed_pubkey);
|
||||
return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED);
|
||||
assert(return_val);
|
||||
/* Should be the same size as the size of the output, because we passed a 33 byte array. */
|
||||
assert(len == sizeof(compressed_pubkey));
|
||||
|
||||
/*** Signing ***/
|
||||
|
||||
/* Generate an ECDSA signature `noncefp` and `ndata` allows you to pass a
|
||||
* custom nonce function, passing `NULL` will use the RFC-6979 safe default.
|
||||
* Signing with a valid context, verified secret key
|
||||
* and the default nonce function should never fail. */
|
||||
return_val = secp256k1_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL);
|
||||
assert(return_val);
|
||||
|
||||
/* Serialize the signature in a compact form. Should always return 1
|
||||
* according to the documentation in secp256k1.h. */
|
||||
return_val = secp256k1_ecdsa_signature_serialize_compact(ctx, serialized_signature, &sig);
|
||||
assert(return_val);
|
||||
|
||||
|
||||
/*** Verification ***/
|
||||
|
||||
/* Deserialize the signature. This will return 0 if the signature can't be parsed correctly. */
|
||||
if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, serialized_signature)) {
|
||||
printf("Failed parsing the signature\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */
|
||||
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))) {
|
||||
printf("Failed parsing the public key\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
|
||||
is_signature_valid = secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey);
|
||||
|
||||
printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
|
||||
printf("Secret Key: ");
|
||||
print_hex(seckey, sizeof(seckey));
|
||||
printf("Public Key: ");
|
||||
print_hex(compressed_pubkey, sizeof(compressed_pubkey));
|
||||
printf("Signature: ");
|
||||
print_hex(serialized_signature, sizeof(serialized_signature));
|
||||
|
||||
/* This will clear everything from the context and free the memory */
|
||||
secp256k1_context_destroy(ctx);
|
||||
|
||||
/* Bonus example: if all we need is signature verification (and no key
|
||||
generation or signing), we don't need to use a context created via
|
||||
secp256k1_context_create(). We can simply use the static (i.e., global)
|
||||
context secp256k1_context_static. See its description in
|
||||
include/secp256k1.h for details. */
|
||||
is_signature_valid2 = secp256k1_ecdsa_verify(secp256k1_context_static,
|
||||
&sig, msg_hash, &pubkey);
|
||||
assert(is_signature_valid2 == is_signature_valid);
|
||||
|
||||
/* It's best practice to try to clear secrets from memory after using them.
|
||||
* This is done because some bugs can allow an attacker to leak memory, for
|
||||
* example through "out of bounds" array access (see Heartbleed), or the OS
|
||||
* swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
|
||||
*
|
||||
* Here we are preventing these writes from being optimized out, as any good compiler
|
||||
* will remove any writes that aren't used. */
|
||||
secure_erase(seckey, sizeof(seckey));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
122
crypto/secp256k1/libsecp256k1/examples/ellswift.c
Normal file
122
crypto/secp256k1/libsecp256k1/examples/ellswift.c
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*************************************************************************
|
||||
* Written in 2024 by Sebastian Falbesoner *
|
||||
* To the extent possible under law, the author(s) have dedicated all *
|
||||
* copyright and related and neighboring rights to the software in this *
|
||||
* file to the public domain worldwide. This software is distributed *
|
||||
* without any warranty. For the CC0 Public Domain Dedication, see *
|
||||
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
||||
*************************************************************************/
|
||||
|
||||
/** This file demonstrates how to use the ElligatorSwift module to perform
|
||||
* a key exchange according to BIP 324. Additionally, see the documentation
|
||||
* in include/secp256k1_ellswift.h and doc/ellswift.md.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_ellswift.h>
|
||||
|
||||
#include "examples_util.h"
|
||||
|
||||
int main(void) {
|
||||
secp256k1_context* ctx;
|
||||
unsigned char randomize[32];
|
||||
unsigned char auxrand1[32];
|
||||
unsigned char auxrand2[32];
|
||||
unsigned char seckey1[32];
|
||||
unsigned char seckey2[32];
|
||||
unsigned char ellswift_pubkey1[64];
|
||||
unsigned char ellswift_pubkey2[64];
|
||||
unsigned char shared_secret1[32];
|
||||
unsigned char shared_secret2[32];
|
||||
int return_val;
|
||||
|
||||
/* Create a secp256k1 context */
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
if (!fill_random(randomize, sizeof(randomize))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Randomizing the context is recommended to protect against side-channel
|
||||
* leakage. See `secp256k1_context_randomize` in secp256k1.h for more
|
||||
* information about it. This should never fail. */
|
||||
return_val = secp256k1_context_randomize(ctx, randomize);
|
||||
assert(return_val);
|
||||
|
||||
/*** Generate secret keys ***/
|
||||
if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* If the secret key is zero or out of range (greater than secp256k1's
|
||||
* order), we fail. Note that the probability of this occurring is negligible
|
||||
* with a properly functioning random number generator. */
|
||||
if (!secp256k1_ec_seckey_verify(ctx, seckey1) || !secp256k1_ec_seckey_verify(ctx, seckey2)) {
|
||||
printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Generate ElligatorSwift public keys. This should never fail with valid context and
|
||||
verified secret keys. Note that providing additional randomness (fourth parameter) is
|
||||
optional, but recommended. */
|
||||
if (!fill_random(auxrand1, sizeof(auxrand1)) || !fill_random(auxrand2, sizeof(auxrand2))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return_val = secp256k1_ellswift_create(ctx, ellswift_pubkey1, seckey1, auxrand1);
|
||||
assert(return_val);
|
||||
return_val = secp256k1_ellswift_create(ctx, ellswift_pubkey2, seckey2, auxrand2);
|
||||
assert(return_val);
|
||||
|
||||
/*** Create the shared secret on each side ***/
|
||||
|
||||
/* Perform x-only ECDH with seckey1 and ellswift_pubkey2. Should never fail
|
||||
* with a verified seckey and valid pubkey. Note that both parties pass both
|
||||
* EllSwift pubkeys in the same order; the pubkey of the calling party is
|
||||
* determined by the "party" boolean (sixth parameter). */
|
||||
return_val = secp256k1_ellswift_xdh(ctx, shared_secret1, ellswift_pubkey1, ellswift_pubkey2,
|
||||
seckey1, 0, secp256k1_ellswift_xdh_hash_function_bip324, NULL);
|
||||
assert(return_val);
|
||||
|
||||
/* Perform x-only ECDH with seckey2 and ellswift_pubkey1. Should never fail
|
||||
* with a verified seckey and valid pubkey. */
|
||||
return_val = secp256k1_ellswift_xdh(ctx, shared_secret2, ellswift_pubkey1, ellswift_pubkey2,
|
||||
seckey2, 1, secp256k1_ellswift_xdh_hash_function_bip324, NULL);
|
||||
assert(return_val);
|
||||
|
||||
/* Both parties should end up with the same shared secret */
|
||||
return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1));
|
||||
assert(return_val == 0);
|
||||
|
||||
printf( " Secret Key1: ");
|
||||
print_hex(seckey1, sizeof(seckey1));
|
||||
printf( "EllSwift Pubkey1: ");
|
||||
print_hex(ellswift_pubkey1, sizeof(ellswift_pubkey1));
|
||||
printf("\n Secret Key2: ");
|
||||
print_hex(seckey2, sizeof(seckey2));
|
||||
printf( "EllSwift Pubkey2: ");
|
||||
print_hex(ellswift_pubkey2, sizeof(ellswift_pubkey2));
|
||||
printf("\n Shared Secret: ");
|
||||
print_hex(shared_secret1, sizeof(shared_secret1));
|
||||
|
||||
/* This will clear everything from the context and free the memory */
|
||||
secp256k1_context_destroy(ctx);
|
||||
|
||||
/* It's best practice to try to clear secrets from memory after using them.
|
||||
* This is done because some bugs can allow an attacker to leak memory, for
|
||||
* example through "out of bounds" array access (see Heartbleed), or the OS
|
||||
* swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
|
||||
*
|
||||
* Here we are preventing these writes from being optimized out, as any good compiler
|
||||
* will remove any writes that aren't used. */
|
||||
secure_erase(seckey1, sizeof(seckey1));
|
||||
secure_erase(seckey2, sizeof(seckey2));
|
||||
secure_erase(shared_secret1, sizeof(shared_secret1));
|
||||
secure_erase(shared_secret2, sizeof(shared_secret2));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
108
crypto/secp256k1/libsecp256k1/examples/examples_util.h
Normal file
108
crypto/secp256k1/libsecp256k1/examples/examples_util.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*************************************************************************
|
||||
* Copyright (c) 2020-2021 Elichai Turkel *
|
||||
* Distributed under the CC0 software license, see the accompanying file *
|
||||
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
||||
*************************************************************************/
|
||||
|
||||
/*
|
||||
* This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems.
|
||||
* It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below.
|
||||
*
|
||||
* Platform randomness sources:
|
||||
* Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom
|
||||
* macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html
|
||||
* FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
|
||||
* OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom
|
||||
* Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
/*
|
||||
* The defined WIN32_NO_STATUS macro disables return code definitions in
|
||||
* windows.h, which avoids "macro redefinition" MSVC warnings in ntstatus.h.
|
||||
*/
|
||||
#define WIN32_NO_STATUS
|
||||
#include <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
#include <ntstatus.h>
|
||||
#include <bcrypt.h>
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <sys/random.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#error "Couldn't identify the OS"
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/* Returns 1 on success, and 0 on failure. */
|
||||
static int fill_random(unsigned char* data, size_t size) {
|
||||
#if defined(_WIN32)
|
||||
NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
if (res != STATUS_SUCCESS || size > ULONG_MAX) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||
/* If `getrandom(2)` is not available you should fallback to /dev/urandom */
|
||||
ssize_t res = getrandom(data, size, 0);
|
||||
if (res < 0 || (size_t)res != size ) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__OpenBSD__)
|
||||
/* If `getentropy(2)` is not available you should fallback to either
|
||||
* `SecRandomCopyBytes` or /dev/urandom */
|
||||
int res = getentropy(data, size);
|
||||
if (res == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_hex(unsigned char* data, size_t size) {
|
||||
size_t i;
|
||||
printf("0x");
|
||||
for (i = 0; i < size; i++) {
|
||||
printf("%02x", data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// For SecureZeroMemory
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */
|
||||
static void secure_erase(void *ptr, size_t len) {
|
||||
#if defined(_MSC_VER)
|
||||
/* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */
|
||||
SecureZeroMemory(ptr, len);
|
||||
#elif defined(__GNUC__)
|
||||
/* We use a memory barrier that scares the compiler away from optimizing out the memset.
|
||||
*
|
||||
* Quoting Adam Langley <agl@google.com> in commit ad1907fe73334d6c696c8539646c21b11178f20f
|
||||
* in BoringSSL (ISC License):
|
||||
* As best as we can tell, this is sufficient to break any optimisations that
|
||||
* might try to eliminate "superfluous" memsets.
|
||||
* This method used in memzero_explicit() the Linux kernel, too. Its advantage is that it is
|
||||
* pretty efficient, because the compiler can still implement the memset() efficiently,
|
||||
* just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by
|
||||
* Yang et al. (USENIX Security 2017) for more background.
|
||||
*/
|
||||
memset(ptr, 0, len);
|
||||
__asm__ __volatile__("" : : "r"(ptr) : "memory");
|
||||
#else
|
||||
void *(*volatile const volatile_memset)(void *, int, size_t) = memset;
|
||||
volatile_memset(ptr, 0, len);
|
||||
#endif
|
||||
}
|
||||
261
crypto/secp256k1/libsecp256k1/examples/musig.c
Normal file
261
crypto/secp256k1/libsecp256k1/examples/musig.c
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
/*************************************************************************
|
||||
* To the extent possible under law, the author(s) have dedicated all *
|
||||
* copyright and related and neighboring rights to the software in this *
|
||||
* file to the public domain worldwide. This software is distributed *
|
||||
* without any warranty. For the CC0 Public Domain Dedication, see *
|
||||
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
||||
*************************************************************************/
|
||||
|
||||
/** This file demonstrates how to use the MuSig module to create a
|
||||
* 3-of-3 multisignature. Additionally, see the documentation in
|
||||
* include/secp256k1_musig.h and doc/musig.md.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_extrakeys.h>
|
||||
#include <secp256k1_musig.h>
|
||||
#include <secp256k1_schnorrsig.h>
|
||||
|
||||
#include "examples_util.h"
|
||||
|
||||
struct signer_secrets {
|
||||
secp256k1_keypair keypair;
|
||||
secp256k1_musig_secnonce secnonce;
|
||||
};
|
||||
|
||||
struct signer {
|
||||
secp256k1_pubkey pubkey;
|
||||
secp256k1_musig_pubnonce pubnonce;
|
||||
secp256k1_musig_partial_sig partial_sig;
|
||||
};
|
||||
|
||||
/* Number of public keys involved in creating the aggregate signature */
|
||||
#define N_SIGNERS 3
|
||||
/* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */
|
||||
static int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
|
||||
unsigned char seckey[32];
|
||||
|
||||
if (!fill_random(seckey, sizeof(seckey))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return 0;
|
||||
}
|
||||
/* Try to create a keypair with a valid context. This only fails if the
|
||||
* secret key is zero or out of range (greater than secp256k1's order). Note
|
||||
* that the probability of this occurring is negligible with a properly
|
||||
* functioning random number generator. */
|
||||
if (!secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_keypair_pub(ctx, &signer->pubkey, &signer_secrets->keypair)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secure_erase(seckey, sizeof(seckey));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Tweak the pubkey corresponding to the provided keyagg cache, update the cache
|
||||
* and return the tweaked aggregate pk. */
|
||||
static int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *cache) {
|
||||
secp256k1_pubkey output_pk;
|
||||
/* For BIP 32 tweaking the plain_tweak is set to a hash as defined in BIP
|
||||
* 32. */
|
||||
unsigned char plain_tweak[32] = "this could be a BIP32 tweak....";
|
||||
/* For Taproot tweaking the xonly_tweak is set to the TapTweak hash as
|
||||
* defined in BIP 341 */
|
||||
unsigned char xonly_tweak[32] = "this could be a Taproot tweak..";
|
||||
|
||||
|
||||
/* Plain tweaking which, for example, allows deriving multiple child
|
||||
* public keys from a single aggregate key using BIP32 */
|
||||
if (!secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, cache, plain_tweak)) {
|
||||
return 0;
|
||||
}
|
||||
/* Note that we did not provide an output_pk argument, because the
|
||||
* resulting pk is also saved in the cache and so if one is just interested
|
||||
* in signing, the output_pk argument is unnecessary. On the other hand, if
|
||||
* one is not interested in signing, the same output_pk can be obtained by
|
||||
* calling `secp256k1_musig_pubkey_get` right after key aggregation to get
|
||||
* the full pubkey and then call `secp256k1_ec_pubkey_tweak_add`. */
|
||||
|
||||
/* Xonly tweaking which, for example, allows creating Taproot commitments */
|
||||
if (!secp256k1_musig_pubkey_xonly_tweak_add(ctx, &output_pk, cache, xonly_tweak)) {
|
||||
return 0;
|
||||
}
|
||||
/* Note that if we wouldn't care about signing, we can arrive at the same
|
||||
* output_pk by providing the untweaked public key to
|
||||
* `secp256k1_xonly_pubkey_tweak_add` (after converting it to an xonly pubkey
|
||||
* if necessary with `secp256k1_xonly_pubkey_from_pubkey`). */
|
||||
|
||||
/* Now we convert the output_pk to an xonly pubkey to allow to later verify
|
||||
* the Schnorr signature against it. For this purpose we can ignore the
|
||||
* `pk_parity` output argument; we would need it if we would have to open
|
||||
* the Taproot commitment. */
|
||||
if (!secp256k1_xonly_pubkey_from_pubkey(ctx, agg_pk, NULL, &output_pk)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Sign a message hash with the given key pairs and store the result in sig */
|
||||
static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const secp256k1_musig_keyagg_cache *cache, const unsigned char *msg32, unsigned char *sig64) {
|
||||
int i;
|
||||
const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS];
|
||||
const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS];
|
||||
/* The same for all signers */
|
||||
secp256k1_musig_session session;
|
||||
secp256k1_musig_aggnonce agg_pubnonce;
|
||||
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
unsigned char seckey[32];
|
||||
unsigned char session_secrand[32];
|
||||
/* Create random session ID. It is absolutely necessary that the session ID
|
||||
* is unique for every call of secp256k1_musig_nonce_gen. Otherwise
|
||||
* it's trivial for an attacker to extract the secret key! */
|
||||
if (!fill_random(session_secrand, sizeof(session_secrand))) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) {
|
||||
return 0;
|
||||
}
|
||||
/* Initialize session and create secret nonce for signing and public
|
||||
* nonce to send to the other signers. */
|
||||
if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_secrand, seckey, &signer[i].pubkey, msg32, NULL, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
pubnonces[i] = &signer[i].pubnonce;
|
||||
|
||||
secure_erase(seckey, sizeof(seckey));
|
||||
}
|
||||
|
||||
/* Communication round 1: Every signer sends their pubnonce to the
|
||||
* coordinator. The coordinator runs secp256k1_musig_nonce_agg and sends
|
||||
* agg_pubnonce to each signer */
|
||||
if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Every signer creates a partial signature */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
/* Initialize the signing session by processing the aggregate nonce */
|
||||
if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache)) {
|
||||
return 0;
|
||||
}
|
||||
/* partial_sign will clear the secnonce by setting it to 0. That's because
|
||||
* you must _never_ reuse the secnonce (or use the same session_secrand to
|
||||
* create a secnonce). If you do, you effectively reuse the nonce and
|
||||
* leak the secret key. */
|
||||
if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, cache, &session)) {
|
||||
return 0;
|
||||
}
|
||||
partial_sigs[i] = &signer[i].partial_sig;
|
||||
}
|
||||
/* Communication round 2: Every signer sends their partial signature to the
|
||||
* coordinator, who verifies the partial signatures and aggregates them. */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
/* To check whether signing was successful, it suffices to either verify
|
||||
* the aggregate signature with the aggregate public key using
|
||||
* secp256k1_schnorrsig_verify, or verify all partial signatures of all
|
||||
* signers individually. Verifying the aggregate signature is cheaper but
|
||||
* verifying the individual partial signatures has the advantage that it
|
||||
* can be used to determine which of the partial signatures are invalid
|
||||
* (if any), i.e., which of the partial signatures cause the aggregate
|
||||
* signature to be invalid and thus the protocol run to fail. It's also
|
||||
* fine to first verify the aggregate sig, and only verify the individual
|
||||
* sigs if it does not work.
|
||||
*/
|
||||
if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, cache, &session)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return secp256k1_musig_partial_sig_agg(ctx, sig64, &session, partial_sigs, N_SIGNERS);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
secp256k1_context* ctx;
|
||||
int i;
|
||||
struct signer_secrets signer_secrets[N_SIGNERS];
|
||||
struct signer signers[N_SIGNERS];
|
||||
const secp256k1_pubkey *pubkeys_ptr[N_SIGNERS];
|
||||
secp256k1_xonly_pubkey agg_pk;
|
||||
secp256k1_musig_keyagg_cache cache;
|
||||
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg";
|
||||
unsigned char sig[64];
|
||||
|
||||
/* Create a secp256k1 context */
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
printf("Creating key pairs......");
|
||||
fflush(stdout);
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) {
|
||||
printf("FAILED\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
pubkeys_ptr[i] = &signers[i].pubkey;
|
||||
}
|
||||
printf("ok\n");
|
||||
|
||||
/* The aggregate public key produced by secp256k1_musig_pubkey_agg depends
|
||||
* on the order of the provided public keys. If there is no canonical order
|
||||
* of the signers, the individual public keys can optionally be sorted with
|
||||
* secp256k1_ec_pubkey_sort to ensure that the aggregate public key is
|
||||
* independent of the order of signers. */
|
||||
printf("Sorting public keys.....");
|
||||
fflush(stdout);
|
||||
if (!secp256k1_ec_pubkey_sort(ctx, pubkeys_ptr, N_SIGNERS)) {
|
||||
printf("FAILED\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("ok\n");
|
||||
|
||||
printf("Combining public keys...");
|
||||
fflush(stdout);
|
||||
/* If you just want to aggregate and not sign, you can call
|
||||
* secp256k1_musig_pubkey_agg with the keyagg_cache argument set to NULL
|
||||
* while providing a non-NULL agg_pk argument. */
|
||||
if (!secp256k1_musig_pubkey_agg(ctx, NULL, &cache, pubkeys_ptr, N_SIGNERS)) {
|
||||
printf("FAILED\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Tweaking................");
|
||||
fflush(stdout);
|
||||
/* Optionally tweak the aggregate key */
|
||||
if (!tweak(ctx, &agg_pk, &cache)) {
|
||||
printf("FAILED\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Signing message.........");
|
||||
fflush(stdout);
|
||||
if (!sign(ctx, signer_secrets, signers, &cache, msg, sig)) {
|
||||
printf("FAILED\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Verifying signature.....");
|
||||
fflush(stdout);
|
||||
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) {
|
||||
printf("FAILED\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("ok\n");
|
||||
|
||||
/* It's best practice to try to clear secrets from memory after using them.
|
||||
* This is done because some bugs can allow an attacker to leak memory, for
|
||||
* example through "out of bounds" array access (see Heartbleed), or the OS
|
||||
* swapping them to disk. Hence, we overwrite secret key material with zeros.
|
||||
*
|
||||
* Here we are preventing these writes from being optimized out, as any good compiler
|
||||
* will remove any writes that aren't used. */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
secure_erase(&signer_secrets[i], sizeof(signer_secrets[i]));
|
||||
}
|
||||
secp256k1_context_destroy(ctx);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
154
crypto/secp256k1/libsecp256k1/examples/schnorr.c
Normal file
154
crypto/secp256k1/libsecp256k1/examples/schnorr.c
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*************************************************************************
|
||||
* Written in 2020-2022 by Elichai Turkel *
|
||||
* To the extent possible under law, the author(s) have dedicated all *
|
||||
* copyright and related and neighboring rights to the software in this *
|
||||
* file to the public domain worldwide. This software is distributed *
|
||||
* without any warranty. For the CC0 Public Domain Dedication, see *
|
||||
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
||||
*************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_extrakeys.h>
|
||||
#include <secp256k1_schnorrsig.h>
|
||||
|
||||
#include "examples_util.h"
|
||||
|
||||
int main(void) {
|
||||
unsigned char msg[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
|
||||
unsigned char msg_hash[32];
|
||||
unsigned char tag[] = {'m', 'y', '_', 'f', 'a', 'n', 'c', 'y', '_', 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l'};
|
||||
unsigned char seckey[32];
|
||||
unsigned char randomize[32];
|
||||
unsigned char auxiliary_rand[32];
|
||||
unsigned char serialized_pubkey[32];
|
||||
unsigned char signature[64];
|
||||
int is_signature_valid, is_signature_valid2;
|
||||
int return_val;
|
||||
secp256k1_xonly_pubkey pubkey;
|
||||
secp256k1_keypair keypair;
|
||||
/* Before we can call actual API functions, we need to create a "context". */
|
||||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
if (!fill_random(randomize, sizeof(randomize))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Randomizing the context is recommended to protect against side-channel
|
||||
* leakage See `secp256k1_context_randomize` in secp256k1.h for more
|
||||
* information about it. This should never fail. */
|
||||
return_val = secp256k1_context_randomize(ctx, randomize);
|
||||
assert(return_val);
|
||||
|
||||
/*** Key Generation ***/
|
||||
if (!fill_random(seckey, sizeof(seckey))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Try to create a keypair with a valid context. This only fails if the
|
||||
* secret key is zero or out of range (greater than secp256k1's order). Note
|
||||
* that the probability of this occurring is negligible with a properly
|
||||
* functioning random number generator. */
|
||||
if (!secp256k1_keypair_create(ctx, &keypair, seckey)) {
|
||||
printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Extract the X-only public key from the keypair. We pass NULL for
|
||||
* `pk_parity` as the parity isn't needed for signing or verification.
|
||||
* `secp256k1_keypair_xonly_pub` supports returning the parity for
|
||||
* other use cases such as tests or verifying Taproot tweaks.
|
||||
* This should never fail with a valid context and public key. */
|
||||
return_val = secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, &keypair);
|
||||
assert(return_val);
|
||||
|
||||
/* Serialize the public key. Should always return 1 for a valid public key. */
|
||||
return_val = secp256k1_xonly_pubkey_serialize(ctx, serialized_pubkey, &pubkey);
|
||||
assert(return_val);
|
||||
|
||||
/*** Signing ***/
|
||||
|
||||
/* Instead of signing (possibly very long) messages directly, we sign a
|
||||
* 32-byte hash of the message in this example.
|
||||
*
|
||||
* We use secp256k1_tagged_sha256 to create this hash. This function expects
|
||||
* a context-specific "tag", which restricts the context in which the signed
|
||||
* messages should be considered valid. For example, if protocol A mandates
|
||||
* to use the tag "my_fancy_protocol" and protocol B mandates to use the tag
|
||||
* "my_boring_protocol", then signed messages from protocol A will never be
|
||||
* valid in protocol B (and vice versa), even if keys are reused across
|
||||
* protocols. This implements "domain separation", which is considered good
|
||||
* practice. It avoids attacks in which users are tricked into signing a
|
||||
* message that has intended consequences in the intended context (e.g.,
|
||||
* protocol A) but would have unintended consequences if it were valid in
|
||||
* some other context (e.g., protocol B). */
|
||||
return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
|
||||
assert(return_val);
|
||||
|
||||
/* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */
|
||||
if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) {
|
||||
printf("Failed to generate randomness\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Generate a Schnorr signature.
|
||||
*
|
||||
* We use the secp256k1_schnorrsig_sign32 function that provides a simple
|
||||
* interface for signing 32-byte messages (which in our case is a hash of
|
||||
* the actual message). BIP-340 recommends passing 32 bytes of randomness
|
||||
* to the signing function to improve security against side-channel attacks.
|
||||
* Signing with a valid context, a 32-byte message, a verified keypair, and
|
||||
* any 32 bytes of auxiliary random data should never fail. */
|
||||
return_val = secp256k1_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand);
|
||||
assert(return_val);
|
||||
|
||||
/*** Verification ***/
|
||||
|
||||
/* Deserialize the public key. This will return 0 if the public key can't
|
||||
* be parsed correctly */
|
||||
if (!secp256k1_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) {
|
||||
printf("Failed parsing the public key\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Compute the tagged hash on the received messages using the same tag as the signer. */
|
||||
return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
|
||||
assert(return_val);
|
||||
|
||||
/* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
|
||||
is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey);
|
||||
|
||||
|
||||
printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
|
||||
printf("Secret Key: ");
|
||||
print_hex(seckey, sizeof(seckey));
|
||||
printf("Public Key: ");
|
||||
print_hex(serialized_pubkey, sizeof(serialized_pubkey));
|
||||
printf("Signature: ");
|
||||
print_hex(signature, sizeof(signature));
|
||||
|
||||
/* This will clear everything from the context and free the memory */
|
||||
secp256k1_context_destroy(ctx);
|
||||
|
||||
/* Bonus example: if all we need is signature verification (and no key
|
||||
generation or signing), we don't need to use a context created via
|
||||
secp256k1_context_create(). We can simply use the static (i.e., global)
|
||||
context secp256k1_context_static. See its description in
|
||||
include/secp256k1.h for details. */
|
||||
is_signature_valid2 = secp256k1_schnorrsig_verify(secp256k1_context_static,
|
||||
signature, msg_hash, 32, &pubkey);
|
||||
assert(is_signature_valid2 == is_signature_valid);
|
||||
|
||||
/* It's best practice to try to clear secrets from memory after using them.
|
||||
* This is done because some bugs can allow an attacker to leak memory, for
|
||||
* example through "out of bounds" array access (see Heartbleed), or the OS
|
||||
* swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
|
||||
*
|
||||
* Here we are preventing these writes from being optimized out, as any good compiler
|
||||
* will remove any writes that aren't used. */
|
||||
secure_erase(seckey, sizeof(seckey));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue