Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions api/solana/programs/reward-manager/EvaluateAttestations.go

This file was deleted.

185 changes: 185 additions & 0 deletions api/solana/programs/reward_manager/EvaluateAttestations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package reward_manager

import (
"encoding/hex"
"strings"

bin "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
)

type EvaluateAttestation struct {
// Instruction Data
Amount uint64
DisbursementID string
ReceipientEthAddress string

// Used for derivations
RewardManagerState solana.PublicKey `bin:"-" borsh_skip:"true"`
Payer solana.PublicKey `bin:"-" borsh_skip:"true"`
DestinationUserBank solana.PublicKey `bin:"-" borsh_skip:"true"`
TokenSource solana.PublicKey `bin:"-" borsh_skip:"true"`
AntiAbuseOracleEthAddress string `bin:"-" borsh_skip:"true"`

// Accounts
solana.AccountMetaSlice `bin:"-" borsh_skip:"true"`
}

func NewEvaluateAttestationInstructionBuilder() *EvaluateAttestation {
data := &EvaluateAttestation{}
return data
}

func (inst *EvaluateAttestation) SetDisbursementID(challengedId string, specifier string) *EvaluateAttestation {
inst.DisbursementID = challengedId + ":" + specifier
return inst
}

func (inst *EvaluateAttestation) SetRecipientEthAddress(recipientEthAddress string) *EvaluateAttestation {
inst.ReceipientEthAddress = recipientEthAddress
return inst
}

func (inst *EvaluateAttestation) SetAmount(amount uint64) *EvaluateAttestation {
inst.Amount = amount
return inst
}

func (inst *EvaluateAttestation) SetAntiAbuseOracleEthAddress(antiAbuseOracleAddress string) *EvaluateAttestation {
inst.AntiAbuseOracleEthAddress = antiAbuseOracleAddress
return inst
}

func (inst *EvaluateAttestation) SetRewardManagerState(state solana.PublicKey) *EvaluateAttestation {
inst.RewardManagerState = state
return inst
}

func (inst *EvaluateAttestation) SetTokenSource(tokenSource solana.PublicKey) *EvaluateAttestation {
inst.TokenSource = tokenSource
return inst
}

func (inst *EvaluateAttestation) SetDestinationUserBank(userBank solana.PublicKey) *EvaluateAttestation {
inst.DestinationUserBank = userBank
return inst
}

func (inst *EvaluateAttestation) SetPayer(payer solana.PublicKey) *EvaluateAttestation {
inst.Payer = payer
return inst
}

func (inst EvaluateAttestation) Build() *Instruction {
authority, _, _ := deriveAuthority(ProgramID, inst.RewardManagerState)
attestations, _, _ := deriveAttestations(ProgramID, authority, inst.DisbursementID)
disbursement, _, _ := deriveDisbursement(ProgramID, authority, inst.DisbursementID)
antiAbuseOracle, _, _ := deriveSender(ProgramID, authority, inst.AntiAbuseOracleEthAddress)

inst.AccountMetaSlice = []*solana.AccountMeta{
{
PublicKey: attestations,
IsSigner: false,
IsWritable: true,
},
{
PublicKey: inst.RewardManagerState,
IsSigner: false,
IsWritable: false,
},
{
PublicKey: authority,
IsSigner: false,
IsWritable: false,
},
{
PublicKey: inst.TokenSource,
IsSigner: false,
IsWritable: true,
},
{
PublicKey: inst.DestinationUserBank,
IsSigner: false,
IsWritable: true,
},
{
PublicKey: disbursement,
IsSigner: false,
IsWritable: true,
},
{
PublicKey: antiAbuseOracle,
IsSigner: false,
IsWritable: false,
},
{
PublicKey: inst.Payer,
IsSigner: true,
IsWritable: true,
},
{
PublicKey: solana.SysVarRentPubkey,
IsSigner: false,
IsWritable: false,
},
{
PublicKey: solana.TokenProgramID,
IsSigner: false,
IsWritable: false,
},
{
PublicKey: solana.SystemProgramID,
IsSigner: false,
IsWritable: false,
},
}

return &Instruction{BaseVariant: bin.BaseVariant{
Impl: inst,
TypeID: bin.TypeIDFromUint8(Instruction_EvaluateAttestations),
}}
}

func (inst EvaluateAttestation) MarshalWithEncoder(encoder *bin.Encoder) error {
err := encoder.Encode(inst.Amount)
if err != nil {
return err
}

err = encoder.Encode(inst.DisbursementID)
if err != nil {
return err
}

address, err := hex.DecodeString(strings.TrimPrefix(inst.ReceipientEthAddress, "0x"))
if err != nil {
return err
}
return encoder.WriteBytes(address, false)
}

func (inst *EvaluateAttestation) UnmarshalWithDecoder(decoder *bin.Decoder) error {
return decoder.Decode(&inst)
}

func NewEvaluateAttestationInstruction(
challengeId string,
specifier string,
recipientEthAddress string,
amount uint64,
antiAbuseOracleAddress string,
rewardManagerState solana.PublicKey,
tokenSource solana.PublicKey,
destinationUserBank solana.PublicKey,
payer solana.PublicKey,
) *EvaluateAttestation {
return NewEvaluateAttestationInstructionBuilder().
SetRewardManagerState(rewardManagerState).
SetDisbursementID(challengeId, specifier).
SetRecipientEthAddress(recipientEthAddress).
SetAmount(amount).
SetAntiAbuseOracleEthAddress(antiAbuseOracleAddress).
SetTokenSource(tokenSource).
SetDestinationUserBank(destinationUserBank).
SetPayer(payer)
}
63 changes: 63 additions & 0 deletions api/solana/programs/reward_manager/EvaluateAttestations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package reward_manager_test

import (
"encoding/hex"
"testing"

"bridgerton.audius.co/api/solana/programs/reward_manager"
"github.com/gagliardetto/solana-go"
"github.com/stretchr/testify/require"
)

func TestEvaluateAttestationsInstruction(t *testing.T) {
// Test data
challengeId := "ft"
specifier := "37364e80"
recipientEthAddress := "0x3f6d9fcf0d4466dd5886e3b1def017adfb7916b4"
amount := uint64(200000000)
antiAbuseOracleEthAddress := "0x00b6462e955dA5841b6D9e1E2529B830F00f31Bf"

// Expected Accounts
// From successful stage transaction (signature 26gT9HVMhzBDzsKcsiKREYmGcXuZhjAJpCVUu9WFNhVMyKje8SdApYc4ev3HrumZB4LEXLUaPnKyriBPLmtzwrWp)
rewardState := solana.MustPublicKeyFromBase58("GaiG9LDYHfZGqeNaoGRzFEnLiwUT7WiC6sA6FDJX9ZPq")
expectedAuthority := solana.MustPublicKeyFromBase58("6mpecd6bJCpH8oDwwjqPzTPU6QacnwW3cR9pAwEwkYJa")
tokenSource := solana.MustPublicKeyFromBase58("HJQj8P47BdA7ugjQEn45LaESYrxhiZDygmukt8iumFZJ")
destinationUserBank := solana.MustPublicKeyFromBase58("Cjv8dvVfWU8wUYAR82T5oZ4nHLB6EyGNvpPBzw3r76Qy")
expectedDisbursement := solana.MustPublicKeyFromBase58("3qQfuDEBWEmxRo5G4J2a4eYUVf9u1LWzLgRPndiwew2w")
expectedOracle := solana.MustPublicKeyFromBase58("FNz5mur7EFh1LyH5HDaKyWVx7vcfGK6gRizEpDqMfgGk")
payer := solana.MustPublicKeyFromBase58("E3CfijtAJwBSHfwFEViAUd3xp7c8TBxwC1eXn1Fgxp8h")

// Expected Data (from same tx)
expectedData, err := hex.DecodeString("0700c2eb0b000000000b00000066743a33373336346538303f6d9fcf0d4466dd5886e3b1def017adfb7916b4")
require.NoError(t, err)

// Use stage program ID
stageProgramId := solana.MustPublicKeyFromBase58("CDpzvz7DfgbF95jSSCHLX3ERkugyfgn9Fw8ypNZ1hfXp")
reward_manager.SetProgramID(stageProgramId)

inst := reward_manager.NewEvaluateAttestationInstruction(
challengeId,
specifier,
recipientEthAddress,
amount,
antiAbuseOracleEthAddress,
rewardState,
tokenSource,
destinationUserBank,
payer,
).Build()

require.Equal(t, stageProgramId, inst.ProgramID())
require.Len(t, inst.Accounts(), 11)
require.Equal(t, rewardState.String(), inst.Accounts()[1].PublicKey.String())
require.Equal(t, expectedAuthority.String(), inst.Accounts()[2].PublicKey.String())
require.Equal(t, tokenSource.String(), inst.Accounts()[3].PublicKey.String())
require.Equal(t, destinationUserBank.String(), inst.Accounts()[4].PublicKey.String())
require.Equal(t, expectedDisbursement.String(), inst.Accounts()[5].PublicKey.String())
require.Equal(t, expectedOracle.String(), inst.Accounts()[6].PublicKey.String())
require.Equal(t, payer.String(), inst.Accounts()[7].PublicKey.String())

data, err := inst.Data()
require.NoError(t, err)
require.Equal(t, expectedData, data)
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
package reward_manager

import (
"encoding/hex"

bin "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
)

const (
SenderSeedPrefix = "S_"
EthAddressByteLength = 20
AttestationsSeedPrefix = "V_"
)

type SubmitAttestation struct {
DisbursementID string
// Instruction Data
DisbursementID string

// Used for derivations
SenderEthAddress string `bin:"-" borsh_skip:"true"`
RewardManagerState solana.PublicKey `bin:"-" borsh_skip:"true"`
Payer solana.PublicKey `bin:"-" borsh_skip:"true"`

// Accounts
solana.AccountMetaSlice `bin:"-" borsh_skip:"true"`
}

Expand Down Expand Up @@ -47,34 +43,6 @@ func (inst *SubmitAttestation) SetPayer(payer solana.PublicKey) *SubmitAttestati
return inst
}

func deriveAuthority(programId solana.PublicKey, state solana.PublicKey) (solana.PublicKey, uint8, error) {
seeds := make([][]byte, 1)
seeds[0] = state.Bytes()[0:32]
return solana.FindProgramAddress(seeds, programId)
}

func deriveSender(programId solana.PublicKey, authority solana.PublicKey, ethAddress string) (solana.PublicKey, uint8, error) {

senderSeedPrefix := []byte(SenderSeedPrefix)
// Remove 0x and decode hex
decodedEthAddress, err := hex.DecodeString(ethAddress[2:])
if err != nil {
return solana.PublicKey{}, 0, err
}
// Pad the eth address if necessary w/ leading 0
senderSeed := make([]byte, len(senderSeedPrefix)+EthAddressByteLength)
copy(senderSeed, senderSeedPrefix)
copy(senderSeed[len(senderSeed)-len(decodedEthAddress):], decodedEthAddress)
return solana.FindProgramAddress([][]byte{authority.Bytes()[0:32], senderSeed}, programId)
}

func deriveAttestations(programId solana.PublicKey, authority solana.PublicKey, disbursementId string) (solana.PublicKey, uint8, error) {
attestationsSeed := make([]byte, len(AttestationsSeedPrefix)+len(disbursementId))
copy(attestationsSeed, []byte(AttestationsSeedPrefix))
copy(attestationsSeed[len([]byte(AttestationsSeedPrefix)):], disbursementId)
return solana.FindProgramAddress([][]byte{authority.Bytes()[0:32], attestationsSeed}, programId)
}

