mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
Merge pull request #548 from XinFinOrg/trie-proof
GetTransactionAndReceiptProof API
This commit is contained in:
commit
65d7e6a060
4 changed files with 193 additions and 0 deletions
|
|
@ -532,6 +532,49 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add
|
|||
return (*hexutil.Big)(state.GetBalance(address)), state.Error()
|
||||
}
|
||||
|
||||
// GetTransactionAndReceiptProof returns the Trie transaction and receipt proof of the given transaction hash.
|
||||
func (s *PublicBlockChainAPI) GetTransactionAndReceiptProof(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
||||
tx, blockHash, _, index := core.GetTransaction(s.b.ChainDb(), hash)
|
||||
if tx == nil {
|
||||
return nil, nil
|
||||
}
|
||||
block, err := s.b.GetBlock(ctx, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tx_tr := deriveTrie(block.Transactions())
|
||||
|
||||
keybuf := new(bytes.Buffer)
|
||||
rlp.Encode(keybuf, uint(index))
|
||||
var tx_proof proofPairList
|
||||
if err := tx_tr.Prove(keybuf.Bytes(), 0, &tx_proof); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receipts, err := s.b.GetReceipts(ctx, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(receipts) <= int(index) {
|
||||
return nil, nil
|
||||
}
|
||||
receipt_tr := deriveTrie(receipts)
|
||||
var receipt_proof proofPairList
|
||||
if err := receipt_tr.Prove(keybuf.Bytes(), 0, &receipt_proof); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields := map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
"txRoot": tx_tr.Hash(),
|
||||
"receiptRoot": receipt_tr.Hash(),
|
||||
"key": hexutil.Encode(keybuf.Bytes()),
|
||||
"txProofKeys": tx_proof.keys,
|
||||
"txProofValues": tx_proof.values,
|
||||
"receiptProofKeys": receipt_proof.keys,
|
||||
"receiptProofValues": receipt_proof.values,
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all
|
||||
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
|
||||
func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
|
||||
|
|
|
|||
39
internal/ethapi/trie_proof.go
Normal file
39
internal/ethapi/trie_proof.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package ethapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||
)
|
||||
|
||||
// proofPairList implements ethdb.KeyValueWriter and collects the proofs as
|
||||
// hex-strings of key and value for delivery to rpc-caller.
|
||||
type proofPairList struct {
|
||||
keys []string
|
||||
values []string
|
||||
}
|
||||
|
||||
func (n *proofPairList) Put(key []byte, value []byte) error {
|
||||
n.keys = append(n.keys, hexutil.Encode(key))
|
||||
n.values = append(n.values, hexutil.Encode(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *proofPairList) Delete(key []byte) error {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// modified from core/types/derive_sha.go
|
||||
func deriveTrie(list types.DerivableList) *trie.Trie {
|
||||
keybuf := new(bytes.Buffer)
|
||||
trie := new(trie.Trie)
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
keybuf.Reset()
|
||||
rlp.Encode(keybuf, uint(i))
|
||||
trie.Update(keybuf.Bytes(), list.GetRlp(i))
|
||||
}
|
||||
return trie
|
||||
}
|
||||
106
internal/ethapi/trie_proof_test.go
Normal file
106
internal/ethapi/trie_proof_test.go
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package ethapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||
)
|
||||
|
||||
// implement interface only for testing verifyProof
|
||||
func (n *proofPairList) Has(key []byte) (bool, error) {
|
||||
key_hex := hexutil.Encode(key)
|
||||
for _, k := range n.keys {
|
||||
if k == key_hex {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (n *proofPairList) Get(key []byte) ([]byte, error) {
|
||||
key_hex := hexutil.Encode(key)
|
||||
for i, k := range n.keys {
|
||||
if k == key_hex {
|
||||
b, err := hexutil.Decode(n.values[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("key not found")
|
||||
}
|
||||
|
||||
func TestTransactionProof(t *testing.T) {
|
||||
to1 := common.HexToAddress("0x01")
|
||||
to2 := common.HexToAddress("0x02")
|
||||
t1 := types.NewTransaction(1, to1, big.NewInt(1), 1, big.NewInt(1), []byte{})
|
||||
t2 := types.NewTransaction(2, to2, big.NewInt(2), 2, big.NewInt(2), []byte{})
|
||||
t3 := types.NewTransaction(3, to2, big.NewInt(3), 3, big.NewInt(3), []byte{})
|
||||
t4 := types.NewTransaction(4, to1, big.NewInt(4), 4, big.NewInt(4), []byte{})
|
||||
transactions := types.Transactions([]*types.Transaction{t1, t2, t3, t4})
|
||||
tr := deriveTrie(transactions)
|
||||
// for verifying the proof
|
||||
root := types.DeriveSha(transactions)
|
||||
for i := 0; i < transactions.Len(); i++ {
|
||||
var proof proofPairList
|
||||
keybuf := new(bytes.Buffer)
|
||||
rlp.Encode(keybuf, uint(i))
|
||||
if err := tr.Prove(keybuf.Bytes(), 0, &proof); err != nil {
|
||||
t.Fatal("Prove err:", err)
|
||||
}
|
||||
// verify the proof
|
||||
value, err := trie.VerifyProof(root, keybuf.Bytes(), &proof)
|
||||
if err != nil {
|
||||
t.Fatal("verify proof error")
|
||||
}
|
||||
encodedTransaction, err := rlp.EncodeToBytes(transactions[i])
|
||||
if err != nil {
|
||||
t.Fatal("encode receipt error")
|
||||
}
|
||||
if !reflect.DeepEqual(encodedTransaction, value) {
|
||||
t.Fatal("verify does not return the receipt we want")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReceiptProof(t *testing.T) {
|
||||
root1 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
root2 := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
r1 := types.NewReceipt(root1, false, 1)
|
||||
r2 := types.NewReceipt(root2, true, 2)
|
||||
r3 := types.NewReceipt(root2, false, 3)
|
||||
r4 := types.NewReceipt(root1, true, 4)
|
||||
receipts := types.Receipts([]*types.Receipt{r1, r2, r3, r4})
|
||||
tr := deriveTrie(receipts)
|
||||
// for verifying the proof
|
||||
root := types.DeriveSha(receipts)
|
||||
for i := 0; i < receipts.Len(); i++ {
|
||||
var proof proofPairList
|
||||
keybuf := new(bytes.Buffer)
|
||||
rlp.Encode(keybuf, uint(i))
|
||||
if err := tr.Prove(keybuf.Bytes(), 0, &proof); err != nil {
|
||||
t.Fatal("Prove err:", err)
|
||||
}
|
||||
// verify the proof
|
||||
value, err := trie.VerifyProof(root, keybuf.Bytes(), &proof)
|
||||
if err != nil {
|
||||
t.Fatal("verify proof error")
|
||||
}
|
||||
encodedReceipt, err := rlp.EncodeToBytes(receipts[i])
|
||||
if err != nil {
|
||||
t.Fatal("encode receipt error")
|
||||
}
|
||||
if !reflect.DeepEqual(encodedReceipt, value) {
|
||||
t.Fatal("verify does not return the receipt we want")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -502,6 +502,11 @@ web3._extend({
|
|||
call: 'eth_getRewardByHash',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getTransactionAndReceiptProof',
|
||||
call: 'eth_getTransactionAndReceiptProof',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getRawTransactionFromBlock',
|
||||
call: function(args) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue