diff --git a/consensus/bor/api.go b/consensus/bor/api.go index 98f7c46b6e615f3adc7625b0c7699cfdc189fc85..7ef9cdcd473117798ce9a7dfce745d746e3135e5 100644 --- a/consensus/bor/api.go +++ b/consensus/bor/api.go @@ -17,10 +17,16 @@ package bor import ( + "math/big" + "sync" + "github.com/maticnetwork/bor/common" "github.com/maticnetwork/bor/consensus" "github.com/maticnetwork/bor/core/types" + "github.com/maticnetwork/bor/crypto" "github.com/maticnetwork/bor/rpc" + "github.com/xsleonard/go-merkle" + "golang.org/x/crypto/sha3" ) // API is a user facing RPC API to allow controlling the signer and voting @@ -105,3 +111,41 @@ func (api *API) GetCurrentValidators() ([]*Validator, error) { } return snap.ValidatorSet.Validators, nil } + +// GetRootHash gets the current validators +func (api *API) GetRootHash(start uint64, end uint64) ([]byte, error) { + blockHeaders := make([]*types.Header, end-start+1) + wg := new(sync.WaitGroup) + // do we want to limit the # of concurrent go routines? + for i := start; i <= end; i++ { + wg.Add(1) + go func(number uint64) { + blockHeaders[number-start] = api.chain.GetHeaderByNumber(number) + wg.Done() + }(i) + } + wg.Wait() + + expectedLength := nextPowerOfTwo(end - start + 1) + headers := make([][32]byte, expectedLength) + for i := 0; i < len(blockHeaders); i++ { + blockHeader := blockHeaders[i] + header := crypto.Keccak256(appendBytes32( + blockHeader.Number.Bytes(), + new(big.Int).SetUint64(blockHeader.Time).Bytes(), + blockHeader.TxHash.Bytes(), + blockHeader.ReceiptHash.Bytes(), + )) + + var arr [32]byte + copy(arr[:], header) + headers[i] = arr + } + + tree := merkle.NewTreeWithOpts(merkle.TreeOptions{EnableHashSorting: false, DisableHashLeaves: true}) + if err := tree.Generate(convert(headers), sha3.NewLegacyKeccak256()); err != nil { + return nil, err + } + + return tree.Root().Hash, nil +} diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 84fabf3336685fc8a33671fea37bfe9e94f6a3a0..945395dd6be9462f3195642c5156ecc38a986361 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -441,7 +441,7 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainReader, header *types.H copy(validatorsBytes[i*validatorHeaderBytesLength:], validator.HeaderBytes()) } // len(header.Extra) >= extraVanity+extraSeal has already been validated in validateHeaderExtraField, so this won't result in a panic - if !bytes.Equal(header.Extra[extraVanity : len(header.Extra)-extraSeal], validatorsBytes) { + if !bytes.Equal(header.Extra[extraVanity:len(header.Extra)-extraSeal], validatorsBytes) { return errMismatchingSprintValidators } } diff --git a/consensus/bor/bor_test/helper.go b/consensus/bor/bor_test/helper.go index 1ead54ed88a9d02c3bdc2a5c8176700dfd6a2ab2..48735a7c5f71f7c784aab6a3283b982bf833fda5 100644 --- a/consensus/bor/bor_test/helper.go +++ b/consensus/bor/bor_test/helper.go @@ -107,13 +107,13 @@ func buildMinimalNextHeader(t *testing.T, block *types.Block, borConfig *params. header.Time += bor.CalcProducerDelay(header.Number.Uint64(), borConfig.Period, borConfig.Sprint, borConfig.ProducerDelay) isSprintEnd := (header.Number.Uint64()+1)%borConfig.Sprint == 0 if isSprintEnd { - header.Extra = make([]byte, 32 + 40 + 65) // vanity + validatorBytes + extraSeal + header.Extra = make([]byte, 32+40+65) // vanity + validatorBytes + extraSeal // the genesis file was initialized with a validator 0x71562b71999873db5b286df957af199ec94617f7 with power 10 // So, if you change ./genesis.json, do change the following as well validatorBytes, _ := hex.DecodeString("71562b71999873db5b286df957af199ec94617f7000000000000000000000000000000000000000a") copy(header.Extra[32:72], validatorBytes) } else { - header.Extra = make([]byte, 32 + 65) // vanity + extraSeal + header.Extra = make([]byte, 32+65) // vanity + extraSeal } _key, _ := hex.DecodeString(privKey) sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), _key) diff --git a/consensus/bor/merkle.go b/consensus/bor/merkle.go new file mode 100644 index 0000000000000000000000000000000000000000..bdfbaba9834eb57f23243a049b5f9e40592c31a7 --- /dev/null +++ b/consensus/bor/merkle.go @@ -0,0 +1,48 @@ +package bor + +func appendBytes32(data ...[]byte) []byte { + var result []byte + for _, v := range data { + paddedV, err := convertTo32(v) + if err == nil { + result = append(result, paddedV[:]...) + } + } + return result +} + +func nextPowerOfTwo(n uint64) uint64 { + if n == 0 { + return 1 + } + // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + n-- + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + n |= n >> 32 + n++ + return n +} + +func convertTo32(input []byte) (output [32]byte, err error) { + l := len(input) + if l > 32 || l == 0 { + return + } + copy(output[32-l:], input[:]) + return +} + +func convert(input []([32]byte)) [][]byte { + var output [][]byte + for _, in := range input { + newInput := make([]byte, len(in[:])) + copy(newInput, in[:]) + output = append(output, newInput) + + } + return output +} diff --git a/go.mod b/go.mod index ca433a71c6e86be991aa6fc41768bd611f3d735e..efce11a0bf9f85afb93992b53e0a8f53161a7f88 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,7 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 + github.com/xsleonard/go-merkle v1.1.0 golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 golang.org/x/net v0.0.0-20200301022130-244492dfa37a golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e diff --git a/go.sum b/go.sum index c51ed2d8e957a19c286f02059467ee6d93ff1938..c15b30bb97f700cbf66266ae02be6fb655867871 100644 --- a/go.sum +++ b/go.sum @@ -176,6 +176,8 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +github.com/xsleonard/go-merkle v1.1.0 h1:fHe1fuhJjGH22ZzVTAH0jqHLhTGhOq3wQjJN+8P0jQg= +github.com/xsleonard/go-merkle v1.1.0/go.mod h1:cW4z+UZ/4f2n9IJgIiyDCdYguchoDyDAPmpuOWGxdGg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA= diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index d3fea7b50cc52366df901f91d244f686f11e2841..9ee4b4f3a7a46ceaf19b8c8afc51eca8b0b2f5bb 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -149,6 +149,11 @@ web3._extend({ call: 'bor_getCurrentValidators', params: 0 }), + new web3._extend.Method({ + name: 'getRootHash', + call: 'bor_getRootHash', + params: 2, + }), ] }); `