func (inst SubmitAttestation) Build() *Instruction {
authority, _, _ := deriveAuthority(ProgramID, inst.RewardManagerState)
sender, _, _ := deriveSender(ProgramID, authority, inst.SenderEthAddress)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ package reward_manager

import (
"bytes"
"encoding/hex"
"fmt"

bin "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
ag_text "github.com/gagliardetto/solana-go/text"
)

const (
SenderSeedPrefix = "S_"
EthAddressByteLength = 20
AttestationsSeedPrefix = "V_"
DisbursementSeedPrefix = "T_"
)

var ProgramID = solana.MustPublicKeyFromBase58("CDpzvz7DfgbF95jSSCHLX3ERkugyfgn9Fw8ypNZ1hfXp")

func SetProgramID(pubkey solana.PublicKey) {
Expand Down Expand Up @@ -76,7 +84,7 @@ var InstructionImplDef = bin.NewVariantDefinition(
Name: "SubmitAttestation", Type: (*SubmitAttestation)(nil),
},
{
Name: "EvaluateAttestation", Type: (*SubmitAttestation)(nil),
Name: "EvaluateAttestation", Type: (*EvaluateAttestation)(nil),
},
},
)
Expand Down Expand Up @@ -118,3 +126,37 @@ func DecodeInstruction(accounts []*solana.AccountMeta, data []byte) (*Instructio
}
return inst, nil
}

func deriveAuthority(programId solana.PublicKey, state solana.PublicKey) (solana.PublicKey, uint8, error) {
seeds := make([][]byte, 1)
seeds[0] = state.Bytes()[0:32]
return solana.FindProgramAddress(seeds, programId)
}

func deriveSender(programId solana.PublicKey, authority solana.PublicKey, ethAddress string) (solana.PublicKey, uint8, error) {
senderSeedPrefix := []byte(SenderSeedPrefix)
// Remove 0x and decode hex
decodedEthAddress, err := hex.DecodeString(ethAddress[2:])
if err != nil {
return solana.PublicKey{}, 0, err
}
// Pad the eth address if necessary w/ leading 0
senderSeed := make([]byte, len(senderSeedPrefix)+EthAddressByteLength)
copy(senderSeed, senderSeedPrefix)
copy(senderSeed[len(senderSeed)-len(decodedEthAddress):], decodedEthAddress)
return solana.FindProgramAddress([][]byte{authority.Bytes()[0:32], senderSeed}, programId)
}

func deriveAttestations(programId solana.PublicKey, authority solana.PublicKey, disbursementId string) (solana.PublicKey, uint8, error) {
attestationsSeed := make([]byte, len(AttestationsSeedPrefix)+len(disbursementId))
copy(attestationsSeed, []byte(AttestationsSeedPrefix))
copy(attestationsSeed[len([]byte(AttestationsSeedPrefix)):], disbursementId)
return solana.FindProgramAddress([][]byte{authority.Bytes()[0:32], attestationsSeed}, programId)
}

func deriveDisbursement(programId solana.PublicKey, authority solana.PublicKey, disbursementId string) (solana.PublicKey, uint8, error) {
disbursementSeed := make([]byte, len(DisbursementSeedPrefix)+len(disbursementId))
copy(disbursementSeed, []byte(DisbursementSeedPrefix))
copy(disbursementSeed[len([]byte(DisbursementSeedPrefix)):], disbursementId)
return solana.FindProgramAddress([][]byte{authority.Bytes()[0:32], disbursementSeed}, programId)
}
2 changes: 1 addition & 1 deletion api/solana/programs/secp256k1/Secp256k1Instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (obj *Secp256k1Instruction) UnmarshalWithDecoder(decoder *ag_binary.Decoder
if err != nil {
return err
}
signature, err := decoder.ReadBytes(int(offsets.MessageDataOffset) - int(offsets.SignatureOffset) - 1)
signature, err := decoder.ReadBytes(int(offsets.MessageDataOffset) - int(offsets.SignatureOffset))
if err != nil {
return err
}
Expand Down
Loading
Loading